From ddcd6a71250656d37f2718c134c80a6a70218eb1 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 4 Oct 2006 03:38:54 -0400 Subject: [PATCH] --- yaml --- r: 38890 b: refs/heads/master c: 038b0a6d8d32db934bba6a24e74e76e4e327a94f h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/CREDITS | 22 +- trunk/Documentation/DocBook/kernel-api.tmpl | 1 - trunk/Documentation/DocBook/libata.tmpl | 2 +- trunk/Documentation/RCU/checklist.txt | 38 - trunk/Documentation/RCU/rcu.txt | 3 +- trunk/Documentation/RCU/torture.txt | 33 +- trunk/Documentation/RCU/whatisRCU.txt | 3 - trunk/Documentation/ecryptfs.txt | 77 - .../feature-removal-schedule.txt | 8 + trunk/Documentation/filesystems/gfs2.txt | 43 - .../Documentation/kbuild/kconfig-language.txt | 2 +- trunk/Documentation/kbuild/makefiles.txt | 2 +- trunk/Documentation/kernel-parameters.txt | 29 + .../powerpc/booting-without-of.txt | 252 - trunk/Documentation/sound/oss/AWE32 | 76 + trunk/Documentation/sound/oss/CMI8338 | 85 + trunk/Documentation/sound/oss/INSTALL.awe | 134 + trunk/Documentation/sound/oss/MAD16 | 56 + trunk/Documentation/sound/oss/Maestro | 123 + trunk/Documentation/sound/oss/Maestro3 | 92 + trunk/Documentation/sound/oss/NEWS | 42 + trunk/Documentation/sound/oss/OPL3-SA | 52 + trunk/Documentation/sound/oss/README.awe | 218 + trunk/Documentation/sound/oss/Wavefront | 339 + trunk/Documentation/sound/oss/es1370 | 70 + trunk/Documentation/sound/oss/rme96xx | 767 ++ trunk/Documentation/sound/oss/solo1 | 70 + trunk/Documentation/sound/oss/sonicvibes | 81 + trunk/MAINTAINERS | 54 +- trunk/Makefile | 4 +- trunk/arch/alpha/kernel/setup.c | 1 - trunk/arch/alpha/kernel/systbls.S | 1 - trunk/arch/arm/kernel/crunch.c | 1 - trunk/arch/arm/kernel/iwmmxt-notifier.c | 1 - trunk/arch/arm/mach-at91rm9200/board-1arm.c | 1 - .../arch/arm/mach-at91rm9200/board-carmeva.c | 1 - trunk/arch/arm/mach-at91rm9200/board-eb9200.c | 1 - trunk/arch/arm/mach-at91rm9200/board-kafa.c | 1 - trunk/arch/arm/mach-at91rm9200/board-kb9202.c | 1 - trunk/arch/arm/mach-ep93xx/edb9302.c | 1 - trunk/arch/arm/mach-ep93xx/edb9312.c | 1 - trunk/arch/arm/mach-ep93xx/edb9315.c | 1 - trunk/arch/arm/mach-ep93xx/edb9315a.c | 1 - trunk/arch/arm/mach-lh7a40x/clcd.c | 2 +- trunk/arch/arm/mach-lh7a40x/clocks.c | 1 - trunk/arch/arm/mach-omap2/pm-domain.c | 1 - trunk/arch/arm/mach-pnx4008/gpio.c | 1 - trunk/arch/arm/mach-pnx4008/sleep.S | 1 - trunk/arch/arm/mach-pnx4008/time.c | 1 - trunk/arch/arm/mach-pxa/leds-trizeps4.c | 1 - trunk/arch/arm/tools/gen-mach-types | 1 - trunk/arch/arm26/lib/ecard.S | 1 - trunk/arch/arm26/lib/io-acorn.S | 1 - trunk/arch/frv/kernel/time.c | 1 - trunk/arch/h8300/kernel/time.c | 1 - trunk/arch/i386/kernel/acpi/boot.c | 9 +- trunk/arch/i386/kernel/i8259.c | 45 +- trunk/arch/i386/kernel/io_apic.c | 495 +- trunk/arch/i386/kernel/irq.c | 19 +- trunk/arch/i386/lib/semaphore.S | 1 - trunk/arch/i386/pci/irq.c | 34 + trunk/arch/ia64/kernel/Makefile | 1 - trunk/arch/ia64/kernel/irq_ia64.c | 20 - trunk/arch/ia64/kernel/msi_ia64.c | 143 - trunk/arch/ia64/pci/pci.c | 9 + trunk/arch/ia64/sn/kernel/Makefile | 1 - trunk/arch/m32r/mm/mmu.S | 1 - trunk/arch/m68k/kernel/time.c | 1 - trunk/arch/m68knommu/platform/532x/config.c | 1 - trunk/arch/m68knommu/platform/68328/romvec.S | 2 - trunk/arch/parisc/Kconfig | 2 +- trunk/arch/parisc/hpux/fs.c | 2 +- trunk/arch/parisc/kernel/binfmt_elf32.c | 24 +- trunk/arch/parisc/kernel/cache.c | 48 +- trunk/arch/parisc/kernel/entry.S | 21 +- trunk/arch/parisc/kernel/hardware.c | 3 - trunk/arch/parisc/kernel/head.S | 2 - trunk/arch/parisc/kernel/irq.c | 151 +- trunk/arch/parisc/kernel/processor.c | 5 +- trunk/arch/parisc/kernel/signal.c | 5 +- trunk/arch/parisc/kernel/smp.c | 7 +- trunk/arch/parisc/kernel/sys_parisc.c | 45 - trunk/arch/parisc/kernel/syscall.S | 1 - trunk/arch/parisc/kernel/syscall_table.S | 4 +- trunk/arch/parisc/kernel/time.c | 208 +- trunk/arch/parisc/kernel/traps.c | 10 - trunk/arch/parisc/mm/init.c | 23 +- trunk/arch/parisc/mm/ioremap.c | 2 +- trunk/arch/powerpc/Kconfig | 21 +- trunk/arch/powerpc/boot/Makefile | 2 +- trunk/arch/powerpc/boot/dts/mpc8272ads.dts | 223 - trunk/arch/powerpc/boot/dts/mpc8360emds.dts | 375 - trunk/arch/powerpc/boot/zImage.coff.lds.S | 1 - .../powerpc/configs/mpc8360emds_defconfig | 1018 --- trunk/arch/powerpc/kernel/cputable.c | 15 +- trunk/arch/powerpc/kernel/entry_64.S | 18 +- trunk/arch/powerpc/kernel/head_64.S | 28 +- trunk/arch/powerpc/kernel/misc_64.S | 46 - trunk/arch/powerpc/kernel/pci_64.c | 58 +- trunk/arch/powerpc/kernel/setup-common.c | 25 + trunk/arch/powerpc/kernel/setup_32.c | 8 +- trunk/arch/powerpc/kernel/setup_64.c | 12 +- trunk/arch/powerpc/kernel/vmlinux.lds.S | 8 - trunk/arch/powerpc/mm/pgtable_64.c | 29 +- trunk/arch/powerpc/mm/slb_low.S | 3 - trunk/arch/powerpc/platforms/82xx/Kconfig | 21 - trunk/arch/powerpc/platforms/82xx/Makefile | 5 - trunk/arch/powerpc/platforms/82xx/m82xx_pci.h | 19 - trunk/arch/powerpc/platforms/82xx/mpc82xx.c | 111 - .../arch/powerpc/platforms/82xx/mpc82xx_ads.c | 661 -- trunk/arch/powerpc/platforms/82xx/pq2ads.h | 67 - trunk/arch/powerpc/platforms/83xx/Kconfig | 13 - .../arch/powerpc/platforms/83xx/mpc832x_mds.c | 215 - .../arch/powerpc/platforms/83xx/mpc832x_mds.h | 19 - .../arch/powerpc/platforms/83xx/mpc834x_itx.c | 1 - .../arch/powerpc/platforms/83xx/mpc8360e_pb.c | 219 - .../arch/powerpc/platforms/85xx/mpc85xx_ads.h | 1 - .../arch/powerpc/platforms/85xx/mpc85xx_cds.c | 1 - trunk/arch/powerpc/platforms/cell/cbe_regs.c | 2 - trunk/arch/powerpc/platforms/cell/interrupt.c | 235 +- trunk/arch/powerpc/platforms/cell/interrupt.h | 97 +- trunk/arch/powerpc/platforms/cell/ras.c | 1 - .../arch/powerpc/platforms/cell/spider-pic.c | 9 +- trunk/arch/powerpc/platforms/cell/spu_base.c | 19 +- .../platforms/embedded6xx/mpc7448_hpc2.c | 1 - trunk/arch/powerpc/platforms/iseries/pci.c | 8 + trunk/arch/powerpc/platforms/iseries/setup.c | 16 +- trunk/arch/powerpc/platforms/pasemi/setup.c | 1 - trunk/arch/powerpc/platforms/pasemi/time.c | 1 - .../powerpc/platforms/powermac/udbg_scc.c | 14 +- trunk/arch/powerpc/platforms/pseries/setup.c | 2 +- trunk/arch/powerpc/sysdev/Makefile | 1 - trunk/arch/powerpc/sysdev/cpm2_pic.c | 2 +- trunk/arch/powerpc/sysdev/cpm2_pic.h | 2 +- trunk/arch/powerpc/sysdev/fsl_soc.c | 62 +- trunk/arch/powerpc/sysdev/mpic.c | 2 +- trunk/arch/powerpc/sysdev/qe_lib/Kconfig | 30 - trunk/arch/powerpc/sysdev/qe_lib/Makefile | 8 - trunk/arch/powerpc/sysdev/qe_lib/qe.c | 353 - trunk/arch/powerpc/sysdev/qe_lib/qe_ic.c | 555 -- trunk/arch/powerpc/sysdev/qe_lib/qe_ic.h | 106 - trunk/arch/powerpc/sysdev/qe_lib/qe_io.c | 226 - trunk/arch/powerpc/sysdev/qe_lib/ucc.c | 251 - trunk/arch/powerpc/sysdev/qe_lib/ucc_fast.c | 396 -- trunk/arch/powerpc/sysdev/qe_lib/ucc_slow.c | 404 -- trunk/arch/powerpc/sysdev/tsi108_dev.c | 1 - trunk/arch/powerpc/xmon/xmon.c | 35 +- trunk/arch/ppc/amiga/time.c | 1 - trunk/arch/ppc/platforms/4xx/cpci405.h | 1 - trunk/arch/s390/kernel/kprobes.c | 1 - trunk/arch/sparc/kernel/ioport.c | 1 - trunk/arch/sparc/kernel/of_device.c | 1 - trunk/arch/sparc64/kernel/auxio.c | 1 - trunk/arch/sparc64/kernel/of_device.c | 1 - trunk/arch/sparc64/kernel/prom.c | 1 - trunk/arch/um/drivers/hostaudio_kern.c | 1 - trunk/arch/um/drivers/net_kern.c | 1 - trunk/arch/um/drivers/slip_kern.c | 1 - trunk/arch/um/drivers/ssl.c | 1 - trunk/arch/um/drivers/stdio_console.c | 1 - trunk/arch/um/drivers/ubd_kern.c | 1 - trunk/arch/um/include/mconsole_kern.h | 1 - trunk/arch/um/include/mode_kern.h | 2 - trunk/arch/um/include/skas/mmu-skas.h | 1 - trunk/arch/um/include/sysdep-ppc/ptrace.h | 1 - trunk/arch/um/include/um_uaccess.h | 1 - trunk/arch/um/kernel/init_task.c | 1 - trunk/arch/um/kernel/irq.c | 1 - trunk/arch/um/kernel/ksyms.c | 1 - trunk/arch/um/kernel/signal.c | 1 - trunk/arch/um/kernel/skas/mem.c | 1 - trunk/arch/um/kernel/skas/mmu.c | 1 - trunk/arch/um/kernel/skas/tlb.c | 1 - trunk/arch/um/kernel/smp.c | 1 - trunk/arch/um/kernel/sysrq.c | 1 - trunk/arch/um/kernel/trap.c | 1 - trunk/arch/um/kernel/tt/gdb_kern.c | 1 - trunk/arch/um/kernel/tt/mem.c | 1 - trunk/arch/um/kernel/um_arch.c | 1 - trunk/arch/um/sys-i386/ldt.c | 1 - trunk/arch/um/sys-i386/sysrq.c | 1 - trunk/arch/um/sys-i386/tls.c | 1 - trunk/arch/v850/kernel/time.c | 1 - trunk/arch/x86_64/boot/video.S | 2 - trunk/arch/x86_64/ia32/ia32_binfmt.c | 1 - trunk/arch/x86_64/kernel/i8259.c | 108 +- trunk/arch/x86_64/kernel/io_apic.c | 694 +- trunk/arch/x86_64/kernel/irq.c | 14 +- trunk/arch/x86_64/kernel/mpparse.c | 42 +- trunk/arch/x86_64/kernel/pci-calgary.c | 1 - trunk/arch/x86_64/kernel/tce.c | 1 - trunk/arch/x86_64/kernel/x8664_ksyms.c | 1 - trunk/arch/x86_64/lib/copy_page.S | 3 +- trunk/arch/x86_64/lib/delay.c | 1 - trunk/arch/x86_64/lib/memcpy.S | 3 +- trunk/arch/x86_64/lib/memset.S | 1 - trunk/arch/x86_64/lib/thunk.S | 1 - trunk/drivers/ata/ahci.c | 90 +- trunk/drivers/ata/libata-core.c | 12 +- trunk/drivers/ata/libata-scsi.c | 14 +- trunk/drivers/ata/libata-sff.c | 44 +- trunk/drivers/ata/pata_ali.c | 13 +- trunk/drivers/ata/pata_amd.c | 38 +- trunk/drivers/ata/pata_artop.c | 17 +- trunk/drivers/ata/pata_atiixp.c | 14 +- trunk/drivers/ata/pata_cmd64x.c | 17 +- trunk/drivers/ata/pata_cs5520.c | 10 +- trunk/drivers/ata/pata_cs5530.c | 11 +- trunk/drivers/ata/pata_cs5535.c | 9 +- trunk/drivers/ata/pata_cypress.c | 13 +- trunk/drivers/ata/pata_efar.c | 4 +- trunk/drivers/ata/pata_hpt366.c | 9 +- trunk/drivers/ata/pata_hpt37x.c | 19 +- trunk/drivers/ata/pata_hpt3x2n.c | 17 +- trunk/drivers/ata/pata_hpt3x3.c | 9 +- trunk/drivers/ata/pata_it821x.c | 13 +- trunk/drivers/ata/pata_jmicron.c | 11 +- trunk/drivers/ata/pata_mpiix.c | 9 +- trunk/drivers/ata/pata_netcell.c | 3 +- trunk/drivers/ata/pata_ns87410.c | 9 +- trunk/drivers/ata/pata_oldpiix.c | 4 +- trunk/drivers/ata/pata_opti.c | 10 +- trunk/drivers/ata/pata_optidma.c | 9 +- trunk/drivers/ata/pata_pcmcia.c | 4 +- trunk/drivers/ata/pata_pdc2027x.c | 15 +- trunk/drivers/ata/pata_pdc202xx_old.c | 19 +- trunk/drivers/ata/pata_radisys.c | 4 +- trunk/drivers/ata/pata_rz1000.c | 12 +- trunk/drivers/ata/pata_sc1200.c | 11 +- trunk/drivers/ata/pata_serverworks.c | 17 +- trunk/drivers/ata/pata_sil680.c | 9 +- trunk/drivers/ata/pata_sis.c | 6 +- trunk/drivers/ata/pata_sl82c105.c | 9 +- trunk/drivers/ata/pata_triflex.c | 10 +- trunk/drivers/ata/pata_via.c | 15 +- trunk/drivers/ata/pdc_adma.c | 3 +- trunk/drivers/ata/sata_mv.c | 27 +- trunk/drivers/ata/sata_nv.c | 53 +- trunk/drivers/ata/sata_promise.c | 55 +- trunk/drivers/ata/sata_qstor.c | 3 +- trunk/drivers/ata/sata_sil.c | 15 +- trunk/drivers/ata/sata_sil24.c | 11 +- trunk/drivers/ata/sata_sis.c | 8 +- trunk/drivers/ata/sata_svw.c | 15 +- trunk/drivers/ata/sata_sx4.c | 5 +- trunk/drivers/ata/sata_uli.c | 8 +- trunk/drivers/ata/sata_via.c | 6 +- trunk/drivers/ata/sata_vsc.c | 6 +- trunk/drivers/atm/adummy.c | 6 +- trunk/drivers/atm/ambassador.c | 4 +- trunk/drivers/atm/firestream.c | 12 +- trunk/drivers/atm/he.c | 4 +- trunk/drivers/atm/horizon.c | 4 +- trunk/drivers/atm/idt77252.c | 23 +- trunk/drivers/atm/lanai.c | 8 +- trunk/drivers/atm/zatm.c | 7 +- trunk/drivers/block/cciss.c | 1 - trunk/drivers/block/cpqarray.c | 1 - trunk/drivers/block/pktcdvd.c | 132 +- trunk/drivers/block/swim3.c | 4 +- trunk/drivers/char/agp/Kconfig | 10 +- trunk/drivers/char/agp/Makefile | 1 - trunk/drivers/char/agp/parisc-agp.c | 416 -- trunk/drivers/char/amiserial.c | 30 +- trunk/drivers/char/briq_panel.c | 1 - trunk/drivers/char/cyclades.c | 28 +- trunk/drivers/char/epca.c | 17 +- trunk/drivers/char/epca.h | 1 + trunk/drivers/char/ftape/lowlevel/fdc-io.c | 1 - trunk/drivers/char/ftape/zftape/zftape-rw.c | 1 - trunk/drivers/char/ftape/zftape/zftape-rw.h | 1 - trunk/drivers/char/generic_serial.c | 19 +- trunk/drivers/char/hvc_iseries.c | 8 +- trunk/drivers/char/hvc_vio.c | 4 - trunk/drivers/char/hw_random/ixp4xx-rng.c | 1 - trunk/drivers/char/mspec.c | 1 - trunk/drivers/char/nsc_gpio.c | 1 - trunk/drivers/char/pcmcia/synclink_cs.c | 1 - trunk/drivers/char/riscom8.c | 10 +- trunk/drivers/char/serial167.c | 22 +- trunk/drivers/char/sx.c | 2 - trunk/drivers/char/synclink.c | 1 - trunk/drivers/char/watchdog/iTCO_wdt.c | 1 - trunk/drivers/char/watchdog/omap_wdt.c | 1 - trunk/drivers/char/watchdog/pcwd.c | 1 - trunk/drivers/char/watchdog/pcwd_pci.c | 1 - trunk/drivers/char/watchdog/pnx4008_wdt.c | 1 - trunk/drivers/clocksource/scx200_hrt.c | 4 +- trunk/drivers/cpufreq/cpufreq.c | 20 +- trunk/drivers/hwmon/w83791d.c | 1 - trunk/drivers/i2c/busses/i2c-ocores.c | 1 - trunk/drivers/ide/pci/generic.c | 1 - trunk/drivers/ide/pci/jmicron.c | 1 - trunk/drivers/ide/pci/rz1000.c | 1 - .../drivers/infiniband/hw/ipath/ipath_mmap.c | 1 - trunk/drivers/isdn/hisax/niccy.c | 223 +- trunk/drivers/leds/leds-ams-delta.c | 1 - trunk/drivers/misc/Kconfig | 32 +- trunk/drivers/misc/Makefile | 2 - trunk/drivers/misc/tifm_7xx1.c | 437 -- trunk/drivers/misc/tifm_core.c | 272 - trunk/drivers/mmc/Kconfig | 16 - trunk/drivers/mmc/Makefile | 1 - trunk/drivers/mmc/mmc.c | 6 +- trunk/drivers/mmc/mmc.h | 4 - trunk/drivers/mmc/mmc_block.c | 24 +- trunk/drivers/mmc/mmc_sysfs.c | 35 +- trunk/drivers/mmc/sdhci.c | 23 +- trunk/drivers/mmc/tifm_sd.c | 933 --- trunk/drivers/net/arm/ep93xx_eth.c | 1 - trunk/drivers/net/fs_enet/mii-fec.c | 2 - trunk/drivers/net/pcnet32.c | 2 - trunk/drivers/net/phy/fixed.c | 1 - trunk/drivers/net/ucc_geth_phy.c | 1 - trunk/drivers/parisc/iosapic.c | 5 +- trunk/drivers/parisc/lba_pci.c | 122 +- trunk/drivers/parisc/sba_iommu.c | 267 +- trunk/drivers/pci/Kconfig | 8 - trunk/drivers/pci/Makefile | 11 +- trunk/drivers/pci/htirq.c | 190 - .../msi_sn.c => drivers/pci/msi-altix.c} | 108 +- trunk/drivers/pci/msi-apic.c | 101 + trunk/drivers/pci/msi.c | 941 ++- trunk/drivers/pci/msi.h | 110 +- trunk/drivers/pci/setup-bus.c | 10 +- trunk/drivers/rtc/rtc-ds1307.c | 6 +- trunk/drivers/rtc/rtc-ds1672.c | 4 +- trunk/drivers/rtc/rtc-max6902.c | 1 - trunk/drivers/rtc/rtc-rs5c372.c | 4 +- trunk/drivers/scsi/aic94xx/aic94xx_init.c | 1 - trunk/drivers/scsi/imm.h | 1 - trunk/drivers/scsi/ppa.h | 1 - trunk/drivers/serial/8250_gsc.c | 4 +- trunk/drivers/serial/Kconfig | 9 +- trunk/drivers/serial/cpm_uart/cpm_uart.h | 1 - trunk/drivers/serial/netx-serial.c | 2 - trunk/drivers/serial/sh-sci.c | 1 - trunk/drivers/serial/sh-sci.h | 1 - trunk/drivers/spi/spi_s3c24xx.c | 1 - trunk/drivers/spi/spi_s3c24xx_gpio.c | 1 - trunk/drivers/usb/core/generic.c | 1 - trunk/drivers/usb/gadget/gmidi.c | 1 - trunk/drivers/usb/host/u132-hcd.c | 1 - trunk/drivers/usb/input/usbtouchscreen.c | 1 - trunk/drivers/usb/misc/appledisplay.c | 1 - trunk/drivers/usb/misc/ftdi-elan.c | 1 - trunk/drivers/usb/misc/sisusbvga/sisusb.c | 1 - trunk/drivers/usb/misc/sisusbvga/sisusb_con.c | 1 - trunk/drivers/video/intelfb/intelfb_i2c.c | 1 - trunk/drivers/video/riva/fbdev.c | 2 +- trunk/fs/Kconfig | 14 - trunk/fs/Makefile | 3 - trunk/fs/binfmt_som.c | 18 +- trunk/fs/configfs/item.c | 2 +- trunk/fs/dcache.c | 9 +- trunk/fs/dlm/Kconfig | 21 - trunk/fs/dlm/Makefile | 19 - trunk/fs/dlm/ast.c | 173 - trunk/fs/dlm/ast.h | 26 - trunk/fs/dlm/config.c | 789 --- trunk/fs/dlm/config.h | 42 - trunk/fs/dlm/debug_fs.c | 387 -- trunk/fs/dlm/dir.c | 423 -- trunk/fs/dlm/dir.h | 30 - trunk/fs/dlm/dlm_internal.h | 543 -- trunk/fs/dlm/lock.c | 3871 ----------- trunk/fs/dlm/lock.h | 62 - trunk/fs/dlm/lockspace.c | 717 -- trunk/fs/dlm/lockspace.h | 25 - trunk/fs/dlm/lowcomms.c | 1238 ---- trunk/fs/dlm/lowcomms.h | 26 - trunk/fs/dlm/lvb_table.h | 18 - trunk/fs/dlm/main.c | 97 - trunk/fs/dlm/member.c | 327 - trunk/fs/dlm/member.h | 24 - trunk/fs/dlm/memory.c | 116 - trunk/fs/dlm/memory.h | 29 - trunk/fs/dlm/midcomms.c | 140 - trunk/fs/dlm/midcomms.h | 21 - trunk/fs/dlm/rcom.c | 472 -- trunk/fs/dlm/rcom.h | 24 - trunk/fs/dlm/recover.c | 765 -- trunk/fs/dlm/recover.h | 34 - trunk/fs/dlm/recoverd.c | 290 - trunk/fs/dlm/recoverd.h | 24 - trunk/fs/dlm/requestqueue.c | 184 - trunk/fs/dlm/requestqueue.h | 22 - trunk/fs/dlm/user.c | 788 --- trunk/fs/dlm/user.h | 16 - trunk/fs/dlm/util.c | 161 - trunk/fs/dlm/util.h | 22 - trunk/fs/ecryptfs/Makefile | 7 - trunk/fs/ecryptfs/crypto.c | 1659 ----- trunk/fs/ecryptfs/debug.c | 123 - trunk/fs/ecryptfs/dentry.c | 87 - trunk/fs/ecryptfs/ecryptfs_kernel.h | 482 -- trunk/fs/ecryptfs/file.c | 440 -- trunk/fs/ecryptfs/inode.c | 1079 --- trunk/fs/ecryptfs/keystore.c | 1061 --- trunk/fs/ecryptfs/main.c | 831 --- trunk/fs/ecryptfs/mmap.c | 788 --- trunk/fs/ecryptfs/super.c | 198 - trunk/fs/gfs2/Kconfig | 44 - trunk/fs/gfs2/Makefile | 10 - trunk/fs/gfs2/acl.c | 309 - trunk/fs/gfs2/acl.h | 39 - trunk/fs/gfs2/bmap.c | 1221 ---- trunk/fs/gfs2/bmap.h | 31 - trunk/fs/gfs2/daemon.c | 196 - trunk/fs/gfs2/daemon.h | 19 - trunk/fs/gfs2/dir.c | 1961 ------ trunk/fs/gfs2/dir.h | 79 - trunk/fs/gfs2/eaops.c | 230 - trunk/fs/gfs2/eaops.h | 30 - trunk/fs/gfs2/eattr.c | 1501 ---- trunk/fs/gfs2/eattr.h | 100 - trunk/fs/gfs2/gfs2.h | 31 - trunk/fs/gfs2/glock.c | 2231 ------ trunk/fs/gfs2/glock.h | 153 - trunk/fs/gfs2/glops.c | 615 -- trunk/fs/gfs2/glops.h | 25 - trunk/fs/gfs2/incore.h | 634 -- trunk/fs/gfs2/inode.c | 1379 ---- trunk/fs/gfs2/inode.h | 56 - trunk/fs/gfs2/lm.c | 217 - trunk/fs/gfs2/lm.h | 42 - trunk/fs/gfs2/locking.c | 184 - trunk/fs/gfs2/locking/dlm/Makefile | 3 - trunk/fs/gfs2/locking/dlm/lock.c | 524 -- trunk/fs/gfs2/locking/dlm/lock_dlm.h | 187 - trunk/fs/gfs2/locking/dlm/main.c | 64 - trunk/fs/gfs2/locking/dlm/mount.c | 255 - trunk/fs/gfs2/locking/dlm/plock.c | 301 - trunk/fs/gfs2/locking/dlm/sysfs.c | 226 - trunk/fs/gfs2/locking/dlm/thread.c | 359 - trunk/fs/gfs2/locking/nolock/Makefile | 3 - trunk/fs/gfs2/locking/nolock/main.c | 246 - trunk/fs/gfs2/log.c | 687 -- trunk/fs/gfs2/log.h | 65 - trunk/fs/gfs2/lops.c | 809 --- trunk/fs/gfs2/lops.h | 99 - trunk/fs/gfs2/main.c | 150 - trunk/fs/gfs2/meta_io.c | 590 -- trunk/fs/gfs2/meta_io.h | 78 - trunk/fs/gfs2/mount.c | 214 - trunk/fs/gfs2/mount.h | 17 - trunk/fs/gfs2/ondisk.c | 308 - trunk/fs/gfs2/ops_address.c | 790 --- trunk/fs/gfs2/ops_address.h | 22 - trunk/fs/gfs2/ops_dentry.c | 119 - trunk/fs/gfs2/ops_dentry.h | 17 - trunk/fs/gfs2/ops_export.c | 298 - trunk/fs/gfs2/ops_export.h | 22 - trunk/fs/gfs2/ops_file.c | 661 -- trunk/fs/gfs2/ops_file.h | 24 - trunk/fs/gfs2/ops_fstype.c | 928 --- trunk/fs/gfs2/ops_fstype.h | 18 - trunk/fs/gfs2/ops_inode.c | 1151 --- trunk/fs/gfs2/ops_inode.h | 20 - trunk/fs/gfs2/ops_super.c | 468 -- trunk/fs/gfs2/ops_super.h | 17 - trunk/fs/gfs2/ops_vm.c | 184 - trunk/fs/gfs2/ops_vm.h | 18 - trunk/fs/gfs2/quota.c | 1227 ---- trunk/fs/gfs2/quota.h | 35 - trunk/fs/gfs2/recovery.c | 570 -- trunk/fs/gfs2/recovery.h | 34 - trunk/fs/gfs2/rgrp.c | 1513 ---- trunk/fs/gfs2/rgrp.h | 69 - trunk/fs/gfs2/super.c | 976 --- trunk/fs/gfs2/super.h | 55 - trunk/fs/gfs2/sys.c | 583 -- trunk/fs/gfs2/sys.h | 27 - trunk/fs/gfs2/trans.c | 184 - trunk/fs/gfs2/trans.h | 39 - trunk/fs/gfs2/util.c | 245 - trunk/fs/gfs2/util.h | 170 - trunk/fs/isofs/namei.c | 1 - trunk/fs/lockd/clntlock.c | 54 +- trunk/fs/lockd/clntproc.c | 17 +- trunk/fs/lockd/host.c | 325 +- trunk/fs/lockd/mon.c | 65 +- trunk/fs/lockd/svc.c | 19 - trunk/fs/lockd/svc4proc.c | 29 +- trunk/fs/lockd/svclock.c | 197 +- trunk/fs/lockd/svcproc.c | 27 +- trunk/fs/lockd/svcshare.c | 20 +- trunk/fs/lockd/svcsubs.c | 164 +- trunk/fs/nfs/client.c | 1 - trunk/fs/nfs/getroot.c | 1 - trunk/fs/nfs/namespace.c | 2 - trunk/fs/nfs/nfs4namespace.c | 2 - trunk/fs/nfs/nfsroot.c | 1 - trunk/fs/nfs/super.c | 1 - trunk/fs/nfsd/export.c | 149 +- trunk/fs/nfsd/nfs2acl.c | 3 +- trunk/fs/nfsd/nfs3acl.c | 3 +- trunk/fs/nfsd/nfs3proc.c | 18 +- trunk/fs/nfsd/nfs3xdr.c | 56 +- trunk/fs/nfsd/nfs4acl.c | 711 +- trunk/fs/nfsd/nfs4proc.c | 32 +- trunk/fs/nfsd/nfs4xdr.c | 231 +- trunk/fs/nfsd/nfsctl.c | 49 +- trunk/fs/nfsd/nfsproc.c | 12 +- trunk/fs/nfsd/nfssvc.c | 19 +- trunk/fs/nfsd/nfsxdr.c | 43 +- trunk/fs/nfsd/vfs.c | 86 +- trunk/fs/reiserfs/file.c | 1 - trunk/fs/reiserfs/inode.c | 2 +- trunk/include/asm-arm/arch-lh7a40x/clocks.h | 2 - trunk/include/asm-arm/pgtable-nommu.h | 1 - trunk/include/asm-i386/alternative-asm.i | 2 - trunk/include/asm-i386/frame.i | 1 - trunk/include/asm-i386/hw_irq.h | 3 + trunk/include/asm-i386/hypertransport.h | 42 - trunk/include/asm-i386/io_apic.h | 42 + .../mach-default/irq_vectors_limits.h | 5 + trunk/include/asm-i386/msi.h | 23 + trunk/include/asm-i386/msidef.h | 47 - trunk/include/asm-ia64/machvec.h | 21 +- trunk/include/asm-ia64/machvec_sn2.h | 9 +- trunk/include/asm-ia64/msi.h | 29 + trunk/include/asm-parisc/agp.h | 25 - trunk/include/asm-parisc/assembly.h | 6 +- trunk/include/asm-parisc/cacheflush.h | 30 +- trunk/include/asm-parisc/compat.h | 4 +- trunk/include/asm-parisc/dma.h | 7 +- trunk/include/asm-parisc/futex.h | 71 +- trunk/include/asm-parisc/io.h | 2 +- trunk/include/asm-parisc/iosapic.h | 53 + trunk/include/asm-parisc/irq.h | 6 +- trunk/include/asm-parisc/mckinley.h | 9 - trunk/include/asm-parisc/page.h | 22 +- trunk/include/asm-parisc/param.h | 10 +- trunk/include/asm-parisc/parisc-device.h | 5 - trunk/include/asm-parisc/pci.h | 5 - trunk/include/asm-parisc/prefetch.h | 39 - trunk/include/asm-parisc/processor.h | 39 +- trunk/include/asm-parisc/ropes.h | 322 - trunk/include/asm-parisc/serial.h | 16 +- trunk/include/asm-parisc/spinlock.h | 115 +- trunk/include/asm-powerpc/firmware.h | 67 +- trunk/include/asm-powerpc/immap_qe.h | 477 -- trunk/include/asm-powerpc/irq.h | 1 - trunk/include/asm-powerpc/pci-bridge.h | 1 - trunk/include/asm-powerpc/qe.h | 457 -- trunk/include/asm-powerpc/qe_ic.h | 64 - trunk/include/asm-powerpc/system.h | 4 + trunk/include/asm-powerpc/ucc.h | 84 - trunk/include/asm-powerpc/ucc_fast.h | 243 - trunk/include/asm-powerpc/ucc_slow.h | 289 - trunk/include/asm-powerpc/xmon.h | 26 +- trunk/include/asm-sparc64/compat_signal.h | 1 - trunk/include/asm-x86_64/alternative-asm.i | 2 - trunk/include/asm-x86_64/hardirq.h | 3 - trunk/include/asm-x86_64/hw_irq.h | 8 +- trunk/include/asm-x86_64/hypertransport.h | 42 - trunk/include/asm-x86_64/io_apic.h | 43 + trunk/include/asm-x86_64/irq.h | 7 +- trunk/include/asm-x86_64/msi.h | 24 + trunk/include/asm-x86_64/msidef.h | 47 - trunk/include/linux/Kbuild | 7 +- trunk/include/linux/ac97_codec.h | 5 + trunk/include/linux/audit.h | 3 +- trunk/include/linux/config.h | 1 + trunk/include/linux/debug_locks.h | 2 - trunk/include/linux/dlm.h | 302 - trunk/include/linux/dlm_device.h | 86 - trunk/include/linux/fs.h | 2 - trunk/include/linux/fsl_devices.h | 65 +- trunk/include/linux/gfs2_ondisk.h | 443 -- trunk/include/linux/hardirq.h | 7 +- trunk/include/linux/htirq.h | 15 - trunk/include/linux/in.h | 1 - trunk/include/linux/ip.h | 9 - trunk/include/linux/ipc.h | 3 +- trunk/include/linux/ipsec.h | 3 +- trunk/include/linux/irq.h | 57 +- trunk/include/linux/libata.h | 9 +- trunk/include/linux/lm_interface.h | 273 - trunk/include/linux/lock_dlm_plock.h | 41 - trunk/include/linux/lockd/lockd.h | 61 +- trunk/include/linux/lockd/share.h | 3 +- trunk/include/linux/lockd/sm_inter.h | 5 +- trunk/include/linux/msi.h | 49 - .../linux/netfilter_bridge/ebt_mark_t.h | 12 - trunk/include/linux/netfilter_ipv4.h | 2 +- trunk/include/linux/nfsd/const.h | 20 +- trunk/include/linux/nfsd/export.h | 21 - trunk/include/linux/nfsd/nfsd.h | 5 +- trunk/include/linux/nfsd/xdr.h | 2 + trunk/include/linux/nfsd/xdr3.h | 2 + trunk/include/linux/nfsd/xdr4.h | 2 + trunk/include/linux/notifier.h | 43 +- trunk/include/linux/pci.h | 7 - trunk/include/linux/pci_regs.h | 21 - trunk/include/linux/rcupdate.h | 7 +- trunk/include/linux/scx200.h | 2 +- trunk/include/linux/slab.h | 26 +- trunk/include/linux/sound.h | 2 + trunk/include/linux/srcu.h | 53 - trunk/include/linux/sunrpc/auth.h | 3 + trunk/include/linux/sunrpc/cache.h | 11 - trunk/include/linux/sunrpc/msg_prot.h | 40 - trunk/include/linux/sunrpc/svc.h | 97 +- trunk/include/linux/sunrpc/svcauth.h | 1 - trunk/include/linux/sunrpc/svcsock.h | 3 - trunk/include/linux/sunrpc/xprt.h | 8 +- trunk/include/linux/tifm.h | 158 - trunk/include/linux/utsname.h | 17 +- trunk/include/linux/wavefront.h | 675 ++ trunk/include/linux/xfrm.h | 3 +- trunk/include/net/netdma.h | 1 - trunk/init/do_mounts.h | 1 - trunk/kernel/Makefile | 2 +- trunk/kernel/auditfilter.c | 9 +- trunk/kernel/auditsc.c | 28 +- trunk/kernel/irq/chip.c | 63 - trunk/kernel/irq/migration.c | 34 +- trunk/kernel/rcupdate.c | 11 +- trunk/kernel/rcutorture.c | 317 +- trunk/kernel/rtmutex-debug.c | 1 - trunk/kernel/rtmutex-tester.c | 1 - trunk/kernel/srcu.c | 258 - trunk/kernel/sys.c | 125 +- trunk/mm/filemap.c | 10 +- trunk/mm/filemap.h | 1 - trunk/mm/hugetlb.c | 8 +- trunk/mm/page_alloc.c | 53 +- trunk/mm/readahead.c | 1 - trunk/mm/slab.c | 14 +- trunk/mm/util.c | 6 +- trunk/mm/vmstat.c | 1 - trunk/net/atm/lec.h | 1 - trunk/net/bridge/netfilter/ebt_mark.c | 21 +- trunk/net/core/fib_rules.c | 1 - trunk/net/core/neighbour.c | 12 +- trunk/net/core/skbuff.c | 3 +- trunk/net/ipv4/Kconfig | 9 - trunk/net/ipv4/Makefile | 1 - trunk/net/ipv4/esp4.c | 26 +- trunk/net/ipv4/ipcomp.c | 5 +- trunk/net/ipv4/ipvs/ip_vs_core.c | 10 - trunk/net/ipv4/netfilter.c | 9 +- trunk/net/ipv4/netfilter/ip_nat_standalone.c | 3 +- trunk/net/ipv4/netfilter/ipt_REJECT.c | 97 +- trunk/net/ipv4/netfilter/iptable_mangle.c | 3 +- trunk/net/ipv4/tcp_input.c | 2 +- trunk/net/ipv4/udp.c | 2 - trunk/net/ipv4/xfrm4_mode_beet.c | 139 - trunk/net/ipv6/Kconfig | 10 - trunk/net/ipv6/Makefile | 1 - trunk/net/ipv6/fib6_rules.c | 1 - trunk/net/ipv6/ipcomp6.c | 5 +- trunk/net/ipv6/mip6.c | 1 - trunk/net/ipv6/udp.c | 64 +- trunk/net/ipv6/xfrm6_mode_beet.c | 107 - trunk/net/netfilter/Kconfig | 2 +- trunk/net/sched/estimator.c | 196 + trunk/net/sched/sch_htb.c | 2 +- trunk/net/sunrpc/auth_gss/svcauth_gss.c | 67 +- trunk/net/sunrpc/svc.c | 79 +- trunk/net/sunrpc/svcauth_unix.c | 47 +- trunk/net/sunrpc/svcsock.c | 51 +- trunk/net/tipc/link.c | 5 +- trunk/net/xfrm/xfrm_hash.h | 7 +- trunk/net/xfrm/xfrm_policy.c | 7 +- trunk/net/xfrm/xfrm_state.c | 16 +- trunk/net/xfrm/xfrm_user.c | 1 - trunk/scripts/Makefile.headersinst | 2 +- trunk/sound/aoa/soundbus/sysfs.c | 1 - trunk/sound/core/memory.c | 1 - trunk/sound/oss/Makefile | 57 +- trunk/sound/oss/ac97.c | 20 + trunk/sound/oss/ac97.h | 3 + trunk/sound/oss/ac97_codec.c | 89 + trunk/sound/oss/ac97_plugin_ad1980.c | 125 + trunk/sound/oss/ad1848.c | 4 +- trunk/sound/oss/ad1848.h | 1 + trunk/sound/oss/ali5455.c | 3735 ++++++++++ trunk/sound/oss/au1000.c | 2216 ++++++ trunk/sound/oss/audio_syms.c | 16 + trunk/sound/oss/awe_hw.h | 99 + trunk/sound/oss/awe_wave.c | 6148 +++++++++++++++++ trunk/sound/oss/awe_wave.h | 77 + trunk/sound/oss/cmpci.c | 3380 +++++++++ trunk/sound/oss/cs4281/Makefile | 6 + trunk/sound/oss/cs4281/cs4281_hwdefs.h | 1234 ++++ trunk/sound/oss/cs4281/cs4281_wrapper-24.c | 41 + trunk/sound/oss/cs4281/cs4281m.c | 4487 ++++++++++++ trunk/sound/oss/cs4281/cs4281pm-24.c | 45 + trunk/sound/oss/cs4281/cs4281pm.h | 74 + trunk/sound/oss/dev_table.c | 44 +- trunk/sound/oss/dev_table.h | 17 +- trunk/sound/oss/dm.h | 79 + trunk/sound/oss/dmabuf.c | 40 +- trunk/sound/oss/es1370.c | 2819 ++++++++ trunk/sound/oss/esssolo1.c | 2516 +++++++ trunk/sound/oss/forte.c | 2138 ++++++ trunk/sound/oss/gus.h | 24 + trunk/sound/oss/gus_card.c | 292 + trunk/sound/oss/gus_hw.h | 50 + trunk/sound/oss/gus_linearvol.h | 18 + trunk/sound/oss/gus_midi.c | 256 + trunk/sound/oss/gus_vol.c | 153 + trunk/sound/oss/gus_wave.c | 3463 ++++++++++ trunk/sound/oss/harmony.c | 1330 ++++ trunk/sound/oss/ics2101.c | 247 + trunk/sound/oss/iwmem.h | 36 + trunk/sound/oss/mad16.c | 1112 +++ trunk/sound/oss/maestro.c | 3686 ++++++++++ trunk/sound/oss/maestro.h | 60 + trunk/sound/oss/maestro3.c | 2968 ++++++++ trunk/sound/oss/maestro3.h | 821 +++ trunk/sound/oss/maui.c | 477 ++ trunk/sound/oss/midi_syms.c | 29 + trunk/sound/oss/midi_synth.c | 21 +- trunk/sound/oss/midibuf.c | 11 +- trunk/sound/oss/mpu401.c | 13 +- trunk/sound/oss/mpu401.h | 2 + trunk/sound/oss/opl3sa.c | 329 + trunk/sound/oss/rme96xx.c | 1857 +++++ trunk/sound/oss/rme96xx.h | 78 + trunk/sound/oss/sequencer.c | 15 +- trunk/sound/oss/sequencer_syms.c | 29 + trunk/sound/oss/sgalaxy.c | 207 + trunk/sound/oss/sonicvibes.c | 2792 ++++++++ trunk/sound/oss/sound_calls.h | 3 + trunk/sound/oss/sound_syms.c | 50 + trunk/sound/oss/sound_timer.c | 4 - trunk/sound/oss/soundcard.c | 16 +- trunk/sound/oss/tuning.h | 10 +- trunk/sound/oss/wavfront.c | 3553 ++++++++++ trunk/sound/oss/wf_midi.c | 880 +++ trunk/sound/oss/ymfpci.c | 2691 ++++++++ trunk/sound/oss/ymfpci.h | 360 + trunk/sound/oss/ymfpci_image.h | 1565 +++++ trunk/sound/oss/yss225.c | 319 + trunk/sound/oss/yss225.h | 24 + trunk/sound/sound_core.c | 34 + 741 files changed, 67496 insertions(+), 64247 deletions(-) delete mode 100644 trunk/Documentation/ecryptfs.txt delete mode 100644 trunk/Documentation/filesystems/gfs2.txt create mode 100644 trunk/Documentation/sound/oss/AWE32 create mode 100644 trunk/Documentation/sound/oss/CMI8338 create mode 100644 trunk/Documentation/sound/oss/INSTALL.awe create mode 100644 trunk/Documentation/sound/oss/MAD16 create mode 100644 trunk/Documentation/sound/oss/Maestro create mode 100644 trunk/Documentation/sound/oss/Maestro3 create mode 100644 trunk/Documentation/sound/oss/NEWS create mode 100644 trunk/Documentation/sound/oss/OPL3-SA create mode 100644 trunk/Documentation/sound/oss/README.awe create mode 100644 trunk/Documentation/sound/oss/Wavefront create mode 100644 trunk/Documentation/sound/oss/es1370 create mode 100644 trunk/Documentation/sound/oss/rme96xx create mode 100644 trunk/Documentation/sound/oss/solo1 create mode 100644 trunk/Documentation/sound/oss/sonicvibes delete mode 100644 trunk/arch/ia64/kernel/msi_ia64.c delete mode 100644 trunk/arch/powerpc/boot/dts/mpc8272ads.dts delete mode 100644 trunk/arch/powerpc/boot/dts/mpc8360emds.dts delete mode 100644 trunk/arch/powerpc/configs/mpc8360emds_defconfig delete mode 100644 trunk/arch/powerpc/platforms/82xx/Kconfig delete mode 100644 trunk/arch/powerpc/platforms/82xx/Makefile delete mode 100644 trunk/arch/powerpc/platforms/82xx/m82xx_pci.h delete mode 100644 trunk/arch/powerpc/platforms/82xx/mpc82xx.c delete mode 100644 trunk/arch/powerpc/platforms/82xx/mpc82xx_ads.c delete mode 100644 trunk/arch/powerpc/platforms/82xx/pq2ads.h delete mode 100644 trunk/arch/powerpc/platforms/83xx/mpc832x_mds.c delete mode 100644 trunk/arch/powerpc/platforms/83xx/mpc832x_mds.h delete mode 100644 trunk/arch/powerpc/platforms/83xx/mpc8360e_pb.c delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/Kconfig delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/Makefile delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/qe.c delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/qe_ic.c delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/qe_ic.h delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/qe_io.c delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/ucc.c delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/ucc_fast.c delete mode 100644 trunk/arch/powerpc/sysdev/qe_lib/ucc_slow.c delete mode 100644 trunk/drivers/char/agp/parisc-agp.c delete mode 100644 trunk/drivers/misc/tifm_7xx1.c delete mode 100644 trunk/drivers/misc/tifm_core.c delete mode 100644 trunk/drivers/mmc/tifm_sd.c delete mode 100644 trunk/drivers/pci/htirq.c rename trunk/{arch/ia64/sn/kernel/msi_sn.c => drivers/pci/msi-altix.c} (65%) create mode 100644 trunk/drivers/pci/msi-apic.c delete mode 100644 trunk/fs/dlm/Kconfig delete mode 100644 trunk/fs/dlm/Makefile delete mode 100644 trunk/fs/dlm/ast.c delete mode 100644 trunk/fs/dlm/ast.h delete mode 100644 trunk/fs/dlm/config.c delete mode 100644 trunk/fs/dlm/config.h delete mode 100644 trunk/fs/dlm/debug_fs.c delete mode 100644 trunk/fs/dlm/dir.c delete mode 100644 trunk/fs/dlm/dir.h delete mode 100644 trunk/fs/dlm/dlm_internal.h delete mode 100644 trunk/fs/dlm/lock.c delete mode 100644 trunk/fs/dlm/lock.h delete mode 100644 trunk/fs/dlm/lockspace.c delete mode 100644 trunk/fs/dlm/lockspace.h delete mode 100644 trunk/fs/dlm/lowcomms.c delete mode 100644 trunk/fs/dlm/lowcomms.h delete mode 100644 trunk/fs/dlm/lvb_table.h delete mode 100644 trunk/fs/dlm/main.c delete mode 100644 trunk/fs/dlm/member.c delete mode 100644 trunk/fs/dlm/member.h delete mode 100644 trunk/fs/dlm/memory.c delete mode 100644 trunk/fs/dlm/memory.h delete mode 100644 trunk/fs/dlm/midcomms.c delete mode 100644 trunk/fs/dlm/midcomms.h delete mode 100644 trunk/fs/dlm/rcom.c delete mode 100644 trunk/fs/dlm/rcom.h delete mode 100644 trunk/fs/dlm/recover.c delete mode 100644 trunk/fs/dlm/recover.h delete mode 100644 trunk/fs/dlm/recoverd.c delete mode 100644 trunk/fs/dlm/recoverd.h delete mode 100644 trunk/fs/dlm/requestqueue.c delete mode 100644 trunk/fs/dlm/requestqueue.h delete mode 100644 trunk/fs/dlm/user.c delete mode 100644 trunk/fs/dlm/user.h delete mode 100644 trunk/fs/dlm/util.c delete mode 100644 trunk/fs/dlm/util.h delete mode 100644 trunk/fs/ecryptfs/Makefile delete mode 100644 trunk/fs/ecryptfs/crypto.c delete mode 100644 trunk/fs/ecryptfs/debug.c delete mode 100644 trunk/fs/ecryptfs/dentry.c delete mode 100644 trunk/fs/ecryptfs/ecryptfs_kernel.h delete mode 100644 trunk/fs/ecryptfs/file.c delete mode 100644 trunk/fs/ecryptfs/inode.c delete mode 100644 trunk/fs/ecryptfs/keystore.c delete mode 100644 trunk/fs/ecryptfs/main.c delete mode 100644 trunk/fs/ecryptfs/mmap.c delete mode 100644 trunk/fs/ecryptfs/super.c delete mode 100644 trunk/fs/gfs2/Kconfig delete mode 100644 trunk/fs/gfs2/Makefile delete mode 100644 trunk/fs/gfs2/acl.c delete mode 100644 trunk/fs/gfs2/acl.h delete mode 100644 trunk/fs/gfs2/bmap.c delete mode 100644 trunk/fs/gfs2/bmap.h delete mode 100644 trunk/fs/gfs2/daemon.c delete mode 100644 trunk/fs/gfs2/daemon.h delete mode 100644 trunk/fs/gfs2/dir.c delete mode 100644 trunk/fs/gfs2/dir.h delete mode 100644 trunk/fs/gfs2/eaops.c delete mode 100644 trunk/fs/gfs2/eaops.h delete mode 100644 trunk/fs/gfs2/eattr.c delete mode 100644 trunk/fs/gfs2/eattr.h delete mode 100644 trunk/fs/gfs2/gfs2.h delete mode 100644 trunk/fs/gfs2/glock.c delete mode 100644 trunk/fs/gfs2/glock.h delete mode 100644 trunk/fs/gfs2/glops.c delete mode 100644 trunk/fs/gfs2/glops.h delete mode 100644 trunk/fs/gfs2/incore.h delete mode 100644 trunk/fs/gfs2/inode.c delete mode 100644 trunk/fs/gfs2/inode.h delete mode 100644 trunk/fs/gfs2/lm.c delete mode 100644 trunk/fs/gfs2/lm.h delete mode 100644 trunk/fs/gfs2/locking.c delete mode 100644 trunk/fs/gfs2/locking/dlm/Makefile delete mode 100644 trunk/fs/gfs2/locking/dlm/lock.c delete mode 100644 trunk/fs/gfs2/locking/dlm/lock_dlm.h delete mode 100644 trunk/fs/gfs2/locking/dlm/main.c delete mode 100644 trunk/fs/gfs2/locking/dlm/mount.c delete mode 100644 trunk/fs/gfs2/locking/dlm/plock.c delete mode 100644 trunk/fs/gfs2/locking/dlm/sysfs.c delete mode 100644 trunk/fs/gfs2/locking/dlm/thread.c delete mode 100644 trunk/fs/gfs2/locking/nolock/Makefile delete mode 100644 trunk/fs/gfs2/locking/nolock/main.c delete mode 100644 trunk/fs/gfs2/log.c delete mode 100644 trunk/fs/gfs2/log.h delete mode 100644 trunk/fs/gfs2/lops.c delete mode 100644 trunk/fs/gfs2/lops.h delete mode 100644 trunk/fs/gfs2/main.c delete mode 100644 trunk/fs/gfs2/meta_io.c delete mode 100644 trunk/fs/gfs2/meta_io.h delete mode 100644 trunk/fs/gfs2/mount.c delete mode 100644 trunk/fs/gfs2/mount.h delete mode 100644 trunk/fs/gfs2/ondisk.c delete mode 100644 trunk/fs/gfs2/ops_address.c delete mode 100644 trunk/fs/gfs2/ops_address.h delete mode 100644 trunk/fs/gfs2/ops_dentry.c delete mode 100644 trunk/fs/gfs2/ops_dentry.h delete mode 100644 trunk/fs/gfs2/ops_export.c delete mode 100644 trunk/fs/gfs2/ops_export.h delete mode 100644 trunk/fs/gfs2/ops_file.c delete mode 100644 trunk/fs/gfs2/ops_file.h delete mode 100644 trunk/fs/gfs2/ops_fstype.c delete mode 100644 trunk/fs/gfs2/ops_fstype.h delete mode 100644 trunk/fs/gfs2/ops_inode.c delete mode 100644 trunk/fs/gfs2/ops_inode.h delete mode 100644 trunk/fs/gfs2/ops_super.c delete mode 100644 trunk/fs/gfs2/ops_super.h delete mode 100644 trunk/fs/gfs2/ops_vm.c delete mode 100644 trunk/fs/gfs2/ops_vm.h delete mode 100644 trunk/fs/gfs2/quota.c delete mode 100644 trunk/fs/gfs2/quota.h delete mode 100644 trunk/fs/gfs2/recovery.c delete mode 100644 trunk/fs/gfs2/recovery.h delete mode 100644 trunk/fs/gfs2/rgrp.c delete mode 100644 trunk/fs/gfs2/rgrp.h delete mode 100644 trunk/fs/gfs2/super.c delete mode 100644 trunk/fs/gfs2/super.h delete mode 100644 trunk/fs/gfs2/sys.c delete mode 100644 trunk/fs/gfs2/sys.h delete mode 100644 trunk/fs/gfs2/trans.c delete mode 100644 trunk/fs/gfs2/trans.h delete mode 100644 trunk/fs/gfs2/util.c delete mode 100644 trunk/fs/gfs2/util.h delete mode 100644 trunk/include/asm-i386/hypertransport.h create mode 100644 trunk/include/asm-i386/msi.h delete mode 100644 trunk/include/asm-i386/msidef.h create mode 100644 trunk/include/asm-ia64/msi.h delete mode 100644 trunk/include/asm-parisc/agp.h create mode 100644 trunk/include/asm-parisc/iosapic.h delete mode 100644 trunk/include/asm-parisc/mckinley.h delete mode 100644 trunk/include/asm-parisc/prefetch.h delete mode 100644 trunk/include/asm-parisc/ropes.h delete mode 100644 trunk/include/asm-powerpc/immap_qe.h delete mode 100644 trunk/include/asm-powerpc/qe.h delete mode 100644 trunk/include/asm-powerpc/qe_ic.h delete mode 100644 trunk/include/asm-powerpc/ucc.h delete mode 100644 trunk/include/asm-powerpc/ucc_fast.h delete mode 100644 trunk/include/asm-powerpc/ucc_slow.h delete mode 100644 trunk/include/asm-x86_64/hypertransport.h create mode 100644 trunk/include/asm-x86_64/msi.h delete mode 100644 trunk/include/asm-x86_64/msidef.h delete mode 100644 trunk/include/linux/dlm.h delete mode 100644 trunk/include/linux/dlm_device.h delete mode 100644 trunk/include/linux/gfs2_ondisk.h delete mode 100644 trunk/include/linux/htirq.h delete mode 100644 trunk/include/linux/lm_interface.h delete mode 100644 trunk/include/linux/lock_dlm_plock.h delete mode 100644 trunk/include/linux/msi.h delete mode 100644 trunk/include/linux/srcu.h delete mode 100644 trunk/include/linux/tifm.h create mode 100644 trunk/include/linux/wavefront.h delete mode 100644 trunk/kernel/srcu.c delete mode 100644 trunk/net/ipv4/xfrm4_mode_beet.c delete mode 100644 trunk/net/ipv6/xfrm6_mode_beet.c create mode 100644 trunk/net/sched/estimator.c create mode 100644 trunk/sound/oss/ac97_plugin_ad1980.c create mode 100644 trunk/sound/oss/ali5455.c create mode 100644 trunk/sound/oss/au1000.c create mode 100644 trunk/sound/oss/audio_syms.c create mode 100644 trunk/sound/oss/awe_hw.h create mode 100644 trunk/sound/oss/awe_wave.c create mode 100644 trunk/sound/oss/awe_wave.h create mode 100644 trunk/sound/oss/cmpci.c create mode 100644 trunk/sound/oss/cs4281/Makefile create mode 100644 trunk/sound/oss/cs4281/cs4281_hwdefs.h create mode 100644 trunk/sound/oss/cs4281/cs4281_wrapper-24.c create mode 100644 trunk/sound/oss/cs4281/cs4281m.c create mode 100644 trunk/sound/oss/cs4281/cs4281pm-24.c create mode 100644 trunk/sound/oss/cs4281/cs4281pm.h create mode 100644 trunk/sound/oss/dm.h create mode 100644 trunk/sound/oss/es1370.c create mode 100644 trunk/sound/oss/esssolo1.c create mode 100644 trunk/sound/oss/forte.c create mode 100644 trunk/sound/oss/gus.h create mode 100644 trunk/sound/oss/gus_card.c create mode 100644 trunk/sound/oss/gus_hw.h create mode 100644 trunk/sound/oss/gus_linearvol.h create mode 100644 trunk/sound/oss/gus_midi.c create mode 100644 trunk/sound/oss/gus_vol.c create mode 100644 trunk/sound/oss/gus_wave.c create mode 100644 trunk/sound/oss/harmony.c create mode 100644 trunk/sound/oss/ics2101.c create mode 100644 trunk/sound/oss/iwmem.h create mode 100644 trunk/sound/oss/mad16.c create mode 100644 trunk/sound/oss/maestro.c create mode 100644 trunk/sound/oss/maestro.h create mode 100644 trunk/sound/oss/maestro3.c create mode 100644 trunk/sound/oss/maestro3.h create mode 100644 trunk/sound/oss/maui.c create mode 100644 trunk/sound/oss/midi_syms.c create mode 100644 trunk/sound/oss/opl3sa.c create mode 100644 trunk/sound/oss/rme96xx.c create mode 100644 trunk/sound/oss/rme96xx.h create mode 100644 trunk/sound/oss/sequencer_syms.c create mode 100644 trunk/sound/oss/sgalaxy.c create mode 100644 trunk/sound/oss/sonicvibes.c create mode 100644 trunk/sound/oss/sound_syms.c create mode 100644 trunk/sound/oss/wavfront.c create mode 100644 trunk/sound/oss/wf_midi.c create mode 100644 trunk/sound/oss/ymfpci.c create mode 100644 trunk/sound/oss/ymfpci.h create mode 100644 trunk/sound/oss/ymfpci_image.h create mode 100644 trunk/sound/oss/yss225.c create mode 100644 trunk/sound/oss/yss225.h diff --git a/[refs] b/[refs] index d145c0801d70..bf18080516b6 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 4a61f17378c2cdd9bd8f34ef8bd7422861d0c1f1 +refs/heads/master: 038b0a6d8d32db934bba6a24e74e76e4e327a94f diff --git a/trunk/CREDITS b/trunk/CREDITS index 5329ead9c672..dba3e6334691 100644 --- a/trunk/CREDITS +++ b/trunk/CREDITS @@ -2240,12 +2240,6 @@ D: tc: HFSC scheduler S: Freiburg S: Germany -N: Paul E. McKenney -E: paulmck@us.ibm.com -W: http://www.rdrop.com/users/paulmck/ -D: RCU and variants -D: rcutorture module - N: Mike McLagan E: mike.mclagan@linux.org W: http://www.invlogic.com/~mmclagan @@ -2987,10 +2981,6 @@ S: 69 rue Dunois S: 75013 Paris S: France -N: Dipankar Sarma -E: dipankar@in.ibm.com -D: RCU - N: Hannu Savolainen E: hannu@opensound.com D: Maintainer of the sound drivers until 2.1.x days. @@ -3303,12 +3293,6 @@ S: 3 Ballow Crescent S: MacGregor A.C.T 2615 S: Australia -N: Josh Triplett -E: josh@freedesktop.org -P: 1024D/D0FE7AFB B24A 65C9 1D71 2AC2 DE87 CA26 189B 9946 D0FE 7AFB -D: rcutorture maintainer -D: lock annotations, finding and fixing lock bugs - N: Winfried Trümper E: winni@xpilot.org W: http://www.shop.de/~winni/ @@ -3578,11 +3562,11 @@ S: Fargo, North Dakota 58122 S: USA N: Steven Whitehouse -E: steve@chygwyn.com +E: SteveW@ACM.org W: http://www.chygwyn.com/~steve -D: Linux DECnet project +D: Linux DECnet project: http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html D: Minor debugging of other networking protocols. -D: Misc bug fixes and GFS2 filesystem development +D: Misc bug fixes and filesystem development N: Hans-Joachim Widmaier E: hjw@zvw.de diff --git a/trunk/Documentation/DocBook/kernel-api.tmpl b/trunk/Documentation/DocBook/kernel-api.tmpl index 2b5ac604948c..49c745720f47 100644 --- a/trunk/Documentation/DocBook/kernel-api.tmpl +++ b/trunk/Documentation/DocBook/kernel-api.tmpl @@ -158,7 +158,6 @@ X!Ilib/string.c !Emm/filemap.c !Emm/memory.c !Emm/vmalloc.c -!Imm/page_alloc.c !Emm/mempool.c !Emm/page-writeback.c !Emm/truncate.c diff --git a/trunk/Documentation/DocBook/libata.tmpl b/trunk/Documentation/DocBook/libata.tmpl index 07a635590b36..c684abf0d3b2 100644 --- a/trunk/Documentation/DocBook/libata.tmpl +++ b/trunk/Documentation/DocBook/libata.tmpl @@ -14,7 +14,7 @@ - 2003-2006 + 2003-2005 Jeff Garzik diff --git a/trunk/Documentation/RCU/checklist.txt b/trunk/Documentation/RCU/checklist.txt index f4dffadbcb00..1d50cf0c905e 100644 --- a/trunk/Documentation/RCU/checklist.txt +++ b/trunk/Documentation/RCU/checklist.txt @@ -221,41 +221,3 @@ over a rather long period of time, but improvements are always welcome! disable irq on a given acquisition of that lock will result in deadlock as soon as the RCU callback happens to interrupt that acquisition's critical section. - -13. SRCU (srcu_read_lock(), srcu_read_unlock(), and synchronize_srcu()) - may only be invoked from process context. Unlike other forms of - RCU, it -is- permissible to block in an SRCU read-side critical - section (demarked by srcu_read_lock() and srcu_read_unlock()), - hence the "SRCU": "sleepable RCU". Please note that if you - don't need to sleep in read-side critical sections, you should - be using RCU rather than SRCU, because RCU is almost always - faster and easier to use than is SRCU. - - Also unlike other forms of RCU, explicit initialization - and cleanup is required via init_srcu_struct() and - cleanup_srcu_struct(). These are passed a "struct srcu_struct" - that defines the scope of a given SRCU domain. Once initialized, - the srcu_struct is passed to srcu_read_lock(), srcu_read_unlock() - and synchronize_srcu(). A given synchronize_srcu() waits only - for SRCU read-side critical sections governed by srcu_read_lock() - and srcu_read_unlock() calls that have been passd the same - srcu_struct. This property is what makes sleeping read-side - critical sections tolerable -- a given subsystem delays only - its own updates, not those of other subsystems using SRCU. - Therefore, SRCU is less prone to OOM the system than RCU would - be if RCU's read-side critical sections were permitted to - sleep. - - The ability to sleep in read-side critical sections does not - come for free. First, corresponding srcu_read_lock() and - srcu_read_unlock() calls must be passed the same srcu_struct. - Second, grace-period-detection overhead is amortized only - over those updates sharing a given srcu_struct, rather than - being globally amortized as they are for other forms of RCU. - Therefore, SRCU should be used in preference to rw_semaphore - only in extremely read-intensive situations, or in situations - requiring SRCU's read-side deadlock immunity or low read-side - realtime latency. - - Note that, rcu_assign_pointer() and rcu_dereference() relate to - SRCU just as they do to other forms of RCU. diff --git a/trunk/Documentation/RCU/rcu.txt b/trunk/Documentation/RCU/rcu.txt index f84407cba816..02e27bf1d365 100644 --- a/trunk/Documentation/RCU/rcu.txt +++ b/trunk/Documentation/RCU/rcu.txt @@ -45,8 +45,7 @@ o How can I see where RCU is currently used in the Linux kernel? Search for "rcu_read_lock", "rcu_read_unlock", "call_rcu", "rcu_read_lock_bh", "rcu_read_unlock_bh", "call_rcu_bh", - "srcu_read_lock", "srcu_read_unlock", "synchronize_rcu", - "synchronize_net", and "synchronize_srcu". + "synchronize_rcu", and "synchronize_net". o What guidelines should I follow when writing code that uses RCU? diff --git a/trunk/Documentation/RCU/torture.txt b/trunk/Documentation/RCU/torture.txt index 25a3c3f7d378..a4948591607d 100644 --- a/trunk/Documentation/RCU/torture.txt +++ b/trunk/Documentation/RCU/torture.txt @@ -28,15 +28,6 @@ nreaders This is the number of RCU reading threads supported. To properly exercise RCU implementations with preemptible read-side critical sections. -nfakewriters This is the number of RCU fake writer threads to run. Fake - writer threads repeatedly use the synchronous "wait for - current readers" function of the interface selected by - torture_type, with a delay between calls to allow for various - different numbers of writers running in parallel. - nfakewriters defaults to 4, which provides enough parallelism - to trigger special cases caused by multiple writers, such as - the synchronize_srcu() early return optimization. - stat_interval The number of seconds between output of torture statistics (via printk()). Regardless of the interval, statistics are printed when the module is unloaded. @@ -53,12 +44,9 @@ test_no_idle_hz Whether or not to test the ability of RCU to operate in a kernel that disables the scheduling-clock interrupt to idle CPUs. Boolean parameter, "1" to test, "0" otherwise. -torture_type The type of RCU to test: "rcu" for the rcu_read_lock() API, - "rcu_sync" for rcu_read_lock() with synchronous reclamation, - "rcu_bh" for the rcu_read_lock_bh() API, "rcu_bh_sync" for - rcu_read_lock_bh() with synchronous reclamation, "srcu" for - the "srcu_read_lock()" API, and "sched" for the use of - preempt_disable() together with synchronize_sched(). +torture_type The type of RCU to test: "rcu" for the rcu_read_lock() + API, "rcu_bh" for the rcu_read_lock_bh() API, and "srcu" + for the "srcu_read_lock()" API. verbose Enable debug printk()s. Default is disabled. @@ -130,21 +118,6 @@ o "Free-Block Circulation": Shows the number of torture structures as it is only incremented if a torture structure's counter somehow gets incremented farther than it should. -Different implementations of RCU can provide implementation-specific -additional information. For example, SRCU provides the following: - - srcu-torture: rtc: f8cf46a8 ver: 355 tfle: 0 rta: 356 rtaf: 0 rtf: 346 rtmbe: 0 - srcu-torture: Reader Pipe: 559738 939 0 0 0 0 0 0 0 0 0 - srcu-torture: Reader Batch: 560434 243 0 0 0 0 0 0 0 0 - srcu-torture: Free-Block Circulation: 355 354 353 352 351 350 349 348 347 346 0 - srcu-torture: per-CPU(idx=1): 0(0,1) 1(0,1) 2(0,0) 3(0,1) - -The first four lines are similar to those for RCU. The last line shows -the per-CPU counter state. The numbers in parentheses are the values -of the "old" and "current" counters for the corresponding CPU. The -"idx" value maps the "old" and "current" values to the underlying array, -and is useful for debugging. - USAGE diff --git a/trunk/Documentation/RCU/whatisRCU.txt b/trunk/Documentation/RCU/whatisRCU.txt index e0d6d99b8f9b..820fee236967 100644 --- a/trunk/Documentation/RCU/whatisRCU.txt +++ b/trunk/Documentation/RCU/whatisRCU.txt @@ -778,8 +778,6 @@ Markers for RCU read-side critical sections: rcu_read_unlock rcu_read_lock_bh rcu_read_unlock_bh - srcu_read_lock - srcu_read_unlock RCU pointer/list traversal: @@ -806,7 +804,6 @@ RCU grace period: synchronize_net synchronize_sched synchronize_rcu - synchronize_srcu call_rcu call_rcu_bh diff --git a/trunk/Documentation/ecryptfs.txt b/trunk/Documentation/ecryptfs.txt deleted file mode 100644 index 01d8a08351ac..000000000000 --- a/trunk/Documentation/ecryptfs.txt +++ /dev/null @@ -1,77 +0,0 @@ -eCryptfs: A stacked cryptographic filesystem for Linux - -eCryptfs is free software. Please see the file COPYING for details. -For documentation, please see the files in the doc/ subdirectory. For -building and installation instructions please see the INSTALL file. - -Maintainer: Phillip Hellewell -Lead developer: Michael A. Halcrow -Developers: Michael C. Thompson - Kent Yoder -Web Site: http://ecryptfs.sf.net - -This software is currently undergoing development. Make sure to -maintain a backup copy of any data you write into eCryptfs. - -eCryptfs requires the userspace tools downloadable from the -SourceForge site: - -http://sourceforge.net/projects/ecryptfs/ - -Userspace requirements include: - - David Howells' userspace keyring headers and libraries (version - 1.0 or higher), obtainable from - http://people.redhat.com/~dhowells/keyutils/ - - Libgcrypt - - -NOTES - -In the beta/experimental releases of eCryptfs, when you upgrade -eCryptfs, you should copy the files to an unencrypted location and -then copy the files back into the new eCryptfs mount to migrate the -files. - - -MOUNT-WIDE PASSPHRASE - -Create a new directory into which eCryptfs will write its encrypted -files (i.e., /root/crypt). Then, create the mount point directory -(i.e., /mnt/crypt). Now it's time to mount eCryptfs: - -mount -t ecryptfs /root/crypt /mnt/crypt - -You should be prompted for a passphrase and a salt (the salt may be -blank). - -Try writing a new file: - -echo "Hello, World" > /mnt/crypt/hello.txt - -The operation will complete. Notice that there is a new file in -/root/crypt that is at least 12288 bytes in size (depending on your -host page size). This is the encrypted underlying file for what you -just wrote. To test reading, from start to finish, you need to clear -the user session keyring: - -keyctl clear @u - -Then umount /mnt/crypt and mount again per the instructions given -above. - -cat /mnt/crypt/hello.txt - - -NOTES - -eCryptfs version 0.1 should only be mounted on (1) empty directories -or (2) directories containing files only created by eCryptfs. If you -mount a directory that has pre-existing files not created by eCryptfs, -then behavior is undefined. Do not run eCryptfs in higher verbosity -levels unless you are doing so for the sole purpose of debugging or -development, since secret values will be written out to the system log -in that case. - - -Mike Halcrow -mhalcrow@us.ibm.com diff --git a/trunk/Documentation/feature-removal-schedule.txt b/trunk/Documentation/feature-removal-schedule.txt index 24f3c63b3017..42b95e0ad558 100644 --- a/trunk/Documentation/feature-removal-schedule.txt +++ b/trunk/Documentation/feature-removal-schedule.txt @@ -29,6 +29,14 @@ Who: Adrian Bunk --------------------------- +What: drivers that were depending on OBSOLETE_OSS_DRIVER + (config options already removed) +When: before 2.6.19 +Why: OSS drivers with ALSA replacements +Who: Adrian Bunk + +--------------------------- + What: raw1394: requests of type RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN When: November 2006 Why: Deprecated in favour of the new ioctl-based rawiso interface, which is diff --git a/trunk/Documentation/filesystems/gfs2.txt b/trunk/Documentation/filesystems/gfs2.txt deleted file mode 100644 index 593004b6bbab..000000000000 --- a/trunk/Documentation/filesystems/gfs2.txt +++ /dev/null @@ -1,43 +0,0 @@ -Global File System ------------------- - -http://sources.redhat.com/cluster/ - -GFS is a cluster file system. It allows a cluster of computers to -simultaneously use a block device that is shared between them (with FC, -iSCSI, NBD, etc). GFS reads and writes to the block device like a local -file system, but also uses a lock module to allow the computers coordinate -their I/O so file system consistency is maintained. One of the nifty -features of GFS is perfect consistency -- changes made to the file system -on one machine show up immediately on all other machines in the cluster. - -GFS uses interchangable inter-node locking mechanisms. Different lock -modules can plug into GFS and each file system selects the appropriate -lock module at mount time. Lock modules include: - - lock_nolock -- allows gfs to be used as a local file system - - lock_dlm -- uses a distributed lock manager (dlm) for inter-node locking - The dlm is found at linux/fs/dlm/ - -In addition to interfacing with an external locking manager, a gfs lock -module is responsible for interacting with external cluster management -systems. Lock_dlm depends on user space cluster management systems found -at the URL above. - -To use gfs as a local file system, no external clustering systems are -needed, simply: - - $ mkfs -t gfs2 -p lock_nolock -j 1 /dev/block_device - $ mount -t gfs2 /dev/block_device /dir - -GFS2 is not on-disk compatible with previous versions of GFS. - -The following man pages can be found at the URL above: - gfs2_fsck to repair a filesystem - gfs2_grow to expand a filesystem online - gfs2_jadd to add journals to a filesystem online - gfs2_tool to manipulate, examine and tune a filesystem - gfs2_quota to examine and change quota values in a filesystem - mount.gfs2 to help mount(8) mount a filesystem - mkfs.gfs2 to make a filesystem diff --git a/trunk/Documentation/kbuild/kconfig-language.txt b/trunk/Documentation/kbuild/kconfig-language.txt index 125093c3ef76..7f34778dd23b 100644 --- a/trunk/Documentation/kbuild/kconfig-language.txt +++ b/trunk/Documentation/kbuild/kconfig-language.txt @@ -1,7 +1,7 @@ Introduction ------------ -The configuration database is a collection of configuration options +The configuration database is collection of configuration options organized in a tree structure: +- Code maturity level options diff --git a/trunk/Documentation/kbuild/makefiles.txt b/trunk/Documentation/kbuild/makefiles.txt index 50f4eddf899c..e2cbd59cf2d0 100644 --- a/trunk/Documentation/kbuild/makefiles.txt +++ b/trunk/Documentation/kbuild/makefiles.txt @@ -390,7 +390,7 @@ more details, with real examples. The kernel may be built with several different versions of $(CC), each supporting a unique set of features and options. kbuild provide basic support to check for valid options for $(CC). - $(CC) is usually the gcc compiler, but other alternatives are + $(CC) is useally the gcc compiler, but other alternatives are available. as-option diff --git a/trunk/Documentation/kernel-parameters.txt b/trunk/Documentation/kernel-parameters.txt index ff571f9298e0..12b3b24bfd2f 100644 --- a/trunk/Documentation/kernel-parameters.txt +++ b/trunk/Documentation/kernel-parameters.txt @@ -289,6 +289,9 @@ and is between 256 and 4096 characters. It is defined in the file autotest [IA64] + awe= [HW,OSS] AWE32/SB32/AWE64 wave table synth + Format: ,, + aztcd= [HW,CD] Aztech CD268 CDROM driver Format: ,0x79 (?) @@ -533,6 +536,10 @@ and is between 256 and 4096 characters. It is defined in the file Default value is 0. Value can be changed at runtime via /selinux/enforce. + es1370= [HW,OSS] + Format: [,] + See also header of sound/oss/es1370.c. + es1371= [HW,OSS] Format: ,[,[]] See also header of sound/oss/es1371.c. @@ -573,6 +580,9 @@ and is between 256 and 4096 characters. It is defined in the file gscd= [HW,CD] Format: + gus= [HW,OSS] + Format: ,,, + gvp11= [HW,SCSI] hashdist= [KNL,NUMA] Large hashes allocated during boot @@ -831,6 +841,12 @@ and is between 256 and 4096 characters. It is defined in the file (machvec) in a generic kernel. Example: machvec=hpzx1_swiotlb + mad16= [HW,OSS] Format: + ,,,,,, + + maui= [HW,OSS] + Format: , + max_loop= [LOOP] Maximum number of loopback devices that can be mounted Format: <1-256> @@ -1098,6 +1114,9 @@ and is between 256 and 4096 characters. It is defined in the file opl3= [HW,OSS] Format: + opl3sa= [HW,OSS] + Format: ,,,,, + opl3sa2= [HW,OSS] Format: ,,,,,,,[,, Run specified binary instead of /init from the ramdisk, @@ -1432,6 +1455,9 @@ and is between 256 and 4096 characters. It is defined in the file sg_def_reserved_size= [SCSI] + sgalaxy= [HW,OSS] + Format: ,,,, + shapers= [NET] Maximal number of shapers. @@ -1572,6 +1598,9 @@ and is between 256 and 4096 characters. It is defined in the file snd-ymfpci= [HW,ALSA] + sonicvibes= [HW,OSS] + Format: + sonycd535= [HW,CD] Format: [,] diff --git a/trunk/Documentation/powerpc/booting-without-of.txt b/trunk/Documentation/powerpc/booting-without-of.txt index 27b457c09729..1ccc8a515b44 100644 --- a/trunk/Documentation/powerpc/booting-without-of.txt +++ b/trunk/Documentation/powerpc/booting-without-of.txt @@ -1440,258 +1440,6 @@ platforms are moved over to use the flattened-device-tree model. descriptor-types-mask = <012b0ebf>; }; - h) Board Control and Status (BCSR) - - Required properties: - - - device_type : Should be "board-control" - - reg : Offset and length of the register set for the device - - Example: - - bcsr@f8000000 { - device_type = "board-control"; - reg = ; - }; - - i) Freescale QUICC Engine module (QE) - This represents qe module that is installed on PowerQUICC II Pro. - Hopefully it will merge backward compatibility with CPM/CPM2. - Basically, it is a bus of devices, that could act more or less - as a complete entity (UCC, USB etc ). All of them should be siblings on - the "root" qe node, using the common properties from there. - The description below applies to the the qe of MPC8360 and - more nodes and properties would be extended in the future. - - i) Root QE device - - Required properties: - - device_type : should be "qe"; - - model : precise model of the QE, Can be "QE", "CPM", or "CPM2" - - reg : offset and length of the device registers. - - bus-frequency : the clock frequency for QUICC Engine. - - Recommended properties - - brg-frequency : the internal clock source frequency for baud-rate - generators in Hz. - - Example: - qe@e0100000 { - #address-cells = <1>; - #size-cells = <1>; - #interrupt-cells = <2>; - device_type = "qe"; - model = "QE"; - ranges = <0 e0100000 00100000>; - reg = ; - brg-frequency = <0>; - bus-frequency = <179A7B00>; - } - - - ii) SPI (Serial Peripheral Interface) - - Required properties: - - device_type : should be "spi". - - compatible : should be "fsl_spi". - - mode : the spi operation mode, it can be "cpu" or "qe". - - reg : Offset and length of the register set for the device - - interrupts : where a is the interrupt number and b is a - field that represents an encoding of the sense and level - information for the interrupt. This should be encoded based on - the information in section 2) depending on the type of interrupt - controller you have. - - interrupt-parent : the phandle for the interrupt controller that - services interrupts for this device. - - Example: - spi@4c0 { - device_type = "spi"; - compatible = "fsl_spi"; - reg = <4c0 40>; - interrupts = <82 0>; - interrupt-parent = <700>; - mode = "cpu"; - }; - - - iii) USB (Universal Serial Bus Controller) - - Required properties: - - device_type : should be "usb". - - compatible : could be "qe_udc" or "fhci-hcd". - - mode : the could be "host" or "slave". - - reg : Offset and length of the register set for the device - - interrupts : where a is the interrupt number and b is a - field that represents an encoding of the sense and level - information for the interrupt. This should be encoded based on - the information in section 2) depending on the type of interrupt - controller you have. - - interrupt-parent : the phandle for the interrupt controller that - services interrupts for this device. - - Example(slave): - usb@6c0 { - device_type = "usb"; - compatible = "qe_udc"; - reg = <6c0 40>; - interrupts = <8b 0>; - interrupt-parent = <700>; - mode = "slave"; - }; - - - iv) UCC (Unified Communications Controllers) - - Required properties: - - device_type : should be "network", "hldc", "uart", "transparent" - "bisync" or "atm". - - compatible : could be "ucc_geth" or "fsl_atm" and so on. - - model : should be "UCC". - - device-id : the ucc number(1-8), corresponding to UCCx in UM. - - reg : Offset and length of the register set for the device - - interrupts : where a is the interrupt number and b is a - field that represents an encoding of the sense and level - information for the interrupt. This should be encoded based on - the information in section 2) depending on the type of interrupt - controller you have. - - interrupt-parent : the phandle for the interrupt controller that - services interrupts for this device. - - pio-handle : The phandle for the Parallel I/O port configuration. - - rx-clock : represents the UCC receive clock source. - 0x00 : clock source is disabled; - 0x1~0x10 : clock source is BRG1~BRG16 respectively; - 0x11~0x28: clock source is QE_CLK1~QE_CLK24 respectively. - - tx-clock: represents the UCC transmit clock source; - 0x00 : clock source is disabled; - 0x1~0x10 : clock source is BRG1~BRG16 respectively; - 0x11~0x28: clock source is QE_CLK1~QE_CLK24 respectively. - - Required properties for network device_type: - - mac-address : list of bytes representing the ethernet address. - - phy-handle : The phandle for the PHY connected to this controller. - - Example: - ucc@2000 { - device_type = "network"; - compatible = "ucc_geth"; - model = "UCC"; - device-id = <1>; - reg = <2000 200>; - interrupts = ; - interrupt-parent = <700>; - mac-address = [ 00 04 9f 00 23 23 ]; - rx-clock = "none"; - tx-clock = "clk9"; - phy-handle = <212000>; - pio-handle = <140001>; - }; - - - v) Parallel I/O Ports - - This node configures Parallel I/O ports for CPUs with QE support. - The node should reside in the "soc" node of the tree. For each - device that using parallel I/O ports, a child node should be created. - See the definition of the Pin configuration nodes below for more - information. - - Required properties: - - device_type : should be "par_io". - - reg : offset to the register set and its length. - - num-ports : number of Parallel I/O ports - - Example: - par_io@1400 { - reg = <1400 100>; - #address-cells = <1>; - #size-cells = <0>; - device_type = "par_io"; - num-ports = <7>; - ucc_pin@01 { - ...... - }; - - - vi) Pin configuration nodes - - Required properties: - - linux,phandle : phandle of this node; likely referenced by a QE - device. - - pio-map : array of pin configurations. Each pin is defined by 6 - integers. The six numbers are respectively: port, pin, dir, - open_drain, assignment, has_irq. - - port : port number of the pin; 0-6 represent port A-G in UM. - - pin : pin number in the port. - - dir : direction of the pin, should encode as follows: - - 0 = The pin is disabled - 1 = The pin is an output - 2 = The pin is an input - 3 = The pin is I/O - - - open_drain : indicates the pin is normal or wired-OR: - - 0 = The pin is actively driven as an output - 1 = The pin is an open-drain driver. As an output, the pin is - driven active-low, otherwise it is three-stated. - - - assignment : function number of the pin according to the Pin Assignment - tables in User Manual. Each pin can have up to 4 possible functions in - QE and two options for CPM. - - has_irq : indicates if the pin is used as source of exteral - interrupts. - - Example: - ucc_pin@01 { - linux,phandle = <140001>; - pio-map = < - /* port pin dir open_drain assignment has_irq */ - 0 3 1 0 1 0 /* TxD0 */ - 0 4 1 0 1 0 /* TxD1 */ - 0 5 1 0 1 0 /* TxD2 */ - 0 6 1 0 1 0 /* TxD3 */ - 1 6 1 0 3 0 /* TxD4 */ - 1 7 1 0 1 0 /* TxD5 */ - 1 9 1 0 2 0 /* TxD6 */ - 1 a 1 0 2 0 /* TxD7 */ - 0 9 2 0 1 0 /* RxD0 */ - 0 a 2 0 1 0 /* RxD1 */ - 0 b 2 0 1 0 /* RxD2 */ - 0 c 2 0 1 0 /* RxD3 */ - 0 d 2 0 1 0 /* RxD4 */ - 1 1 2 0 2 0 /* RxD5 */ - 1 0 2 0 2 0 /* RxD6 */ - 1 4 2 0 2 0 /* RxD7 */ - 0 7 1 0 1 0 /* TX_EN */ - 0 8 1 0 1 0 /* TX_ER */ - 0 f 2 0 1 0 /* RX_DV */ - 0 10 2 0 1 0 /* RX_ER */ - 0 0 2 0 1 0 /* RX_CLK */ - 2 9 1 0 3 0 /* GTX_CLK - CLK10 */ - 2 8 2 0 1 0>; /* GTX125 - CLK9 */ - }; - - vii) Multi-User RAM (MURAM) - - Required properties: - - device_type : should be "muram". - - mode : the could be "host" or "slave". - - ranges : Should be defined as specified in 1) to describe the - translation of MURAM addresses. - - data-only : sub-node which defines the address area under MURAM - bus that can be allocated as data/parameter - - Example: - - muram@10000 { - device_type = "muram"; - ranges = <0 00010000 0000c000>; - - data-only@0{ - reg = <0 c000>; - }; - }; More devices will be defined as this spec matures. diff --git a/trunk/Documentation/sound/oss/AWE32 b/trunk/Documentation/sound/oss/AWE32 new file mode 100644 index 000000000000..b5908a66ff55 --- /dev/null +++ b/trunk/Documentation/sound/oss/AWE32 @@ -0,0 +1,76 @@ + Installing and using Creative AWE midi sound under Linux. + +This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and +SB32. + +1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This + is important, because the driver works only with real Creative cards. + +2) The first thing you need to do is re-compile your kernel with support for + your sound card. Run your favourite tool to configure the kernel and when + you get to the "Sound" menu you should enable support for the following: + + Sound card support, + OSS sound modules, + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support, + AWE32 synth + + If your card is "Plug and Play" you will also need to enable these two + options, found under the "Plug and Play configuration" menu: + + Plug and Play support + ISA Plug and Play support + + Now compile and install the kernel in normal fashion. If you don't know + how to do this you can find instructions for this in the README file + located in the root directory of the kernel source. + +3) Before you can start playing midi files you will have to load a sound + bank file. The utility needed for doing this is called "sfxload", and it + is one of the utilities found in a package called "awesfx". If this + package is not available in your distribution you can download the AWE + snapshot from Creative Labs Open Source website: + + http://www.opensource.creative.com/snapshot.html + + Once you have unpacked the AWE snapshot you will see a "awesfx" + directory. Follow the instructions in awesfx/docs/INSTALL to install the + utilities in this package. After doing this, sfxload should be installed + as: + + /usr/local/bin/sfxload + + To enable AWE general midi synthesis you should also get the sound bank + file for general midi from: + + http://members.xoom.com/yar/synthgm.sbk.gz + + Copy it to a directory of your choice, and unpack it there. + +4) Edit /etc/modprobe.conf, and insert the following lines at the end of the + file: + + alias sound-slot-0 sb + alias sound-service-0-1 awe_wave + install awe_wave /sbin/modprobe --first-time -i awe_wave && /usr/local/bin/sfxload PATH_TO_SOUND_BANK_FILE + + You will of course have to change "PATH_TO_SOUND_BANK_FILE" to the full + path of the sound bank file. That will enable the Sound Blaster and AWE + wave synthesis. To play midi files you should get one of these programs if + you don't already have them: + + Playmidi: http://playmidi.openprojects.net + + AWEMidi Player (drvmidi) Included in the previously mentioned AWE + snapshot. + + You will probably have to pass the "-e" switch to playmidi to have it use + your midi device. drvmidi should work without switches. + + If something goes wrong please e-mail me. All comments and suggestions are + welcome. + + Yaroslav Rosomakho (alons55@dialup.ptt.ru) + http://www.yar.opennet.ru + +Last Updated: Feb 3 2001 diff --git a/trunk/Documentation/sound/oss/CMI8338 b/trunk/Documentation/sound/oss/CMI8338 new file mode 100644 index 000000000000..387d058c3f95 --- /dev/null +++ b/trunk/Documentation/sound/oss/CMI8338 @@ -0,0 +1,85 @@ +Audio driver for CM8338/CM8738 chips by Chen-Li Tien + + +HARDWARE SUPPORTED +================================================================================ +C-Media CMI8338 +C-Media CMI8738 +On-board C-Media chips + + +STEPS TO BUILD DRIVER +================================================================================ + + 1. Backup the Config.in and Makefile in the sound driver directory + (/usr/src/linux/driver/sound). + The Configure.help provide help when you config driver in step + 4, please backup the original one (/usr/src/linux/Document) and + copy this file. + The cmpci is document for the driver in detail, please copy it + to /usr/src/linux/Document/sound so you can refer it. Backup if + there is already one. + + 2. Extract the tar file by 'tar xvzf cmpci-xx.tar.gz' in the above + directory. + + 3. Change directory to /usr/src/linux + + 4. Config cm8338 driver by 'make menuconfig', 'make config' or + 'make xconfig' command. + + 5. Please select Sound Card (CONFIG_SOUND=m) support and CMPCI + driver (CONFIG_SOUND_CMPCI=m) as modules. Resident mode not tested. + For driver option, please refer 'DRIVER PARAMETER' + + 6. Compile the kernel if necessary. + + 7. Compile the modules by 'make modules'. + + 8. Install the modules by 'make modules_install' + + +INSTALL DRIVER +================================================================================ + + 1. Before first time to run the driver, create module dependency by + 'depmod -a' + + 2. To install the driver manually, enter 'modprobe cmpci'. + + 3. Driver installation for various distributions: + + a. Slackware 4.0 + Add the 'modprobe cmpci' command in your /etc/rc.d/rc.modules + file.so you can start the driver automatically each time booting. + + b. Caldera OpenLinux 2.2 + Use LISA to load the cmpci module. + + c. RedHat 6.0 and S.u.S.E. 6.1 + Add following command in /etc/conf.modules: + + alias sound cmpci + + also visit http://www.cmedia.com.tw for installation instruction. + +DRIVER PARAMETER +================================================================================ + + Some functions for the cm8738 can be configured in Kernel Configuration + or modules parameters. Set these parameters to 1 to enable. + + mpuio: I/O ports base for MPU-401, 0 if disabled. + fmio: I/O ports base for OPL-3, 0 if disabled. + spdif_inverse:Inverse the S/PDIF-in signal, this depends on your + CD-ROM or DVD-ROM. + spdif_loop: Enable S/PDIF loop, this route S/PDIF-in to S/PDIF-out + directly. + speakers: Number of speakers used. + use_line_as_rear:Enable this if you want to use line-in as + rear-out. + use_line_as_bass:Enable this if you want to use line-in as + bass-out. + joystick: Enable joystick. You will need to install Linux joystick + driver. + diff --git a/trunk/Documentation/sound/oss/INSTALL.awe b/trunk/Documentation/sound/oss/INSTALL.awe new file mode 100644 index 000000000000..310f42ca1e83 --- /dev/null +++ b/trunk/Documentation/sound/oss/INSTALL.awe @@ -0,0 +1,134 @@ +================================================================ + INSTALLATION OF AWE32 SOUND DRIVER FOR LINUX + Takashi Iwai +================================================================ + +---------------------------------------------------------------- +* Attention to SB-PnP Card Users + +If you're using PnP cards, the initialization of PnP is required +before loading this driver. You have now three options: + 1. Use isapnptools. + 2. Use in-kernel isapnp support. + 3. Initialize PnP on DOS/Windows, then boot linux by loadlin. +In this document, only the case 1 case is treated. + +---------------------------------------------------------------- +* Installation on Red Hat 5.0 Sound Driver + +Please use install-rh.sh under RedHat5.0 directory. +DO NOT USE install.sh below. +See INSTALL.RH for more details. + +---------------------------------------------------------------- +* Installation/Update by Shell Script + + 1. Become root + + % su + + 2. If you have never configured the kernel tree yet, run make config + once (to make dependencies and symlinks). + + # cd /usr/src/linux + # make xconfig + + 3. Run install.sh script + + # sh ./install.sh + + 4. Configure your kernel + + (for Linux 2.[01].x user) + # cd /usr/src/linux + # make xconfig (or make menuconfig) + + (for Linux 1.2.x user) + # cd /usr/src/linux + # make config + + Answer YES to both "lowlevel drivers" and "AWE32 wave synth" items + in Sound menu. ("lowlevel drivers" will appear only in 2.x + kernel.) + + 5. Make your kernel (and modules), and install them as usual. + + 5a. make kernel image + # make zImage + + 5b. make modules and install them + # make modules && make modules_install + + 5c. If you're using lilo, copy the kernel image and run lilo. + Otherwise, copy the kernel image to suitable directory or + media for your system. + + 6. Reboot the kernel if necessary. + - If you updated only the modules, you don't have to reboot + the system. Just remove the old sound modules here. + in + # rmmod sound.o (linux-2.0 or OSS/Free) + # rmmod awe_wave.o (linux-2.1) + + 7. If your AWE card is a PnP and not initialized yet, you'll have to + do it by isapnp tools. Otherwise, skip to 8. + + This section described only a brief explanation. For more + details, please see the AWE64-Mini-HOWTO or isapnp tools FAQ. + + 7a. If you have no isapnp.conf file, generate it by pnpdump. + Otherwise, skip to 7d. + # pnpdump > /etc/isapnp.conf + + 7b. Edit isapnp.conf file. Comment out the appropriate + lines containing desirable I/O ports, DMA and IRQs. + Don't forget to enable (ACT Y) line. + + 7c. Add two i/o ports (0xA20 and 0xE20) in WaveTable part. + ex) + (CONFIGURE CTL0048/58128 (LD 2 + # ANSI string -->WaveTable<-- + (IO 0 (BASE 0x0620)) + (IO 1 (BASE 0x0A20)) + (IO 2 (BASE 0x0E20)) + (ACT Y) + )) + + 7d. Load the config file. + CAUTION: This will reset all PnP cards! + + # isapnp /etc/isapnp.conf + + 8. Load the sound module (if you configured it as a module): + + for 2.0 kernel or OSS/Free monolithic module: + + # modprobe sound.o + + for 2.1 kernel: + + # modprobe sound + # insmod uart401 + # insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 + (These values depend on your settings.) + # insmod awe_wave + (Be sure to load awe_wave after sb!) + + See Documentation/sound/oss/AWE32 for + more details. + + 9. (only for obsolete systems) If you don't have /dev/sequencer + device file, make it according to Readme.linux file on + /usr/src/linux/drivers/sound. (Run a shell script included in + that file). <-- This file no longer exists in the recent kernels! + + 10. OK, load your own soundfont file, and enjoy MIDI! + + % sfxload synthgm.sbk + % drvmidi foo.mid + + 11. For more advanced use (eg. dynamic loading, virtual bank and + etc.), please read the awedrv FAQ or the instructions in awesfx + and awemidi packages. + +Good luck! diff --git a/trunk/Documentation/sound/oss/MAD16 b/trunk/Documentation/sound/oss/MAD16 new file mode 100644 index 000000000000..865dbd848742 --- /dev/null +++ b/trunk/Documentation/sound/oss/MAD16 @@ -0,0 +1,56 @@ +(This recipe has been edited to update the configuration symbols, + and change over to modprobe.conf for 2.6) + +From: Shaw Carruthers + +I have been using mad16 sound for some time now with no problems, current +kernel 2.1.89 + +lsmod shows: + +mad16 5176 0 +sb 22044 0 [mad16] +uart401 5576 0 [mad16 sb] +ad1848 14176 1 [mad16] +sound 61928 0 [mad16 sb uart401 ad1848] + +.config has: + +CONFIG_SOUND=m +CONFIG_SOUND_ADLIB=m +CONFIG_SOUND_MAD16=m +CONFIG_SOUND_YM3812=m + +modprobe.conf has: + +alias char-major-14-* mad16 +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 + + +To get the built in mixer to work this needs to be: + +options adlib_card io=0x388 # FM synthesizer +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 + +The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is + +------------------------------------------------------------------------ +The mad16 module in addition supports the following options: + +option: meaning: default: +joystick=0,1 disabled, enabled disabled +cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled + 0x06,0x08,0x0a Mitsumi, Panasonic, + Secondary IDE, Primary IDE +cdport=0x340,0x320, 0x340 + 0x330,0x360 +cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled +cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE +cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic +opl4=0,1 OPL3, OPL4 OPL3 + +for more details see linux/drivers/sound/mad16.c + +Rui Sousa diff --git a/trunk/Documentation/sound/oss/Maestro b/trunk/Documentation/sound/oss/Maestro new file mode 100644 index 000000000000..4a80eb3f8e00 --- /dev/null +++ b/trunk/Documentation/sound/oss/Maestro @@ -0,0 +1,123 @@ + An OSS/Lite Driver for the ESS Maestro family of sound cards + + Zach Brown, December 1999 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + http://www.zabbo.net/maestro/ + +I will try and maintain the most recent stable version of the driver +in both the stable and development kernel lines. + +ESS Maestro Chip Family +----------------------- + +There are 3 main variants of the ESS Maestro PCI sound chip. The first +is the Maestro 1. It was originally produced by Platform Tech as the +'AGOGO'. It can be recognized by Platform Tech's PCI ID 0x1285 with +0x0100 as the device ID. It was put on some sound boards and a few laptops. +ESS bought the design and cleaned it up as the Maestro 2. This starts +their marking with the ESS vendor ID 0x125D and the 'year' device IDs. +The Maestro 2 claims 0x1968 while the Maestro 2e has 0x1978. + +The various families of Maestro are mostly identical as far as this +driver is concerned. It doesn't touch the DSP parts that differ (though +it could for FM synthesis). + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves almost as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, and mmap()ing for playback behaves. Capture/recording +is limited due to oddities with the Maestro hardware. One can only +record in 16bit stereo. For recording the maestro uses non interleaved +stereo buffers so that mmap()ing the incoming data does not result in +a ring buffer of LRLR data. mmap()ing of the read buffers is therefore +disallowed until this can be cleaned up. + +/dev/mixer is an interface to the AC'97 codec on the Maestro. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. + +The driver doesn't support MIDI or FM playback at the moment. Typically +the Maestro is wired to an MPU MIDI chip, but some hardware implementations +don't. We need to assemble a white list of hardware implementations that +have MIDI wired properly before we can claim to support it safely. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound maestro' +should also be added to your module configs (typically /etc/conf.modules) +if you're using modular OSS/Lite sound and want to default to using a +maestro chip. + +As this is a PCI device, the module does not need to be informed of +any IO or IRQ resources it should use, it devines these from the +system. Sometimes, on sucky PCs, the BIOS fails to allocated resources +for the maestro. This will result in a message like: + maestro: PCI subsystem reports IRQ 0, this might not be correct. +from the kernel. Should this happen the sound chip most likely will +not operate correctly. To solve this one has to dig through their BIOS +(typically entered by hitting a hot key at boot time) and figure out +what magic needs to happen so that the BIOS will reward the maestro with +an IRQ. This operation is incredibly system specific, so you're on your +own. Sometimes the magic lies in 'PNP Capable Operating System' settings. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +The other, more interesting option, is 'dsps_order'. Typically at +install time the driver will only register one available /dev/dsp device +for its use. The 'dsps_order' module parameter allows for more devices +to be allocated, as a power of two. Up to 4 devices can be registered +( dsps_order=2 ). These devices act as fully distinct units and use +separate channels in the maestro. + +Power Management +---------------- + +As of version 0.14, this driver has a minimal understanding of PCI +Power Management. If it finds a valid power management capability +on the PCI device it will attempt to use the power management +functions of the maestro. It will only do this on Maestro 2Es and +only on machines that are known to function well. You can +force the use of power management by setting the 'use_pm' module +option to 1, or can disable it entirely by setting it to 0. + +When using power management, the driver does a few things +differently. It will keep the chip in a lower power mode +when the module is inserted but /dev/dsp is not open. This +allows the mixer to function but turns off the clocks +on other parts of the chip. When /dev/dsp is opened the chip +is brought into full power mode, and brought back down +when it is closed. It also powers down the chip entirely +when the module is removed or the machine is shutdown. This +can have nonobvious consequences. CD audio may not work +after a power managing driver is removed. Also, software that +doesn't understand power management may not be able to talk +to the powered down chip until the machine goes through a hard +reboot to bring it back. + +.. more details .. +------------------ + +drivers/sound/maestro.c contains comments that hopefully explain +the maestro implementation. diff --git a/trunk/Documentation/sound/oss/Maestro3 b/trunk/Documentation/sound/oss/Maestro3 new file mode 100644 index 000000000000..a113718e8034 --- /dev/null +++ b/trunk/Documentation/sound/oss/Maestro3 @@ -0,0 +1,92 @@ + An OSS/Lite Driver for the ESS Maestro3 family of sound chips + + Zach Brown, January 2001 + +Driver Status and Availability +------------------------------ + +The most recent version of this driver will hopefully always be available at + http://www.zabbo.net/maestro3/ + +I will try and maintain the most recent stable version of the driver +in both the stable and development kernel lines. + +Historically I've sucked pretty hard at actually doing that, however. + +ESS Maestro3 Chip Family +----------------------- + +The 'Maestro3' is much like the Maestro2 chip. The noted improvement +is the removal of the silicon in the '2' that did PCM mixing. All that +work is now done through a custom DSP called the ASSP, the Asynchronus +Specific Signal Processor. + +The 'Allegro' is a baby version of the Maestro3. I'm not entirely clear +on the extent of the differences, but the driver supports them both :) + +The 'Allegro' shows up as PCI ID 0x1988 and the Maestro3 as 0x1998, +both under ESS's vendor ID of 0x125D. The Maestro3 can also show up as +0x199a when hardware strapping is used. + +The chip can also act as a multi function device. The modem IDs follow +the audio multimedia device IDs. (so the modem part of an Allegro shows +up as 0x1989) + +Driver OSS Behavior +-------------------- + +This OSS driver exports /dev/mixer and /dev/dsp to applications, which +mostly adhere to the OSS spec. This driver doesn't register itself +with /dev/sndstat, so don't expect information to appear there. + +The /dev/dsp device exported behaves as expected. Playback is +supported in all the various lovely formats. 8/16bit stereo/mono from +8khz to 48khz, with both read()/write(), and mmap(). + +/dev/mixer is an interface to the AC'97 codec on the Maestro3. It is +worth noting that there are a variety of AC'97s that can be wired to +the Maestro3. Which is used is entirely up to the hardware implementor. +This should only be visible to the user by the presence, or lack, of +'Bass' and 'Treble' sliders in the mixer. Not all AC'97s have them. +The Allegro has an onchip AC'97. + +The driver doesn't support MIDI or FM playback at the moment. + +Compiling and Installing +------------------------ + +With the drivers inclusion into the kernel, compiling and installing +is the same as most OSS/Lite modular sound drivers. Compilation +of the driver is enabled through the CONFIG_SOUND_MAESTRO3 variable +in the config system. + +It may be modular or statically linked. If it is modular it should be +installed with the rest of the modules for the kernel on the system. +Typically this will be in /lib/modules/ somewhere. 'alias sound-slot-0 +maestro3' should also be added to your module configs (typically +/etc/modprobe.conf) if you're using modular OSS/Lite sound and want to +default to using a maestro3 chip. + +There are very few options to the driver. One is 'debug' which will +tell the driver to print minimal debugging information as it runs. This +can be collected with 'dmesg' or through the klogd daemon. + +One is 'external_amp', which tells the driver to attempt to enable +an external amplifier. This defaults to '1', you can tell the driver +not to bother enabling such an amplifier by setting it to '0'. + +And the last is 'gpio_pin', which tells the driver which GPIO pin number +the external amp uses (0-15), The Allegro uses 8 by default, all others 1. +If everything loads correctly and seems to be working but you get no sound, +try tweaking this value. + +Systems known to need a different value + Panasonic ToughBook CF-72: gpio_pin=13 + +Power Management +---------------- + +This driver has a minimal understanding of PCI Power Management. It will +try and power down the chip when the system is suspended, and power +it up with it is resumed. It will also try and power down the chip +when the machine is shut down. diff --git a/trunk/Documentation/sound/oss/NEWS b/trunk/Documentation/sound/oss/NEWS new file mode 100644 index 000000000000..a81e0ef72ae9 --- /dev/null +++ b/trunk/Documentation/sound/oss/NEWS @@ -0,0 +1,42 @@ +Linux 2.4 Sound Changes +2000-September-25 +Christoph Hellwig, + + + +=== isapnp support + +The Linux 2.4 Kernel does have reliable in-kernel isapnp support. +Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically +detecting and configuring isapnp devices. +If you have a not yet supported isapnp soundcard, mail me the content +of '/proc/isapnp' on your system and some information about your card +and its driver(s) so I can try to get isapnp working for it. + + + +=== soundcard resources on kernel commandline + +Before Linux 2.4 you had to specify the resources for sounddrivers +statically linked into the kernel at compile time +(in make config/menuconfig/xconfig). In Linux 2.4 the resources are +now specified at the boot-time kernel commandline (e.g. the lilo +'append=' line or everything that's after the kernel name in grub). +Read the Configure.help entry for your card for the parameters. + + +=== softoss is gone + +In Linux 2.4 the softoss in-kernel software synthesizer is no more aviable. +Use a user space software synthesizer like timidity instead. + + + +=== /dev/sndstat and /proc/sound are gone + +In older Linux versions those files exported some information about the +OSS/Free configuration to userspace. In Linux 2.3 they were removed because +they did not support the growing number of pci soundcards and there were +some general problems with this interface. + + diff --git a/trunk/Documentation/sound/oss/OPL3-SA b/trunk/Documentation/sound/oss/OPL3-SA new file mode 100644 index 000000000000..66a91835d918 --- /dev/null +++ b/trunk/Documentation/sound/oss/OPL3-SA @@ -0,0 +1,52 @@ +OPL3-SA1 sound driver (opl3sa.o) + +--- +Note: This howto only describes how to setup the OPL3-SA1 chip; this info +does not apply to the SA2, SA3, or SA4. +--- + +The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and +it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 +and OPL3 FM Synth capabilities. + +You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or +CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. + +You'll need to know all of the relevant info (irq, dma, and io port) for the +chip's WSS mode, since that is the mode the kernel sound driver uses, and of +course you'll also need to know about where the MPU401 and OPL3 ports and +IRQs are if you want to use those. + +Here's the skinny on how to load it as a module: + + modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 + +Module options in detail: + + io: This is the WSS's port base. + irq: This is the WSS's IRQ. + dma: This is the WSS's DMA line. In my BIOS setup screen this was + listed as "WSS Play DMA" + dma2: This is the WSS's secondary DMA line. My BIOS calls it the + "WSS capture DMA" + + mpu_io: This is the MPU401's port base. + mpu_irq: This is the MPU401's IRQ. + +If you'd like to use the OPL3 FM Synthesizer, make sure you enable +CONFIG_SOUND_YM3812 (in 'make config'). That'll build the opl3.o module. + +Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. + +You can also use the SoftOSS software synthesizer instead of the builtin OPL3. +Here's how: + +Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. + +If you said yes, the software synth is available once you boot your new +kernel. + +If you chose to build it as a module, just insmod the resulting softoss2.o + +Questions? Comments? + diff --git a/trunk/Documentation/sound/oss/README.awe b/trunk/Documentation/sound/oss/README.awe new file mode 100644 index 000000000000..80054cd8fcde --- /dev/null +++ b/trunk/Documentation/sound/oss/README.awe @@ -0,0 +1,218 @@ +================================================================ + AWE32 Sound Driver for Linux / FreeBSD + version 0.4.3; Nov. 1, 1998 + + Takashi Iwai +================================================================ + +* GENERAL NOTES + +This is a sound driver extension for SoundBlaster AWE32 and other +compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable +the wave synth operations. The driver is provided for Linux 1.2.x +and 2.[012].x kernels, as well as FreeBSD, on Intel x86 and DEC +Alpha systems. + +This driver was written by Takashi Iwai , +and provided "as is". The original source (awedrv-0.4.3.tar.gz) and +binary packages are available on the following URL: + http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ +Note that since the author is apart from this web site, the update is +not frequent now. + + +* NOTE TO LINUX USERS + +To enable this driver on linux-2.[01].x kernels, you need turn on +"AWE32 synth" options in sound menu when configure your linux kernel +and modules. The precise installation procedure is described in the +AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. + +If you're using PnP cards, the card must be initialized before loading +the sound driver. There're several options to do this: + - Initialize the card via ISA PnP tools, and load the sound module. + - Initialize the card on DOS, and load linux by loadlin.exe + - Use PnP kernel driver (for Linux-2.x.x) +The detailed instruction for the solution using isapnp tools is found +in many documents like above. A brief instruction is also included in +the installation document of this package. +For PnP driver project, please refer to the following URL: + http://www-jcr.lmh.ox.ac.uk/~pnp/ + + +* USING THE DRIVER + +The awedrv has several different playing modes to realize easy channel +allocation for MIDI songs. To hear the exact sound quality, you need +to obtain the extended sequencer program, drvmidi or playmidi-2.5. + +For playing MIDI files, you *MUST* load the soundfont file on the +driver previously by sfxload utility. Otherwise you'll here no sounds +at all! All the utilities and driver source packages are found in the +above URL. The sfxload program is included in the package +awesfx-0.4.3.tgz. Binary packages are available there, too. See the +instruction in each package for installation. + +Loading a soundfont file is very simple. Just execute the command + + % sfxload synthgm.sbk + +Then, sfxload transfers the file "synthgm.sbk" to the driver. +Both SF1 and SF2 formats are accepted. + +Now you can hear midi musics by a midi player. + + % drvmidi foo.mid + +If you run MIDI player after MOD player, you need to load soundfont +files again, since MOD player programs clear the previous loaded +samples by their own data. + +If you have only 512kb on the sound card, I recommend to use dynamic +sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is +available in most midi files. + + % sfxload synthgm + % drvmidi -L 2mbgmgs foo.mid + +This makes a big difference (believe me)! For more details, please +refer to the FAQ list which is available on the URL above. + +The current chorus, reverb and equalizer status can be changed by +aweset utility program (included in awesfx package). Note that +some awedrv-native programs (like drvmidi and xmp) will change the +current settings by themselves. The aweset program is effective +only for other programs like playmidi. + +Enjoy. + + +* COMPILE FLAGS + +Compile conditions are defined in awe_config.h. + +[Compatibility Conditions] +The following flags are defined automatically when using installation +shell script. + +- AWE_MODULE_SUPPORT + indicates your Linux kernel supports module for each sound card + (in recent 2.1 or 2.2 kernels and unofficial patched 2.0 kernels + as distributed in the RH5.0 package). + This flag is automatically set when you're using 2.1.x kernels. + You can pass the base address and memory size via the following + module options, + io = base I/O port address (eg. 0x620) + memsize = DRAM size in kilobytes (eg. 512) + As default, AWE driver probes these values automatically. + + +[Hardware Conditions] +You DON'T have to define the following two values. +Define them only when the driver couldn't detect the card properly. + +- AWE_DEFAULT_BASE_ADDR (default: not defined) + specifies the base port address of your AWE32 card. + 0 means to autodetect the address. + +- AWE_DEFAULT_MEM_SIZE (default: not defined) + specifies the memory size of your AWE32 card in kilobytes. + -1 means to autodetect its size. + + +[Sample Table Size] +From ver.0.4.0, sample tables are allocated dynamically (except +Linux-1.2.x system), so you need NOT to touch these parameters. +Linux-1.2.x users may need to increase these values to appropriate size +if the sound card is equipped with more DRAM. + +- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS + + +[Other Conditions] + +- AWE_ALWAYS_INIT_FM (default: not defined) + indicates the AWE driver always initialize FM passthrough even + without DRAM on board. Emu8000 chip has a restriction for playing + samples on DRAM that at least two channels must be occupied as + passthrough channels. + +- AWE_DEBUG_ON (default: defined) + turns on debugging messages if defined. + +- AWE_HAS_GUS_COMPATIBILITY (default: defined) + Enables GUS compatibility mode if defined, reading GUS patches and + GUS control commands. Define this option to use GMOD or other + GUS module players. + +- CONFIG_AWE32_MIDIEMU (default: defined) + Adds a MIDI emulation device by Emu8000 wavetable. The emulation + device can be accessed as an external MIDI, and sends the MIDI + control codes directly. XG and GS sysex/NRPN are accepted. + No MIDI input is supported. + +- CONFIG_AWE32_MIXER (default: not defined) + Adds a mixer device for AWE32 bass/treble equalizer control. + You can access this device using /dev/mixer?? (usually mixer01). + +- AWE_USE_NEW_VOLUME_CALC (default: defined) + Use the new method to calculate the volume change as compatible + with DOS/Win drivers. This option can be toggled via aweset + program, or drvmidi player. + +- AWE_CHECK_VTARGET (default: defined) + Check the current volume target value when searching for an + empty channel to allocate a new voice. This is experimentally + implemented in this version. (probably, this option doesn't + affect the sound quality severely...) + +- AWE_ALLOW_SAMPLE_SHARING (default: defined) + Allow sample sharing for differently loaded patches. + This function is available only together with awesfx-0.4.3p3. + Note that this is still an experimental option. + +- DEF_FM_CHORUS_DEPTH (default: 0x10) + The default strength to be sent to the chorus effect engine. + From 0 to 0xff. Larger numbers may often cause weird sounds. + +- DEF_FM_REVERB_DEPTH (default: 0x10) + The default strength to be sent to the reverb effect engine. + From 0 to 0xff. Larger numbers may often cause weird sounds. + + +* ACKNOWLEDGMENTS + +Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice +on programming of AWE32. Much code is brought from his AWE32-native +MOD player, ALMP. +The port of awedrv to FreeBSD is done by Randall Hopper +(rhh@ct.picker.com). +The new volume calculation routine was derived from Mark Weaver's +ADIP compatible routines. +I also thank linux-awe-ml members for their efforts +to reboot their system many times :-) + + +* TODO'S + +- Complete DOS/Win compatibility +- DSP-like output + + +* COPYRIGHT + +Copyright (C) 1996-1998 Takashi Iwai + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/trunk/Documentation/sound/oss/Wavefront b/trunk/Documentation/sound/oss/Wavefront new file mode 100644 index 000000000000..16f57ea43052 --- /dev/null +++ b/trunk/Documentation/sound/oss/Wavefront @@ -0,0 +1,339 @@ + An OSS/Free Driver for WaveFront soundcards + (Turtle Beach Maui, Tropez, Tropez Plus) + + Paul Barton-Davis, July 1998 + + VERSION 0.2.5 + +Driver Status +------------- + +Requires: Kernel 2.1.106 or later (the driver is included with kernels +2.1.109 and above) + +As of 7/22/1998, this driver is currently in *BETA* state. This means +that it compiles and runs, and that I use it on my system (Linux +2.1.106) with some reasonably demanding applications and uses. I +believe the code is approaching an initial "finished" state that +provides bug-free support for the Tropez Plus. + +Please note that to date, the driver has ONLY been tested on a Tropez +Plus. I would very much like to hear (and help out) people with Tropez +and Maui cards, since I think the driver can support those cards as +well. + +Finally, the driver has not been tested (or even compiled) as a static +(non-modular) part of the kernel. Alan Cox's good work in modularizing +OSS/Free for Linux makes this rather unnecessary. + +Some Questions +-------------- + +********************************************************************** +0) What does this driver do that the maui driver did not ? +********************************************************************** + +* can fully initialize a WaveFront card from cold boot - no DOS + utilities needed +* working patch/sample/program loading and unloading (the maui + driver didn't document how to make this work, and assumed + user-level preparation of the patch data for writing + to the board. ick.) +* full user-level access to all WaveFront commands +* for the Tropez Plus, (primitive) control of the YSS225 FX processor +* Virtual MIDI mode supported - 2 MIDI devices accessible via the + WaveFront's MPU401/UART emulation. One + accesses the WaveFront synth, the other accesses the + external MIDI connector. Full MIDI read/write semantics + for both devices. +* OSS-compliant /dev/sequencer interface for the WaveFront synth, + including native and GUS-format patch downloading. +* semi-intelligent patch management (prototypical at this point) + +********************************************************************** +1) What to do about MIDI interfaces ? +********************************************************************** + +The Tropez Plus (and perhaps other WF cards) can in theory support up +to 2 physical MIDI interfaces. One of these is connected to the +ICS2115 chip (the WaveFront synth itself) and is controlled by +MPU/UART-401 emulation code running as part of the WaveFront OS. The +other is controlled by the CS4232 chip present on the board. However, +physical access to the CS4232 connector is difficult, and it is +unlikely (though not impossible) that you will want to use it. + +An older version of this driver introduced an additional kernel config +variable which controlled whether or not the CS4232 MIDI interface was +configured. Because of Alan Cox's work on modularizing the sound +drivers, and now backporting them to 2.0.34 kernels, there seems to be +little reason to support "static" configuration variables, and so this +has been abandoned in favor of *only* module parameters. Specifying +"mpuio" and "mpuirq" for the cs4232 parameter will result in the +CS4232 MIDI interface being configured; leaving them unspecified will +leave it unconfigured (and thus unusable). + +BTW, I have heard from one Tropez+ user that the CS4232 interface is +more reliable than the ICS2115 one. I have had no problems with the +latter, and I don't have the right cable to test the former one +out. Reports welcome. + +********************************************************************** +2) Why does line XXX of the code look like this .... ? +********************************************************************** + +Either because it's not finished yet, or because you're a better coder +than I am, or because you don't understand some aspect of how the card +or the code works. + +I absolutely welcome comments, criticisms and suggestions about the +design and implementation of the driver. + +********************************************************************** +3) What files are included ? +********************************************************************** + + drivers/sound/README.wavefront -- this file + + drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers + needed to make the rest of this work + DO NOT USE IF YOU'VE APPLIED THEM + BEFORE, OR HAVE 2.1.109 OR ABOVE + + drivers/sound/wavfront.c -- the driver + drivers/sound/ys225.h -- data declarations for FX config + drivers/sound/ys225.c -- data definitions for FX config + drivers/sound/wf_midi.c -- the "uart401" driver + to support virtual MIDI mode. + include/wavefront.h -- the header file + Documentation/sound/oss/Tropez+ -- short docs on configuration + +********************************************************************** +4) How do I compile/install/use it ? +********************************************************************** + +PART ONE: install the source code into your sound driver directory + + cd + tar -zxvf + +PART TWO: apply the patches + + DO THIS ONLY IF YOU HAVE A KERNEL VERSION BELOW 2.1.109 + AND HAVE NOT ALREADY INSTALLED THE PATCH(ES). + + cd drivers/sound + patch < wavefront.patch + +PART THREE: configure your kernel + + cd + make xconfig (or whichever config option you use) + + - choose YES for Sound Support + - choose MODULE (M) for OSS Sound Modules + - choose MODULE(M) to YM3812/OPL3 support + - choose MODULE(M) for WaveFront support + - choose MODULE(M) for CS4232 support + + - choose "N" for everything else (unless you have other + soundcards you want support for) + + + make boot + . + . + . + + make modules + . + . + . + make modules_install + +Here's my autoconf.h SOUND section: + +/* + * Sound + */ +#define CONFIG_SOUND 1 +#undef CONFIG_SOUND_OSS +#define CONFIG_SOUND_OSS_MODULE 1 +#undef CONFIG_SOUND_PAS +#undef CONFIG_SOUND_SB +#undef CONFIG_SOUND_ADLIB +#undef CONFIG_SOUND_GUS +#undef CONFIG_SOUND_MPU401 +#undef CONFIG_SOUND_PSS +#undef CONFIG_SOUND_MSS +#undef CONFIG_SOUND_SSCAPE +#undef CONFIG_SOUND_TRIX +#undef CONFIG_SOUND_MAD16 +#undef CONFIG_SOUND_WAVEFRONT +#define CONFIG_SOUND_WAVEFRONT_MODULE 1 +#undef CONFIG_SOUND_CS4232 +#define CONFIG_SOUND_CS4232_MODULE 1 +#undef CONFIG_SOUND_MAUI +#undef CONFIG_SOUND_SGALAXY +#undef CONFIG_SOUND_OPL3SA1 +#undef CONFIG_SOUND_SOFTOSS +#undef CONFIG_SOUND_YM3812 +#define CONFIG_SOUND_YM3812_MODULE 1 +#undef CONFIG_SOUND_VMIDI +#undef CONFIG_SOUND_UART6850 +/* + * Additional low level sound drivers + */ +#undef CONFIG_LOWLEVEL_SOUND + +************************************************************ +6) How do I configure my card ? +************************************************************ + +You need to edit /etc/modprobe.conf. Here's mine (edited to show the +relevant details): + + # Sound system + alias char-major-14-* wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + install wavefront /sbin/modprobe cs4232 && /sbin/modprobe -i wavefront && /sbin/modprobe opl3 + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the opl3 module if you don't + want to use the OPL/[34] FM synth on the soundcard + + the opl3 io parameter is conventionally not adjustable. + In theory, any not-in-use IO port address would work, but + just use 0x388 and stick with the crowd. + +********************************************************************** +7) What about firmware ? +********************************************************************** + +Turtle Beach have not given me permission to distribute their firmware +for the ICS2115. However, if you have a WaveFront card, then you +almost certainly have the firmware, and if not, its freely available +on their website, at: + + http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus + +The file is called WFOS2001.MOT (for the Tropez+). + +This driver, however, doesn't use the pure firmware as distributed, +but instead relies on a somewhat processed form of it. You can +generate this very easily. Following an idea from Andrew Veliath's +Pinnacle driver, the following flex program will generate the +processed version: + +---- cut here ------------------------- +%option main +%% +^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); +<> { fputc ('\0', stdout); return; } +\n {} +. {} +---- cut here ------------------------- + +To use it, put the above in file (say, ws.l) compile it like this: + + shell> flex -ows.c ws.l + shell> cc -o ws ws.c + +and then use it like this: + + ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os + +If you put it somewhere else, you'll always have to use the wf_ospath +module parameter (see below) or alter the source code. + +********************************************************************** +7) How do I get it working ? +********************************************************************** + +Optionally, you can reboot with the "new" kernel (even though the only +changes have really been made to a module). + +Then, as root do: + + modprobe wavefront + +You should get something like this in /var/log/messages: + + WaveFront: firmware 1.20 already loaded. + +or + + WaveFront: no response to firmware probe, assume raw. + +then: + + WaveFront: waiting for memory configuration ... + WaveFront: hardware version 1.64 + WaveFront: available DRAM 8191k + WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty + WaveFront: 128 programs slots in use + WaveFront: 256 patch slots filled, 142 in use + +The whole process takes about 16 seconds, the longest waits being +after reporting the hardware version (during the firmware download), +and after reporting program status (during patch status inquiry). Its +shorter (about 10 secs) if the firmware is already loaded (i.e. only +warm reboots since the last firmware load). + +The "available DRAM" line will vary depending on how much added RAM +your card has. Mine has 8MB. + +To check basically functionality, use play(1) or splay(1) to send a +.WAV or other audio file through the audio portion. Then use playmidi +to play a General MIDI file. Try the "-D 0" to hear the +difference between sending MIDI to the WaveFront and using the OPL/3, +which is the default (I think ...). If you have an external synth(s) +hooked to the soundcard, you can use "-e" to route to the +external synth(s) (in theory, -D 1 should work as well, but I think +there is a bug in playmidi which prevents this from doing what it +should). + +********************************************************************** +8) What are the module parameters ? +********************************************************************** + +Its best to read wavefront.c for this, but here is a summary: + +integers: + wf_raw - if set, ignore apparent presence of firmware + loaded onto the ICS2115, reset the whole + board, and initialize it from scratch. (default = 0) + + fx_raw - if set, always initialize the YSS225 processor + on the Tropez plus. (default = 1) + + < The next 4 are basically for kernel hackers to allow + tweaking the driver for testing purposes. > + + wait_usecs - loop timer used when waiting for + status conditions on the board. + The default is 150. + + debug_default - debugging flags. See sound/wavefront.h + for WF_DEBUG_* values. Default is zero. + Setting this allows you to debug the + driver during module installation. +strings: + ospath - path to get to the pre-processed OS firmware. + (default: /etc/sound/wavefront.os) + +********************************************************************** +9) Who should I contact if I have problems? +********************************************************************** + +Just me: Paul Barton-Davis + + diff --git a/trunk/Documentation/sound/oss/es1370 b/trunk/Documentation/sound/oss/es1370 new file mode 100644 index 000000000000..7b38b1a096a3 --- /dev/null +++ b/trunk/Documentation/sound/oss/es1370 @@ -0,0 +1,70 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. +The second one goes to the mixer "synth" setting and supports +only a limited set of sampling rates (44100, 22050, 11025, 5512). +By setting lineout to 1 on the driver command line +(eg. insmod es1370 lineout=1) it is even possible on some +cards to convert the LINEIN jack into a second LINEOUT jack, thus +making it possible to output four independent audio channels! + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff --git a/trunk/Documentation/sound/oss/rme96xx b/trunk/Documentation/sound/oss/rme96xx new file mode 100644 index 000000000000..87d7b7b65fa1 --- /dev/null +++ b/trunk/Documentation/sound/oss/rme96xx @@ -0,0 +1,767 @@ +Beta release of the rme96xx (driver for RME 96XX cards like the +"Hammerfall" and the "Hammerfall light") + +Important: The driver module has to be installed on a freshly rebooted system, +otherwise the driver might not be able to acquire its buffers. + +features: + + - OSS programming interface (i.e. runs with standard OSS soundsoftware) + - OSS/Multichannel interface (OSS multichannel is done by just aquiring + more than 2 channels). The driver does not use more than one device + ( yet .. this feature may be implemented later ) + - more than one RME card supported + +The driver uses a specific multichannel interface, which I will document +when the driver gets stable. (take a look at the defines in rme96xx.h, +which adds blocked multichannel formats i.e instead of +lrlrlrlr --> llllrrrr etc. + +Use the "rmectrl" programm to look at the status of the card .. +or use xrmectrl, a GUI interface for the ctrl program. + +What you can do with the rmectrl program is to set the stereo device for +OSS emulation (e.g. if you use SPDIF out). + +You do: + +./ctrl offset 24 24 + +which makes the stereo device use channels 25 and 26. + +Guenter Geiger + +copy the first part of the attached source code into rmectrl.c +and the second part into xrmectrl (or get the program from +http://gige.xdv.org/pages/soft/pages/rme) + +to compile: gcc -o rmectrl rmectrl.c +------------------------------ snip ------------------------------------ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rme96xx.h" + +/* + remctrl.c + (C) 2000 Guenter Geiger + HP20020201 - Heiko Purnhagen +*/ + +/* # define DEVICE_NAME "/dev/mixer" */ +# define DEVICE_NAME "/dev/mixer1" + + +void usage(void) +{ + fprintf(stderr,"usage: rmectrl [/dev/mixer] [command [options]]\n\n"); + fprintf(stderr,"where command is one of:\n"); + fprintf(stderr," help show this help\n"); + fprintf(stderr," status show status bits\n"); + fprintf(stderr," control show control bits\n"); + fprintf(stderr," mix show mixer/offset status\n"); + fprintf(stderr," master set sync master\n"); + fprintf(stderr," pro set spdif out pro\n"); + fprintf(stderr," emphasis set spdif out emphasis\n"); + fprintf(stderr," dolby set spdif out no audio\n"); + fprintf(stderr," optout set spdif out optical\n"); + fprintf(stderr," wordclock set sync wordclock\n"); + fprintf(stderr," spdifin set spdif in (0=optical,1=coax,2=intern)\n"); + fprintf(stderr," syncref set sync source (0=ADAT1,1=ADAT2,2=ADAT3,3=SPDIF)\n"); + fprintf(stderr," adat1cd set ADAT1 on internal CD\n"); + fprintf(stderr," offset set dev (0..3) offset (0..25)\n"); + exit(-1); +} + + +int main(int argc, char* argv[]) +{ + int cards; + int ret; + int i; + double ft; + int fd, fdwr; + int param,orig; + rme_status_t stat; + rme_ctrl_t ctrl; + char *device; + int argidx; + + if (argc < 2) + usage(); + + if (*argv[1]=='/') { + device = argv[1]; + argidx = 2; + } + else { + device = DEVICE_NAME; + argidx = 1; + } + + fprintf(stdout,"mixer device %s\n",device); + if ((fd = open(device,O_RDONLY)) < 0) { + fprintf(stdout,"opening device failed\n"); + exit(-1); + } + + if ((fdwr = open(device,O_WRONLY)) < 0) { + fprintf(stdout,"opening device failed\n"); + exit(-1); + } + + if (argc < argidx+1) + usage(); + + if (!strcmp(argv[argidx],"help")) + usage(); + if (!strcmp(argv[argidx],"-h")) + usage(); + if (!strcmp(argv[argidx],"--help")) + usage(); + + if (!strcmp(argv[argidx],"status")) { + ioctl(fd,SOUND_MIXER_PRIVATE2,&stat); + fprintf(stdout,"stat.irq %d\n",stat.irq); + fprintf(stdout,"stat.lockmask %d\n",stat.lockmask); + fprintf(stdout,"stat.sr48 %d\n",stat.sr48); + fprintf(stdout,"stat.wclock %d\n",stat.wclock); + fprintf(stdout,"stat.bufpoint %d\n",stat.bufpoint); + fprintf(stdout,"stat.syncmask %d\n",stat.syncmask); + fprintf(stdout,"stat.doublespeed %d\n",stat.doublespeed); + fprintf(stdout,"stat.tc_busy %d\n",stat.tc_busy); + fprintf(stdout,"stat.tc_out %d\n",stat.tc_out); + fprintf(stdout,"stat.crystalrate %d (0=64k 3=96k 4=88.2k 5=48k 6=44.1k 7=32k)\n",stat.crystalrate); + fprintf(stdout,"stat.spdif_error %d\n",stat.spdif_error); + fprintf(stdout,"stat.bufid %d\n",stat.bufid); + fprintf(stdout,"stat.tc_valid %d\n",stat.tc_valid); + exit (0); + } + + if (!strcmp(argv[argidx],"control")) { + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + fprintf(stdout,"ctrl.start %d\n",ctrl.start); + fprintf(stdout,"ctrl.latency %d (0=64 .. 7=8192)\n",ctrl.latency); + fprintf(stdout,"ctrl.master %d\n",ctrl.master); + fprintf(stdout,"ctrl.ie %d\n",ctrl.ie); + fprintf(stdout,"ctrl.sr48 %d\n",ctrl.sr48); + fprintf(stdout,"ctrl.spare %d\n",ctrl.spare); + fprintf(stdout,"ctrl.doublespeed %d\n",ctrl.doublespeed); + fprintf(stdout,"ctrl.pro %d\n",ctrl.pro); + fprintf(stdout,"ctrl.emphasis %d\n",ctrl.emphasis); + fprintf(stdout,"ctrl.dolby %d\n",ctrl.dolby); + fprintf(stdout,"ctrl.opt_out %d\n",ctrl.opt_out); + fprintf(stdout,"ctrl.wordclock %d\n",ctrl.wordclock); + fprintf(stdout,"ctrl.spdif_in %d (0=optical,1=coax,2=intern)\n",ctrl.spdif_in); + fprintf(stdout,"ctrl.sync_ref %d (0=ADAT1,1=ADAT2,2=ADAT3,3=SPDIF)\n",ctrl.sync_ref); + fprintf(stdout,"ctrl.spdif_reset %d\n",ctrl.spdif_reset); + fprintf(stdout,"ctrl.spdif_select %d\n",ctrl.spdif_select); + fprintf(stdout,"ctrl.spdif_clock %d\n",ctrl.spdif_clock); + fprintf(stdout,"ctrl.spdif_write %d\n",ctrl.spdif_write); + fprintf(stdout,"ctrl.adat1_cd %d\n",ctrl.adat1_cd); + exit (0); + } + + if (!strcmp(argv[argidx],"mix")) { + rme_mixer mix; + int i; + + for (i=0; i<4; i++) { + mix.devnr = i; + ioctl(fd,SOUND_MIXER_PRIVATE1,&mix); + if (mix.devnr == i) { + fprintf(stdout,"devnr %d\n",mix.devnr); + fprintf(stdout,"mix.i_offset %2d (0-25)\n",mix.i_offset); + fprintf(stdout,"mix.o_offset %2d (0-25)\n",mix.o_offset); + } + } + exit (0); + } + +/* the control flags */ + + if (argc < argidx+2) + usage(); + + if (!strcmp(argv[argidx],"master")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("master = %d\n",val); + ctrl.master = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"pro")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("pro = %d\n",val); + ctrl.pro = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"emphasis")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("emphasis = %d\n",val); + ctrl.emphasis = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"dolby")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("dolby = %d\n",val); + ctrl.dolby = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"optout")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("optout = %d\n",val); + ctrl.opt_out = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"wordclock")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("wordclock = %d\n",val); + ctrl.wordclock = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"spdifin")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("spdifin = %d\n",val); + ctrl.spdif_in = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"syncref")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("syncref = %d\n",val); + ctrl.sync_ref = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + + if (!strcmp(argv[argidx],"adat1cd")) { + int val = atoi(argv[argidx+1]); + ioctl(fd,SOUND_MIXER_PRIVATE3,&ctrl); + printf("adat1cd = %d\n",val); + ctrl.adat1_cd = val; + ioctl(fdwr,SOUND_MIXER_PRIVATE3,&ctrl); + exit (0); + } + +/* setting offset */ + + if (argc < argidx+4) + usage(); + + if (!strcmp(argv[argidx],"offset")) { + rme_mixer mix; + + mix.devnr = atoi(argv[argidx+1]); + + mix.i_offset = atoi(argv[argidx+2]); + mix.o_offset = atoi(argv[argidx+3]); + ioctl(fdwr,SOUND_MIXER_PRIVATE1,&mix); + fprintf(stdout,"devnr %d\n",mix.devnr); + fprintf(stdout,"mix.i_offset to %d\n",mix.i_offset); + fprintf(stdout,"mix.o_offset to %d\n",mix.o_offset); + exit (0); + } + + usage(); + exit (0); /* to avoid warning */ +} + + +---------------------------- -------------------------------- +#!/usr/bin/wish + +# xrmectrl +# (C) 2000 Guenter Geiger +# HP20020201 - Heiko Purnhagen + +#set defaults "-relief ridged" +set CTRLPROG "./rmectrl" +if {$argc} { + set CTRLPROG "$CTRLPROG $argv" +} +puts "CTRLPROG $CTRLPROG" + +frame .butts +button .butts.exit -text "Exit" -command "exit" -relief ridge +#button .butts.state -text "State" -command "get_all" + +pack .butts.exit -side left +pack .butts -side bottom + + +# +# STATUS +# + +frame .status + +# Sampling Rate + +frame .status.sr +label .status.sr.text -text "Sampling Rate" -justify left +radiobutton .status.sr.441 -selectcolor red -text "44.1 kHz" -width 10 -anchor nw -variable srate -value 44100 -font times +radiobutton .status.sr.480 -selectcolor red -text "48 kHz" -width 10 -anchor nw -variable srate -value 48000 -font times +radiobutton .status.sr.882 -selectcolor red -text "88.2 kHz" -width 10 -anchor nw -variable srate -value 88200 -font times +radiobutton .status.sr.960 -selectcolor red -text "96 kHz" -width 10 -anchor nw -variable srate -value 96000 -font times + +pack .status.sr.text .status.sr.441 .status.sr.480 .status.sr.882 .status.sr.960 -side top -padx 3 + +# Lock + +frame .status.lock +label .status.lock.text -text "Lock" -justify left +checkbutton .status.lock.adat1 -selectcolor red -text "ADAT1" -anchor nw -width 10 -variable adatlock1 -font times +checkbutton .status.lock.adat2 -selectcolor red -text "ADAT2" -anchor nw -width 10 -variable adatlock2 -font times +checkbutton .status.lock.adat3 -selectcolor red -text "ADAT3" -anchor nw -width 10 -variable adatlock3 -font times + +pack .status.lock.text .status.lock.adat1 .status.lock.adat2 .status.lock.adat3 -side top -padx 3 + +# Sync + +frame .status.sync +label .status.sync.text -text "Sync" -justify left +checkbutton .status.sync.adat1 -selectcolor red -text "ADAT1" -anchor nw -width 10 -variable adatsync1 -font times +checkbutton .status.sync.adat2 -selectcolor red -text "ADAT2" -anchor nw -width 10 -variable adatsync2 -font times +checkbutton .status.sync.adat3 -selectcolor red -text "ADAT3" -anchor nw -width 10 -variable adatsync3 -font times + +pack .status.sync.text .status.sync.adat1 .status.sync.adat2 .status.sync.adat3 -side top -padx 3 + +# Timecode + +frame .status.tc +label .status.tc.text -text "Timecode" -justify left +checkbutton .status.tc.busy -selectcolor red -text "busy" -anchor nw -width 10 -variable tcbusy -font times +checkbutton .status.tc.out -selectcolor red -text "out" -anchor nw -width 10 -variable tcout -font times +checkbutton .status.tc.valid -selectcolor red -text "valid" -anchor nw -width 10 -variable tcvalid -font times + +pack .status.tc.text .status.tc.busy .status.tc.out .status.tc.valid -side top -padx 3 + +# SPDIF In + +frame .status.spdif +label .status.spdif.text -text "SPDIF In" -justify left +label .status.spdif.sr -text "--.- kHz" -anchor n -width 10 -font times +checkbutton .status.spdif.error -selectcolor red -text "Input Lock" -anchor nw -width 10 -variable spdiferr -font times + +pack .status.spdif.text .status.spdif.sr .status.spdif.error -side top -padx 3 + +pack .status.sr .status.lock .status.sync .status.tc .status.spdif -side left -fill x -anchor n -expand 1 + + +# +# CONTROL +# + +proc setprof {} { + global CTRLPROG + global spprof + exec $CTRLPROG pro $spprof +} + +proc setemph {} { + global CTRLPROG + global spemph + exec $CTRLPROG emphasis $spemph +} + +proc setnoaud {} { + global CTRLPROG + global spnoaud + exec $CTRLPROG dolby $spnoaud +} + +proc setoptical {} { + global CTRLPROG + global spoptical + exec $CTRLPROG optout $spoptical +} + +proc setspdifin {} { + global CTRLPROG + global spdifin + exec $CTRLPROG spdifin [expr $spdifin - 1] +} + +proc setsyncsource {} { + global CTRLPROG + global syncsource + exec $CTRLPROG syncref [expr $syncsource -1] +} + + +proc setmaster {} { + global CTRLPROG + global master + exec $CTRLPROG master $master +} + +proc setwordclock {} { + global CTRLPROG + global wordclock + exec $CTRLPROG wordclock $wordclock +} + +proc setadat1cd {} { + global CTRLPROG + global adat1cd + exec $CTRLPROG adat1cd $adat1cd +} + + +frame .control + +# SPDIF In & SPDIF Out + + +frame .control.spdif + +frame .control.spdif.in +label .control.spdif.in.text -text "SPDIF In" -justify left +radiobutton .control.spdif.in.input1 -text "Optical" -anchor nw -width 13 -variable spdifin -value 1 -command setspdifin -selectcolor blue -font times +radiobutton .control.spdif.in.input2 -text "Coaxial" -anchor nw -width 13 -variable spdifin -value 2 -command setspdifin -selectcolor blue -font times +radiobutton .control.spdif.in.input3 -text "Intern " -anchor nw -width 13 -variable spdifin -command setspdifin -value 3 -selectcolor blue -font times + +checkbutton .control.spdif.in.adat1cd -text "ADAT1 Intern" -anchor nw -width 13 -variable adat1cd -command setadat1cd -selectcolor blue -font times + +pack .control.spdif.in.text .control.spdif.in.input1 .control.spdif.in.input2 .control.spdif.in.input3 .control.spdif.in.adat1cd + +label .control.spdif.space + +frame .control.spdif.out +label .control.spdif.out.text -text "SPDIF Out" -justify left +checkbutton .control.spdif.out.pro -text "Professional" -anchor nw -width 13 -variable spprof -command setprof -selectcolor blue -font times +checkbutton .control.spdif.out.emphasis -text "Emphasis" -anchor nw -width 13 -variable spemph -command setemph -selectcolor blue -font times +checkbutton .control.spdif.out.dolby -text "NoAudio" -anchor nw -width 13 -variable spnoaud -command setnoaud -selectcolor blue -font times +checkbutton .control.spdif.out.optout -text "Optical Out" -anchor nw -width 13 -variable spoptical -command setoptical -selectcolor blue -font times + +pack .control.spdif.out.optout .control.spdif.out.dolby .control.spdif.out.emphasis .control.spdif.out.pro .control.spdif.out.text -side bottom + +pack .control.spdif.in .control.spdif.space .control.spdif.out -side top -fill y -padx 3 -expand 1 + +# Sync Mode & Sync Source + +frame .control.sync +frame .control.sync.mode +label .control.sync.mode.text -text "Sync Mode" -justify left +checkbutton .control.sync.mode.master -text "Master" -anchor nw -width 13 -variable master -command setmaster -selectcolor blue -font times +checkbutton .control.sync.mode.wc -text "Wordclock" -anchor nw -width 13 -variable wordclock -command setwordclock -selectcolor blue -font times + +pack .control.sync.mode.text .control.sync.mode.master .control.sync.mode.wc + +label .control.sync.space + +frame .control.sync.src +label .control.sync.src.text -text "Sync Source" -justify left +radiobutton .control.sync.src.input1 -text "ADAT1" -anchor nw -width 13 -variable syncsource -value 1 -command setsyncsource -selectcolor blue -font times +radiobutton .control.sync.src.input2 -text "ADAT2" -anchor nw -width 13 -variable syncsource -value 2 -command setsyncsource -selectcolor blue -font times +radiobutton .control.sync.src.input3 -text "ADAT3" -anchor nw -width 13 -variable syncsource -command setsyncsource -value 3 -selectcolor blue -font times +radiobutton .control.sync.src.input4 -text "SPDIF" -anchor nw -width 13 -variable syncsource -command setsyncsource -value 4 -selectcolor blue -font times + +pack .control.sync.src.input4 .control.sync.src.input3 .control.sync.src.input2 .control.sync.src.input1 .control.sync.src.text -side bottom + +pack .control.sync.mode .control.sync.space .control.sync.src -side top -fill y -padx 3 -expand 1 + +label .control.space -text "" -width 10 + +# Buffer Size + +frame .control.buf +label .control.buf.text -text "Buffer Size (Latency)" -justify left +radiobutton .control.buf.b1 -selectcolor red -text "64 (1.5 ms)" -width 13 -anchor nw -variable ssrate -value 1 -font times +radiobutton .control.buf.b2 -selectcolor red -text "128 (3 ms)" -width 13 -anchor nw -variable ssrate -value 2 -font times +radiobutton .control.buf.b3 -selectcolor red -text "256 (6 ms)" -width 13 -anchor nw -variable ssrate -value 3 -font times +radiobutton .control.buf.b4 -selectcolor red -text "512 (12 ms)" -width 13 -anchor nw -variable ssrate -value 4 -font times +radiobutton .control.buf.b5 -selectcolor red -text "1024 (23 ms)" -width 13 -anchor nw -variable ssrate -value 5 -font times +radiobutton .control.buf.b6 -selectcolor red -text "2048 (46 ms)" -width 13 -anchor nw -variable ssrate -value 6 -font times +radiobutton .control.buf.b7 -selectcolor red -text "4096 (93 ms)" -width 13 -anchor nw -variable ssrate -value 7 -font times +radiobutton .control.buf.b8 -selectcolor red -text "8192 (186 ms)" -width 13 -anchor nw -variable ssrate -value 8 -font times + +pack .control.buf.text .control.buf.b1 .control.buf.b2 .control.buf.b3 .control.buf.b4 .control.buf.b5 .control.buf.b6 .control.buf.b7 .control.buf.b8 -side top -padx 3 + +# Offset + +frame .control.offset + +frame .control.offset.in +label .control.offset.in.text -text "Offset In" -justify left +label .control.offset.in.off0 -text "dev\#0: -" -anchor nw -width 10 -font times +label .control.offset.in.off1 -text "dev\#1: -" -anchor nw -width 10 -font times +label .control.offset.in.off2 -text "dev\#2: -" -anchor nw -width 10 -font times +label .control.offset.in.off3 -text "dev\#3: -" -anchor nw -width 10 -font times + +pack .control.offset.in.text .control.offset.in.off0 .control.offset.in.off1 .control.offset.in.off2 .control.offset.in.off3 + +label .control.offset.space + +frame .control.offset.out +label .control.offset.out.text -text "Offset Out" -justify left +label .control.offset.out.off0 -text "dev\#0: -" -anchor nw -width 10 -font times +label .control.offset.out.off1 -text "dev\#1: -" -anchor nw -width 10 -font times +label .control.offset.out.off2 -text "dev\#2: -" -anchor nw -width 10 -font times +label .control.offset.out.off3 -text "dev\#3: -" -anchor nw -width 10 -font times + +pack .control.offset.out.off3 .control.offset.out.off2 .control.offset.out.off1 .control.offset.out.off0 .control.offset.out.text -side bottom + +pack .control.offset.in .control.offset.space .control.offset.out -side top -fill y -padx 3 -expand 1 + + +pack .control.spdif .control.sync .control.space .control.buf .control.offset -side left -fill both -anchor n -expand 1 + + +label .statustext -text Status -justify center -relief ridge +label .controltext -text Control -justify center -relief ridge + +label .statusspace +label .controlspace + +pack .statustext .status .statusspace .controltext .control .controlspace -side top -anchor nw -fill both -expand 1 + + +proc get_bit {output sstr} { + set idx1 [string last [concat $sstr 1] $output] + set idx1 [expr $idx1 != -1] + return $idx1 +} + +proc get_val {output sstr} { + set val [string wordend $output [string last $sstr $output]] + set val [string range $output $val [expr $val+1]] + return $val +} + +proc get_val2 {output sstr} { + set val [string wordend $output [string first $sstr $output]] + set val [string range $output $val [expr $val+2]] + return $val +} + +proc get_control {} { + global spprof + global spemph + global spnoaud + global spoptical + global spdifin + global ssrate + global master + global wordclock + global syncsource + global CTRLPROG + + set f [open "| $CTRLPROG control" r+] + set ooo [read $f 1000] + close $f +# puts $ooo + + set spprof [ get_bit $ooo "pro"] + set spemph [ get_bit $ooo "emphasis"] + set spnoaud [ get_bit $ooo "dolby"] + set spoptical [ get_bit $ooo "opt_out"] + set spdifin [ expr [ get_val $ooo "spdif_in"] + 1] + set ssrate [ expr [ get_val $ooo "latency"] + 1] + set master [ expr [ get_val $ooo "master"]] + set wordclock [ expr [ get_val $ooo "wordclock"]] + set syncsource [ expr [ get_val $ooo "sync_ref"] + 1] +} + +proc get_status {} { + global srate + global ctrlcom + + global adatlock1 + global adatlock2 + global adatlock3 + + global adatsync1 + global adatsync2 + global adatsync3 + + global tcbusy + global tcout + global tcvalid + + global spdiferr + global crystal + global .status.spdif.text + global CTRLPROG + + + set f [open "| $CTRLPROG status" r+] + set ooo [read $f 1000] + close $f +# puts $ooo + +# samplerate + + set idx1 [string last "sr48 1" $ooo] + set idx2 [string last "doublespeed 1" $ooo] + if {$idx1 >= 0} { + set fact1 48000 + } else { + set fact1 44100 + } + + if {$idx2 >= 0} { + set fact2 2 + } else { + set fact2 1 + } + set srate [expr $fact1 * $fact2] +# ADAT lock + + set val [get_val $ooo lockmask] + set adatlock1 0 + set adatlock2 0 + set adatlock3 0 + if {[expr $val & 1]} { + set adatlock3 1 + } + if {[expr $val & 2]} { + set adatlock2 1 + } + if {[expr $val & 4]} { + set adatlock1 1 + } + +# ADAT sync + set val [get_val $ooo syncmask] + set adatsync1 0 + set adatsync2 0 + set adatsync3 0 + + if {[expr $val & 1]} { + set adatsync3 1 + } + if {[expr $val & 2]} { + set adatsync2 1 + } + if {[expr $val & 4]} { + set adatsync1 1 + } + +# TC busy + + set tcbusy [get_bit $ooo "busy"] + set tcout [get_bit $ooo "out"] + set tcvalid [get_bit $ooo "valid"] + set spdiferr [expr [get_bit $ooo "spdif_error"] == 0] + +# 000=64kHz, 100=88.2kHz, 011=96kHz +# 111=32kHz, 110=44.1kHz, 101=48kHz + + set val [get_val $ooo crystalrate] + + set crystal "--.- kHz" + if {$val == 0} { + set crystal "64 kHz" + } + if {$val == 4} { + set crystal "88.2 kHz" + } + if {$val == 3} { + set crystal "96 kHz" + } + if {$val == 7} { + set crystal "32 kHz" + } + if {$val == 6} { + set crystal "44.1 kHz" + } + if {$val == 5} { + set crystal "48 kHz" + } + .status.spdif.sr configure -text $crystal +} + +proc get_offset {} { + global inoffset + global outoffset + global CTRLPROG + + set f [open "| $CTRLPROG mix" r+] + set ooo [read $f 1000] + close $f +# puts $ooo + + if { [string match "*devnr*" $ooo] } { + set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] + set val [get_val2 $ooo i_offset] + .control.offset.in.off0 configure -text "dev\#0: $val" + set val [get_val2 $ooo o_offset] + .control.offset.out.off0 configure -text "dev\#0: $val" + } else { + .control.offset.in.off0 configure -text "dev\#0: -" + .control.offset.out.off0 configure -text "dev\#0: -" + } + if { [string match "*devnr*" $ooo] } { + set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] + set val [get_val2 $ooo i_offset] + .control.offset.in.off1 configure -text "dev\#1: $val" + set val [get_val2 $ooo o_offset] + .control.offset.out.off1 configure -text "dev\#1: $val" + } else { + .control.offset.in.off1 configure -text "dev\#1: -" + .control.offset.out.off1 configure -text "dev\#1: -" + } + if { [string match "*devnr*" $ooo] } { + set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] + set val [get_val2 $ooo i_offset] + .control.offset.in.off2 configure -text "dev\#2: $val" + set val [get_val2 $ooo o_offset] + .control.offset.out.off2 configure -text "dev\#2: $val" + } else { + .control.offset.in.off2 configure -text "dev\#2: -" + .control.offset.out.off2 configure -text "dev\#2: -" + } + if { [string match "*devnr*" $ooo] } { + set ooo [string range $ooo [string wordend $ooo [string first devnr $ooo]] end] + set val [get_val2 $ooo i_offset] + .control.offset.in.off3 configure -text "dev\#3: $val" + set val [get_val2 $ooo o_offset] + .control.offset.out.off3 configure -text "dev\#3: $val" + } else { + .control.offset.in.off3 configure -text "dev\#3: -" + .control.offset.out.off3 configure -text "dev\#3: -" + } +} + + +proc get_all {} { +get_status +get_control +get_offset +} + +# main +while {1} { + after 200 + get_all + update +} diff --git a/trunk/Documentation/sound/oss/solo1 b/trunk/Documentation/sound/oss/solo1 new file mode 100644 index 000000000000..95c4c83422b3 --- /dev/null +++ b/trunk/Documentation/sound/oss/solo1 @@ -0,0 +1,70 @@ +Recording +--------- + +Recording does not work on the author's card, but there +is at least one report of it working on later silicon. +The chip behaves differently than described in the data sheet, +likely due to a chip bug. Working around this would require +the help of ESS (for example by publishing an errata sheet), +but ESS has not done so far. + +Also, the chip only supports 24 bit addresses for recording, +which means it cannot work on some Alpha mainboards. + + +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (or later, available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card has an OPL compatible FM synthesizer. + +Thomas Sailer +t.sailer@alumni.ethz.ch diff --git a/trunk/Documentation/sound/oss/sonicvibes b/trunk/Documentation/sound/oss/sonicvibes new file mode 100644 index 000000000000..84dee2e0b37d --- /dev/null +++ b/trunk/Documentation/sound/oss/sonicvibes @@ -0,0 +1,81 @@ +/proc/sound, /dev/sndstat +------------------------- + +/proc/sound and /dev/sndstat is not supported by the +driver. To find out whether the driver succeeded loading, +check the kernel log (dmesg). + + +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card both has an OPL compatible FM synthesizer as well as +a wavetable synthesizer. + +I haven't managed so far to get the OPL synth running. + +Using the wavetable synthesizer requires allocating +1-4MB of physically contiguous memory, which isn't possible +currently on Linux without ugly hacks like the bigphysarea +patch. Therefore, the driver doesn't support wavetable +synthesis. + + +No support from S3 +------------------ + +I do not get any support from S3. Therefore, the driver +still has many problems. For example, although the manual +states that the chip should be able to access the sample +buffer anywhere in 32bit address space, I haven't managed to +get it working with buffers above 16M. Therefore, the card +has the same disadvantages as ISA soundcards. + +Given that the card is also very noisy, and if you haven't +already bought it, you should strongly opt for one of the +comparatively priced Ensoniq products. + + +Thomas Sailer +t.sailer@alumni.ethz.ch diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 17becb9b1a96..1c6223d3ce70 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -898,16 +898,6 @@ M: jack@suse.cz L: linux-kernel@vger.kernel.org S: Maintained -DISTRIBUTED LOCK MANAGER -P: Patrick Caulfield -M: pcaulfie@redhat.com -P: David Teigland -M: teigland@redhat.com -L: cluster-devel@redhat.com -W: http://sources.redhat.com/cluster/ -T: git kernel.org:/pub/scm/linux/kernel/git/steve/gfs-2.6.git -S: Supported - DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER P: Tobias Ringstrom M: tori@unhappy.mine.nu @@ -987,13 +977,6 @@ L: ebtables-devel@lists.sourceforge.net W: http://ebtables.sourceforge.net/ S: Maintained -ECRYPT FILE SYSTEM -P: Mike Halcrow, Phillip Hellewell -M: mhalcrow@us.ibm.com, phillip@hellewell.homeip.net -L: ecryptfs-devel@lists.sourceforge.net -W: http://ecryptfs.sourceforge.net/ -S: Supported - EDAC-CORE P: Doug Thompson M: norsk5@xmission.com @@ -1183,14 +1166,6 @@ M: khc@pm.waw.pl W: http://www.kernel.org/pub/linux/utils/net/hdlc/ S: Maintained -GFS2 FILE SYSTEM -P: Steven Whitehouse -M: swhiteho@redhat.com -L: cluster-devel@redhat.com -W: http://sources.redhat.com/cluster/ -T: git kernel.org:/pub/scm/linux/kernel/git/steve/gfs-2.6.git -S: Supported - GIGASET ISDN DRIVERS P: Hansjoerg Lipp M: hjlipp@web.de @@ -1918,6 +1893,11 @@ M: rroesler@syskonnect.de W: http://www.syskonnect.com S: Supported +MAESTRO PCI SOUND DRIVERS +P: Zach Brown +M: zab@zabbo.net +S: Odd Fixes + MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 P: Michael Kerrisk M: mtk-manpages@gmx.net @@ -2454,19 +2434,6 @@ M: mporter@kernel.crashing.org L: linux-kernel@vger.kernel.org S: Maintained -READ-COPY UPDATE (RCU) -P: Dipankar Sarma -M: dipankar@in.ibm.com -W: http://www.rdrop.com/users/paulmck/rclock/ -L: linux-kernel@vger.kernel.org -S: Supported - -RCUTORTURE MODULE -P: Josh Triplett -M: josh@freedesktop.org -L: linux-kernel@vger.kernel.org -S: Maintained - REAL TIME CLOCK DRIVER P: Paul Gortmaker M: p_gortmaker@yahoo.com @@ -2887,11 +2854,6 @@ M: hlhung3i@gmail.com W: http://tcp-lp-mod.sourceforge.net/ S: Maintained -TI FLASH MEDIA INTERFACE DRIVER -P: Alex Dubov -M: oakad@yahoo.com -S: Maintained - TI OMAP RANDOM NUMBER GENERATOR SUPPORT P: Deepak Saxena M: dsaxena@plexity.net @@ -3415,6 +3377,12 @@ M: Henk.Vergonet@gmail.com L: usbb2k-api-dev@nongnu.org S: Maintained +YMFPCI YAMAHA PCI SOUND (Use ALSA instead) +P: Pete Zaitcev +M: zaitcev@yahoo.com +L: linux-kernel@vger.kernel.org +S: Obsolete + Z8530 DRIVER FOR AX.25 P: Joerg Reuter M: jreuter@yaina.de diff --git a/trunk/Makefile b/trunk/Makefile index adb2c748e105..4c6c5e32ef96 100644 --- a/trunk/Makefile +++ b/trunk/Makefile @@ -1321,7 +1321,7 @@ define xtags --langdef=kconfig \ --language-force=kconfig \ --regex-kconfig='/^[[:blank:]]*config[[:blank:]]+([[:alnum:]_]+)/\1/'; \ - $(all-defconfigs) | xargs -r $1 -a \ + $(all-defconfigs) | xargs $1 -a \ --langdef=dotconfig \ --language-force=dotconfig \ --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/'; \ @@ -1329,7 +1329,7 @@ define xtags $(all-sources) | xargs $1 -a; \ $(all-kconfigs) | xargs $1 -a \ --regex='/^[ \t]*config[ \t]+\([a-zA-Z0-9_]+\)/\1/'; \ - $(all-defconfigs) | xargs -r $1 -a \ + $(all-defconfigs) | xargs $1 -a \ --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/'; \ else \ $(all-sources) | xargs $1 -a; \ diff --git a/trunk/arch/alpha/kernel/setup.c b/trunk/arch/alpha/kernel/setup.c index fd4a8fa0c93d..a94e6d93e2ee 100644 --- a/trunk/arch/alpha/kernel/setup.c +++ b/trunk/arch/alpha/kernel/setup.c @@ -21,7 +21,6 @@ #include #include #include -#include /* CONFIG_ALPHA_LCA etc */ #include #include #include diff --git a/trunk/arch/alpha/kernel/systbls.S b/trunk/arch/alpha/kernel/systbls.S index 4342cea1a926..f6cfe8ce3f96 100644 --- a/trunk/arch/alpha/kernel/systbls.S +++ b/trunk/arch/alpha/kernel/systbls.S @@ -4,7 +4,6 @@ * The system call table. */ -#include /* CONFIG_OSF4_COMPAT */ #include .data diff --git a/trunk/arch/arm/kernel/crunch.c b/trunk/arch/arm/kernel/crunch.c index 748175921f9b..cec83783206e 100644 --- a/trunk/arch/arm/kernel/crunch.c +++ b/trunk/arch/arm/kernel/crunch.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/trunk/arch/arm/kernel/iwmmxt-notifier.c b/trunk/arch/arm/kernel/iwmmxt-notifier.c index 44a86c33796e..0d1a1db40062 100644 --- a/trunk/arch/arm/kernel/iwmmxt-notifier.c +++ b/trunk/arch/arm/kernel/iwmmxt-notifier.c @@ -15,7 +15,6 @@ */ #include -#include #include #include #include diff --git a/trunk/arch/arm/mach-at91rm9200/board-1arm.c b/trunk/arch/arm/mach-at91rm9200/board-1arm.c index 36eecd7161f5..971c3e2d8e36 100644 --- a/trunk/arch/arm/mach-at91rm9200/board-1arm.c +++ b/trunk/arch/arm/mach-at91rm9200/board-1arm.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-at91rm9200/board-carmeva.c b/trunk/arch/arm/mach-at91rm9200/board-carmeva.c index 50e513681ae6..98208740e7c5 100644 --- a/trunk/arch/arm/mach-at91rm9200/board-carmeva.c +++ b/trunk/arch/arm/mach-at91rm9200/board-carmeva.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-at91rm9200/board-eb9200.c b/trunk/arch/arm/mach-at91rm9200/board-eb9200.c index c6e0d51fbea0..65e867ba2df3 100644 --- a/trunk/arch/arm/mach-at91rm9200/board-eb9200.c +++ b/trunk/arch/arm/mach-at91rm9200/board-eb9200.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-at91rm9200/board-kafa.c b/trunk/arch/arm/mach-at91rm9200/board-kafa.c index 91e301924f2c..6ef3c4879829 100644 --- a/trunk/arch/arm/mach-at91rm9200/board-kafa.c +++ b/trunk/arch/arm/mach-at91rm9200/board-kafa.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-at91rm9200/board-kb9202.c b/trunk/arch/arm/mach-at91rm9200/board-kb9202.c index 272fe43bceca..35a954a44b1b 100644 --- a/trunk/arch/arm/mach-at91rm9200/board-kb9202.c +++ b/trunk/arch/arm/mach-at91rm9200/board-kb9202.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-ep93xx/edb9302.c b/trunk/arch/arm/mach-ep93xx/edb9302.c index 62a8efd23256..0315615b74da 100644 --- a/trunk/arch/arm/mach-ep93xx/edb9302.c +++ b/trunk/arch/arm/mach-ep93xx/edb9302.c @@ -10,7 +10,6 @@ * your option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-ep93xx/edb9312.c b/trunk/arch/arm/mach-ep93xx/edb9312.c index 9e399211108c..e310e4d72990 100644 --- a/trunk/arch/arm/mach-ep93xx/edb9312.c +++ b/trunk/arch/arm/mach-ep93xx/edb9312.c @@ -11,7 +11,6 @@ * your option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-ep93xx/edb9315.c b/trunk/arch/arm/mach-ep93xx/edb9315.c index ef7482faad81..249ca9e57bc6 100644 --- a/trunk/arch/arm/mach-ep93xx/edb9315.c +++ b/trunk/arch/arm/mach-ep93xx/edb9315.c @@ -10,7 +10,6 @@ * your option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-ep93xx/edb9315a.c b/trunk/arch/arm/mach-ep93xx/edb9315a.c index fa958e9d6ddd..7ca0e6170a41 100644 --- a/trunk/arch/arm/mach-ep93xx/edb9315a.c +++ b/trunk/arch/arm/mach-ep93xx/edb9315a.c @@ -10,7 +10,6 @@ * your option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-lh7a40x/clcd.c b/trunk/arch/arm/mach-lh7a40x/clcd.c index 93751fee793d..1992db4c2523 100644 --- a/trunk/arch/arm/mach-lh7a40x/clcd.c +++ b/trunk/arch/arm/mach-lh7a40x/clcd.c @@ -8,7 +8,7 @@ * version 2 as published by the Free Software Foundation. * */ -#include + #include #include #include diff --git a/trunk/arch/arm/mach-lh7a40x/clocks.c b/trunk/arch/arm/mach-lh7a40x/clocks.c index 2291afe9f23e..7530a95c15a6 100644 --- a/trunk/arch/arm/mach-lh7a40x/clocks.c +++ b/trunk/arch/arm/mach-lh7a40x/clocks.c @@ -8,7 +8,6 @@ * */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-omap2/pm-domain.c b/trunk/arch/arm/mach-omap2/pm-domain.c index 5e20e740cde5..2494091a078b 100644 --- a/trunk/arch/arm/mach-omap2/pm-domain.c +++ b/trunk/arch/arm/mach-omap2/pm-domain.c @@ -15,7 +15,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-pnx4008/gpio.c b/trunk/arch/arm/mach-pnx4008/gpio.c index e1ce050d8fe0..1ab84ced7b5a 100644 --- a/trunk/arch/arm/mach-pnx4008/gpio.c +++ b/trunk/arch/arm/mach-pnx4008/gpio.c @@ -14,7 +14,6 @@ * or implied. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-pnx4008/sleep.S b/trunk/arch/arm/mach-pnx4008/sleep.S index 93c802bac269..fea1e17a3650 100644 --- a/trunk/arch/arm/mach-pnx4008/sleep.S +++ b/trunk/arch/arm/mach-pnx4008/sleep.S @@ -11,7 +11,6 @@ * or implied. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-pnx4008/time.c b/trunk/arch/arm/mach-pnx4008/time.c index 756228ddd035..b986065cd0f3 100644 --- a/trunk/arch/arm/mach-pnx4008/time.c +++ b/trunk/arch/arm/mach-pnx4008/time.c @@ -11,7 +11,6 @@ * or implied. */ -#include #include #include #include diff --git a/trunk/arch/arm/mach-pxa/leds-trizeps4.c b/trunk/arch/arm/mach-pxa/leds-trizeps4.c index 14cfc85e44b5..2271d20ffeda 100644 --- a/trunk/arch/arm/mach-pxa/leds-trizeps4.c +++ b/trunk/arch/arm/mach-pxa/leds-trizeps4.c @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #include #include diff --git a/trunk/arch/arm/tools/gen-mach-types b/trunk/arch/arm/tools/gen-mach-types index 2f9c9b5dd260..ce319ef64bc1 100644 --- a/trunk/arch/arm/tools/gen-mach-types +++ b/trunk/arch/arm/tools/gen-mach-types @@ -28,7 +28,6 @@ END { printf(" */\n\n"); printf("#ifndef __ASM_ARM_MACH_TYPE_H\n"); printf("#define __ASM_ARM_MACH_TYPE_H\n\n"); - printf("#include \n\n"); printf("#ifndef __ASSEMBLY__\n"); printf("/* The type of machine we're running on */\n"); printf("extern unsigned int __machine_arch_type;\n"); diff --git a/trunk/arch/arm26/lib/ecard.S b/trunk/arch/arm26/lib/ecard.S index b4633150f01c..658bc4529c9d 100644 --- a/trunk/arch/arm26/lib/ecard.S +++ b/trunk/arch/arm26/lib/ecard.S @@ -7,7 +7,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include /* for CONFIG_CPU_nn */ #include #include #include diff --git a/trunk/arch/arm26/lib/io-acorn.S b/trunk/arch/arm26/lib/io-acorn.S index f6c3e30b1b4f..5f62ade5be39 100644 --- a/trunk/arch/arm26/lib/io-acorn.S +++ b/trunk/arch/arm26/lib/io-acorn.S @@ -7,7 +7,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include /* for CONFIG_CPU_nn */ #include #include #include diff --git a/trunk/arch/frv/kernel/time.c b/trunk/arch/frv/kernel/time.c index 7e55884135ed..44a9aebc4f5a 100644 --- a/trunk/arch/frv/kernel/time.c +++ b/trunk/arch/frv/kernel/time.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include /* CONFIG_HEARTBEAT */ #include #include #include diff --git a/trunk/arch/h8300/kernel/time.c b/trunk/arch/h8300/kernel/time.c index e569d17b4ae6..8abab3bc2b6f 100644 --- a/trunk/arch/h8300/kernel/time.c +++ b/trunk/arch/h8300/kernel/time.c @@ -16,7 +16,6 @@ * "A Kernel Model for Precision Timekeeping" by Dave Mills */ -#include /* CONFIG_HEARTBEAT */ #include #include #include diff --git a/trunk/arch/i386/kernel/acpi/boot.c b/trunk/arch/i386/kernel/acpi/boot.c index 92f79cdd9a48..1aaea6ab8c46 100644 --- a/trunk/arch/i386/kernel/acpi/boot.c +++ b/trunk/arch/i386/kernel/acpi/boot.c @@ -62,6 +62,8 @@ static inline int acpi_madt_oem_check(char *oem_id, char *oem_table_id) { return #include #endif /* CONFIG_X86_LOCAL_APIC */ +static inline int gsi_irq_sharing(int gsi) { return gsi; } + #endif /* X86 */ #define BAD_MADT_ENTRY(entry, end) ( \ @@ -466,7 +468,12 @@ void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger) int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) { - *irq = gsi; +#ifdef CONFIG_X86_IO_APIC + if (use_pci_vector() && !platform_legacy_irq(gsi)) + *irq = IO_APIC_VECTOR(gsi); + else +#endif + *irq = gsi_irq_sharing(gsi); return 0; } diff --git a/trunk/arch/i386/kernel/i8259.c b/trunk/arch/i386/kernel/i8259.c index d07ed31f11e3..ea5f4e7958d8 100644 --- a/trunk/arch/i386/kernel/i8259.c +++ b/trunk/arch/i386/kernel/i8259.c @@ -34,15 +34,35 @@ * moves to arch independent land */ -static int i8259A_auto_eoi; DEFINE_SPINLOCK(i8259A_lock); + +static void end_8259A_irq (unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && + irq_desc[irq].action) + enable_8259A_irq(irq); +} + +#define shutdown_8259A_irq disable_8259A_irq + +static int i8259A_auto_eoi; + static void mask_and_ack_8259A(unsigned int); -static struct irq_chip i8259A_chip = { - .name = "XT-PIC", - .mask = disable_8259A_irq, - .unmask = enable_8259A_irq, - .mask_ack = mask_and_ack_8259A, +unsigned int startup_8259A_irq(unsigned int irq) +{ + enable_8259A_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type i8259A_irq_type = { + .typename = "XT-PIC", + .startup = startup_8259A_irq, + .shutdown = shutdown_8259A_irq, + .enable = enable_8259A_irq, + .disable = disable_8259A_irq, + .ack = mask_and_ack_8259A, + .end = end_8259A_irq, }; /* @@ -113,7 +133,7 @@ void make_8259A_irq(unsigned int irq) { disable_irq_nosync(irq); io_apic_irqs &= ~(1< #include #include -#include -#include -#include #include #include @@ -41,8 +38,6 @@ #include #include #include -#include -#include #include #include @@ -91,6 +86,15 @@ static struct irq_pin_list { int apic, pin, next; } irq_2_pin[PIN_MAP_SIZE]; +int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1}; +#ifdef CONFIG_PCI_MSI +#define vector_to_irq(vector) \ + (platform_legacy_irq(vector) ? vector : vector_irq[vector]) +#else +#define vector_to_irq(vector) (vector) +#endif + + union entry_union { struct { u32 w1, w2; }; struct IO_APIC_route_entry entry; @@ -276,7 +280,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t cpumask) break; entry = irq_2_pin + entry->next; } - set_native_irq_info(irq, cpumask); + set_irq_info(irq, cpumask); spin_unlock_irqrestore(&ioapic_lock, flags); } @@ -1177,45 +1181,46 @@ static inline int IO_APIC_irq_trigger(int irq) /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 }; -static int __assign_irq_vector(int irq) +int assign_irq_vector(int irq) { static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; + unsigned long flags; int vector; - BUG_ON((unsigned)irq >= NR_IRQ_VECTORS); + BUG_ON(irq != AUTO_ASSIGN && (unsigned)irq >= NR_IRQ_VECTORS); - if (IO_APIC_VECTOR(irq) > 0) - return IO_APIC_VECTOR(irq); + spin_lock_irqsave(&vector_lock, flags); + if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0) { + spin_unlock_irqrestore(&vector_lock, flags); + return IO_APIC_VECTOR(irq); + } +next: current_vector += 8; if (current_vector == SYSCALL_VECTOR) - current_vector += 8; + goto next; if (current_vector >= FIRST_SYSTEM_VECTOR) { offset++; - if (!(offset % 8)) + if (!(offset%8)) { + spin_unlock_irqrestore(&vector_lock, flags); return -ENOSPC; + } current_vector = FIRST_DEVICE_VECTOR + offset; } vector = current_vector; - IO_APIC_VECTOR(irq) = vector; - - return vector; -} - -static int assign_irq_vector(int irq) -{ - unsigned long flags; - int vector; + vector_irq[vector] = irq; + if (irq != AUTO_ASSIGN) + IO_APIC_VECTOR(irq) = vector; - spin_lock_irqsave(&vector_lock, flags); - vector = __assign_irq_vector(irq); spin_unlock_irqrestore(&vector_lock, flags); return vector; } -static struct irq_chip ioapic_chip; + +static struct hw_interrupt_type ioapic_level_type; +static struct hw_interrupt_type ioapic_edge_type; #define IOAPIC_AUTO -1 #define IOAPIC_EDGE 0 @@ -1223,14 +1228,16 @@ static struct irq_chip ioapic_chip; static void ioapic_register_intr(int irq, int vector, unsigned long trigger) { + unsigned idx; + + idx = use_pci_vector() && !platform_legacy_irq(irq) ? vector : irq; + if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || trigger == IOAPIC_LEVEL) - set_irq_chip_and_handler(irq, &ioapic_chip, - handle_fasteoi_irq); + irq_desc[idx].chip = &ioapic_level_type; else - set_irq_chip_and_handler(irq, &ioapic_chip, - handle_edge_irq); - set_intr_gate(vector, interrupt[irq]); + irq_desc[idx].chip = &ioapic_edge_type; + set_intr_gate(vector, interrupt[idx]); } static void __init setup_IO_APIC_irqs(void) @@ -1339,8 +1346,7 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in * The timer IRQ doesn't have to know that behind the * scene we have a 8259A-master in AEOI mode ... */ - irq_desc[0].chip = &ioapic_chip; - set_irq_handler(0, handle_edge_irq); + irq_desc[0].chip = &ioapic_edge_type; /* * Add it to the IO-APIC irq-routing table: @@ -1475,12 +1481,17 @@ void __init print_IO_APIC(void) ); } } + if (use_pci_vector()) + printk(KERN_INFO "Using vector-based indexing\n"); printk(KERN_DEBUG "IRQ to pin mappings:\n"); for (i = 0; i < NR_IRQS; i++) { struct irq_pin_list *entry = irq_2_pin + i; if (entry->pin < 0) continue; - printk(KERN_DEBUG "IRQ%d ", i); + if (use_pci_vector() && !platform_legacy_irq(i)) + printk(KERN_DEBUG "IRQ%d ", IO_APIC_VECTOR(i)); + else + printk(KERN_DEBUG "IRQ%d ", i); for (;;) { printk("-> %d:%d", entry->apic, entry->pin); if (!entry->next) @@ -1907,8 +1918,6 @@ static int __init timer_irq_works(void) */ /* - * Startup quirk: - * * Starting up a edge-triggered IO-APIC interrupt is * nasty - we need to make sure that we get the edge. * If it is already asserted for some reason, we need @@ -1916,10 +1925,8 @@ static int __init timer_irq_works(void) * * This is not complete - we should be able to fake * an edge even if it isn't on the 8259A... - * - * (We do this for level-triggered IRQs too - it cannot hurt.) */ -static unsigned int startup_ioapic_irq(unsigned int irq) +static unsigned int startup_edge_ioapic_irq(unsigned int irq) { int was_pending = 0; unsigned long flags; @@ -1936,18 +1943,47 @@ static unsigned int startup_ioapic_irq(unsigned int irq) return was_pending; } -static void ack_ioapic_irq(unsigned int irq) +/* + * Once we have recorded IRQ_PENDING already, we can mask the + * interrupt for real. This prevents IRQ storms from unhandled + * devices. + */ +static void ack_edge_ioapic_irq(unsigned int irq) { - move_native_irq(irq); + move_irq(irq); + if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) + == (IRQ_PENDING | IRQ_DISABLED)) + mask_IO_APIC_irq(irq); ack_APIC_irq(); } -static void ack_ioapic_quirk_irq(unsigned int irq) +/* + * Level triggered interrupts can just be masked, + * and shutting down and starting up the interrupt + * is the same as enabling and disabling them -- except + * with a startup need to return a "was pending" value. + * + * Level triggered interrupts are special because we + * do not touch any IO-APIC register while handling + * them. We ack the APIC in the end-IRQ handler, not + * in the start-IRQ-handler. Protection against reentrance + * from the same interrupt is still provided, both by the + * generic IRQ layer and by the fact that an unacked local + * APIC does not accept IRQs. + */ +static unsigned int startup_level_ioapic_irq (unsigned int irq) +{ + unmask_IO_APIC_irq(irq); + + return 0; /* don't check for pending */ +} + +static void end_level_ioapic_irq (unsigned int irq) { unsigned long v; int i; - move_native_irq(irq); + move_irq(irq); /* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various @@ -1982,26 +2018,105 @@ static void ack_ioapic_quirk_irq(unsigned int irq) } } -static int ioapic_retrigger_irq(unsigned int irq) +#ifdef CONFIG_PCI_MSI +static unsigned int startup_edge_ioapic_vector(unsigned int vector) +{ + int irq = vector_to_irq(vector); + + return startup_edge_ioapic_irq(irq); +} + +static void ack_edge_ioapic_vector(unsigned int vector) +{ + int irq = vector_to_irq(vector); + + move_native_irq(vector); + ack_edge_ioapic_irq(irq); +} + +static unsigned int startup_level_ioapic_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + return startup_level_ioapic_irq (irq); +} + +static void end_level_ioapic_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + move_native_irq(vector); + end_level_ioapic_irq(irq); +} + +static void mask_IO_APIC_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + mask_IO_APIC_irq(irq); +} + +static void unmask_IO_APIC_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + unmask_IO_APIC_irq(irq); +} + +#ifdef CONFIG_SMP +static void set_ioapic_affinity_vector (unsigned int vector, + cpumask_t cpu_mask) +{ + int irq = vector_to_irq(vector); + + set_native_irq_info(vector, cpu_mask); + set_ioapic_affinity_irq(irq, cpu_mask); +} +#endif +#endif + +static int ioapic_retrigger(unsigned int irq) { send_IPI_self(IO_APIC_VECTOR(irq)); return 1; } -static struct irq_chip ioapic_chip __read_mostly = { - .name = "IO-APIC", - .startup = startup_ioapic_irq, - .mask = mask_IO_APIC_irq, - .unmask = unmask_IO_APIC_irq, - .ack = ack_ioapic_irq, - .eoi = ack_ioapic_quirk_irq, +/* + * Level and edge triggered IO-APIC interrupts need different handling, + * so we use two separate IRQ descriptors. Edge triggered IRQs can be + * handled with the level-triggered descriptor, but that one has slightly + * more overhead. Level-triggered interrupts cannot be handled with the + * edge-triggered handler, without risking IRQ storms and other ugly + * races. + */ +static struct hw_interrupt_type ioapic_edge_type __read_mostly = { + .typename = "IO-APIC-edge", + .startup = startup_edge_ioapic, + .shutdown = shutdown_edge_ioapic, + .enable = enable_edge_ioapic, + .disable = disable_edge_ioapic, + .ack = ack_edge_ioapic, + .end = end_edge_ioapic, #ifdef CONFIG_SMP - .set_affinity = set_ioapic_affinity_irq, + .set_affinity = set_ioapic_affinity, #endif - .retrigger = ioapic_retrigger_irq, + .retrigger = ioapic_retrigger, }; +static struct hw_interrupt_type ioapic_level_type __read_mostly = { + .typename = "IO-APIC-level", + .startup = startup_level_ioapic, + .shutdown = shutdown_level_ioapic, + .enable = enable_level_ioapic, + .disable = disable_level_ioapic, + .ack = mask_and_ack_level_ioapic, + .end = end_level_ioapic, +#ifdef CONFIG_SMP + .set_affinity = set_ioapic_affinity, +#endif + .retrigger = ioapic_retrigger, +}; static inline void init_IO_APIC_traps(void) { @@ -2020,6 +2135,11 @@ static inline void init_IO_APIC_traps(void) */ for (irq = 0; irq < NR_IRQS ; irq++) { int tmp = irq; + if (use_pci_vector()) { + if (!platform_legacy_irq(tmp)) + if ((tmp = vector_to_irq(tmp)) == -1) + continue; + } if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) { /* * Hmm.. We don't have an entry for this, @@ -2030,21 +2150,20 @@ static inline void init_IO_APIC_traps(void) make_8259A_irq(irq); else /* Strange. Oh, well.. */ - irq_desc[irq].chip = &no_irq_chip; + irq_desc[irq].chip = &no_irq_type; } } } -/* - * The local APIC irq-chip implementation: - */ - -static void ack_apic(unsigned int irq) +static void enable_lapic_irq (unsigned int irq) { - ack_APIC_irq(); + unsigned long v; + + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); } -static void mask_lapic_irq (unsigned int irq) +static void disable_lapic_irq (unsigned int irq) { unsigned long v; @@ -2052,19 +2171,21 @@ static void mask_lapic_irq (unsigned int irq) apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); } -static void unmask_lapic_irq (unsigned int irq) +static void ack_lapic_irq (unsigned int irq) { - unsigned long v; - - v = apic_read(APIC_LVT0); - apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED); + ack_APIC_irq(); } -static struct irq_chip lapic_chip __read_mostly = { - .name = "local-APIC-edge", - .mask = mask_lapic_irq, - .unmask = unmask_lapic_irq, - .eoi = ack_apic, +static void end_lapic_irq (unsigned int i) { /* nothing */ } + +static struct hw_interrupt_type lapic_irq_type __read_mostly = { + .typename = "local-APIC-edge", + .startup = NULL, /* startup_irq() not used for IRQ0 */ + .shutdown = NULL, /* shutdown_irq() not used for IRQ0 */ + .enable = enable_lapic_irq, + .disable = disable_lapic_irq, + .ack = ack_lapic_irq, + .end = end_lapic_irq }; static void setup_nmi (void) @@ -2235,7 +2356,7 @@ static inline void check_timer(void) printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); disable_8259A_irq(0); - set_irq_chip_and_handler(0, &lapic_chip, handle_fasteoi_irq); + irq_desc[0].chip = &lapic_irq_type; apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ enable_8259A_irq(0); @@ -2410,238 +2531,6 @@ static int __init ioapic_init_sysfs(void) device_initcall(ioapic_init_sysfs); -/* - * Dynamic irq allocate and deallocation - */ -int create_irq(void) -{ - /* Allocate an unused irq */ - int irq, new, vector; - unsigned long flags; - - irq = -ENOSPC; - spin_lock_irqsave(&vector_lock, flags); - for (new = (NR_IRQS - 1); new >= 0; new--) { - if (platform_legacy_irq(new)) - continue; - if (irq_vector[new] != 0) - continue; - vector = __assign_irq_vector(new); - if (likely(vector > 0)) - irq = new; - break; - } - spin_unlock_irqrestore(&vector_lock, flags); - - if (irq >= 0) { - set_intr_gate(vector, interrupt[irq]); - dynamic_irq_init(irq); - } - return irq; -} - -void destroy_irq(unsigned int irq) -{ - unsigned long flags; - - dynamic_irq_cleanup(irq); - - spin_lock_irqsave(&vector_lock, flags); - irq_vector[irq] = 0; - spin_unlock_irqrestore(&vector_lock, flags); -} - -/* - * MSI mesage composition - */ -#ifdef CONFIG_PCI_MSI -static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg) -{ - int vector; - unsigned dest; - - vector = assign_irq_vector(irq); - if (vector >= 0) { - dest = cpu_mask_to_apicid(TARGET_CPUS); - - msg->address_hi = MSI_ADDR_BASE_HI; - msg->address_lo = - MSI_ADDR_BASE_LO | - ((INT_DEST_MODE == 0) ? - MSI_ADDR_DEST_MODE_PHYSICAL: - MSI_ADDR_DEST_MODE_LOGICAL) | - ((INT_DELIVERY_MODE != dest_LowestPrio) ? - MSI_ADDR_REDIRECTION_CPU: - MSI_ADDR_REDIRECTION_LOWPRI) | - MSI_ADDR_DEST_ID(dest); - - msg->data = - MSI_DATA_TRIGGER_EDGE | - MSI_DATA_LEVEL_ASSERT | - ((INT_DELIVERY_MODE != dest_LowestPrio) ? - MSI_DATA_DELIVERY_FIXED: - MSI_DATA_DELIVERY_LOWPRI) | - MSI_DATA_VECTOR(vector); - } - return vector; -} - -#ifdef CONFIG_SMP -static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) -{ - struct msi_msg msg; - unsigned int dest; - cpumask_t tmp; - int vector; - - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) - tmp = TARGET_CPUS; - - vector = assign_irq_vector(irq); - if (vector < 0) - return; - - dest = cpu_mask_to_apicid(mask); - - read_msi_msg(irq, &msg); - - msg.data &= ~MSI_DATA_VECTOR_MASK; - msg.data |= MSI_DATA_VECTOR(vector); - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; - msg.address_lo |= MSI_ADDR_DEST_ID(dest); - - write_msi_msg(irq, &msg); - set_native_irq_info(irq, mask); -} -#endif /* CONFIG_SMP */ - -/* - * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, - * which implement the MSI or MSI-X Capability Structure. - */ -static struct irq_chip msi_chip = { - .name = "PCI-MSI", - .unmask = unmask_msi_irq, - .mask = mask_msi_irq, - .ack = ack_ioapic_irq, -#ifdef CONFIG_SMP - .set_affinity = set_msi_irq_affinity, -#endif - .retrigger = ioapic_retrigger_irq, -}; - -int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev) -{ - struct msi_msg msg; - int ret; - ret = msi_compose_msg(dev, irq, &msg); - if (ret < 0) - return ret; - - write_msi_msg(irq, &msg); - - set_irq_chip_and_handler(irq, &msi_chip, handle_edge_irq); - - return 0; -} - -void arch_teardown_msi_irq(unsigned int irq) -{ - return; -} - -#endif /* CONFIG_PCI_MSI */ - -/* - * Hypertransport interrupt support - */ -#ifdef CONFIG_HT_IRQ - -#ifdef CONFIG_SMP - -static void target_ht_irq(unsigned int irq, unsigned int dest) -{ - u32 low, high; - low = read_ht_irq_low(irq); - high = read_ht_irq_high(irq); - - low &= ~(HT_IRQ_LOW_DEST_ID_MASK); - high &= ~(HT_IRQ_HIGH_DEST_ID_MASK); - - low |= HT_IRQ_LOW_DEST_ID(dest); - high |= HT_IRQ_HIGH_DEST_ID(dest); - - write_ht_irq_low(irq, low); - write_ht_irq_high(irq, high); -} - -static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) -{ - unsigned int dest; - cpumask_t tmp; - - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) - tmp = TARGET_CPUS; - - cpus_and(mask, tmp, CPU_MASK_ALL); - - dest = cpu_mask_to_apicid(mask); - - target_ht_irq(irq, dest); - set_native_irq_info(irq, mask); -} -#endif - -static struct hw_interrupt_type ht_irq_chip = { - .name = "PCI-HT", - .mask = mask_ht_irq, - .unmask = unmask_ht_irq, - .ack = ack_ioapic_irq, -#ifdef CONFIG_SMP - .set_affinity = set_ht_irq_affinity, -#endif - .retrigger = ioapic_retrigger_irq, -}; - -int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) -{ - int vector; - - vector = assign_irq_vector(irq); - if (vector >= 0) { - u32 low, high; - unsigned dest; - cpumask_t tmp; - - cpus_clear(tmp); - cpu_set(vector >> 8, tmp); - dest = cpu_mask_to_apicid(tmp); - - high = HT_IRQ_HIGH_DEST_ID(dest); - - low = HT_IRQ_LOW_BASE | - HT_IRQ_LOW_DEST_ID(dest) | - HT_IRQ_LOW_VECTOR(vector) | - ((INT_DEST_MODE == 0) ? - HT_IRQ_LOW_DM_PHYSICAL : - HT_IRQ_LOW_DM_LOGICAL) | - HT_IRQ_LOW_RQEOI_EDGE | - ((INT_DELIVERY_MODE != dest_LowestPrio) ? - HT_IRQ_LOW_MT_FIXED : - HT_IRQ_LOW_MT_ARBITRATED) | - HT_IRQ_LOW_IRQ_MASKED; - - write_ht_irq_low(irq, low); - write_ht_irq_high(irq, high); - - set_irq_chip_and_handler(irq, &ht_irq_chip, handle_edge_irq); - } - return vector; -} -#endif /* CONFIG_HT_IRQ */ - /* -------------------------------------------------------------------------- ACPI-based IOAPIC Configuration -------------------------------------------------------------------------- */ @@ -2795,7 +2684,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a ioapic_write_entry(ioapic, pin, entry); spin_lock_irqsave(&ioapic_lock, flags); - set_native_irq_info(irq, TARGET_CPUS); + set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); return 0; diff --git a/trunk/arch/i386/kernel/irq.c b/trunk/arch/i386/kernel/irq.c index 3dd2e180151b..5fe547cd8f9f 100644 --- a/trunk/arch/i386/kernel/irq.c +++ b/trunk/arch/i386/kernel/irq.c @@ -55,7 +55,6 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs) { /* high bit used in ret_from_ code */ int irq = ~regs->orig_eax; - struct irq_desc *desc = irq_desc + irq; #ifdef CONFIG_4KSTACKS union irq_ctx *curctx, *irqctx; u32 *isp; @@ -95,7 +94,7 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs) * current stack (which is the irq stack already after all) */ if (curctx != irqctx) { - int arg1, arg2, arg3, ebx; + int arg1, arg2, ebx; /* build the stack frame on the IRQ stack */ isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); @@ -111,17 +110,16 @@ fastcall unsigned int do_IRQ(struct pt_regs *regs) (curctx->tinfo.preempt_count & SOFTIRQ_MASK); asm volatile( - " xchgl %%ebx,%%esp \n" - " call *%%edi \n" + " xchgl %%ebx,%%esp \n" + " call __do_IRQ \n" " movl %%ebx,%%esp \n" - : "=a" (arg1), "=d" (arg2), "=c" (arg3), "=b" (ebx) - : "0" (irq), "1" (desc), "2" (regs), "3" (isp), - "D" (desc->handle_irq) - : "memory", "cc" + : "=a" (arg1), "=d" (arg2), "=b" (ebx) + : "0" (irq), "1" (regs), "2" (isp) + : "memory", "cc", "ecx" ); } else #endif - desc->handle_irq(irq, desc, regs); + __do_IRQ(irq, regs); irq_exit(); @@ -255,8 +253,7 @@ int show_interrupts(struct seq_file *p, void *v) for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif - seq_printf(p, " %8s", irq_desc[i].chip->name); - seq_printf(p, "-%s", handle_irq_name(irq_desc[i].handle_irq)); + seq_printf(p, " %14s", irq_desc[i].chip->typename); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) diff --git a/trunk/arch/i386/lib/semaphore.S b/trunk/arch/i386/lib/semaphore.S index 01f80b5c45d2..ef6ad9e1a609 100644 --- a/trunk/arch/i386/lib/semaphore.S +++ b/trunk/arch/i386/lib/semaphore.S @@ -13,7 +13,6 @@ * rw semaphores implemented November 1999 by Benjamin LaHaise */ -#include #include #include #include diff --git a/trunk/arch/i386/pci/irq.c b/trunk/arch/i386/pci/irq.c index 47f02af74be3..4a8995c9c762 100644 --- a/trunk/arch/i386/pci/irq.c +++ b/trunk/arch/i386/pci/irq.c @@ -981,6 +981,10 @@ static void __init pcibios_fixup_irqs(void) pci_name(bridge), 'A' + pin, irq); } if (irq >= 0) { + if (use_pci_vector() && + !platform_legacy_irq(irq)) + irq = IO_APIC_VECTOR(irq); + printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n", pci_name(dev), 'A' + pin, irq); dev->irq = irq; @@ -1165,3 +1169,33 @@ static int pirq_enable_irq(struct pci_dev *dev) } return 0; } + +int pci_vector_resources(int last, int nr_released) +{ + int count = nr_released; + + int next = last; + int offset = (last % 8); + + while (next < FIRST_SYSTEM_VECTOR) { + next += 8; +#ifdef CONFIG_X86_64 + if (next == IA32_SYSCALL_VECTOR) + continue; +#else + if (next == SYSCALL_VECTOR) + continue; +#endif + count++; + if (next >= FIRST_SYSTEM_VECTOR) { + if (offset%8) { + next = FIRST_DEVICE_VECTOR + offset; + offset++; + continue; + } + count--; + } + } + + return count; +} diff --git a/trunk/arch/ia64/kernel/Makefile b/trunk/arch/ia64/kernel/Makefile index cfa099b04cda..31497496eb4b 100644 --- a/trunk/arch/ia64/kernel/Makefile +++ b/trunk/arch/ia64/kernel/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_recovery.o obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o obj-$(CONFIG_AUDIT) += audit.o -obj-$(CONFIG_PCI_MSI) += msi_ia64.o mca_recovery-y += mca_drv.o mca_drv_asm.o obj-$(CONFIG_IA64_ESI) += esi.o diff --git a/trunk/arch/ia64/kernel/irq_ia64.c b/trunk/arch/ia64/kernel/irq_ia64.c index ab2d19c3661f..aafca18ab33b 100644 --- a/trunk/arch/ia64/kernel/irq_ia64.c +++ b/trunk/arch/ia64/kernel/irq_ia64.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -106,25 +105,6 @@ reserve_irq_vector (int vector) return test_and_set_bit(pos, ia64_vector_mask); } -/* - * Dynamic irq allocate and deallocation for MSI - */ -int create_irq(void) -{ - int vector = assign_irq_vector(AUTO_ASSIGN); - - if (vector >= 0) - dynamic_irq_init(vector); - - return vector; -} - -void destroy_irq(unsigned int irq) -{ - dynamic_irq_cleanup(irq); - free_irq_vector(irq); -} - #ifdef CONFIG_SMP # define IS_RESCHEDULE(vec) (vec == IA64_IPI_RESCHEDULE) #else diff --git a/trunk/arch/ia64/kernel/msi_ia64.c b/trunk/arch/ia64/kernel/msi_ia64.c deleted file mode 100644 index 822e59a1b822..000000000000 --- a/trunk/arch/ia64/kernel/msi_ia64.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * MSI hooks for standard x86 apic - */ - -#include -#include -#include -#include - -/* - * Shifts for APIC-based data - */ - -#define MSI_DATA_VECTOR_SHIFT 0 -#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) - -#define MSI_DATA_DELIVERY_SHIFT 8 -#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) -#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) - -#define MSI_DATA_LEVEL_SHIFT 14 -#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) -#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) - -#define MSI_DATA_TRIGGER_SHIFT 15 -#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) -#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) - -/* - * Shift/mask fields for APIC-based bus address - */ - -#define MSI_TARGET_CPU_SHIFT 4 -#define MSI_ADDR_HEADER 0xfee00000 - -#define MSI_ADDR_DESTID_MASK 0xfff0000f -#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) - -#define MSI_ADDR_DESTMODE_SHIFT 2 -#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) -#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) - -#define MSI_ADDR_REDIRECTION_SHIFT 3 -#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) -#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) - -static struct irq_chip ia64_msi_chip; - -#ifdef CONFIG_SMP -static void ia64_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) -{ - struct msi_msg msg; - u32 addr; - - read_msi_msg(irq, &msg); - - addr = msg.address_lo; - addr &= MSI_ADDR_DESTID_MASK; - addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(first_cpu(cpu_mask))); - msg.address_lo = addr; - - write_msi_msg(irq, &msg); - set_native_irq_info(irq, cpu_mask); -} -#endif /* CONFIG_SMP */ - -int ia64_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) -{ - struct msi_msg msg; - unsigned long dest_phys_id; - unsigned int vector; - - dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); - vector = irq; - - msg.address_hi = 0; - msg.address_lo = - MSI_ADDR_HEADER | - MSI_ADDR_DESTMODE_PHYS | - MSI_ADDR_REDIRECTION_CPU | - MSI_ADDR_DESTID_CPU(dest_phys_id); - - msg.data = - MSI_DATA_TRIGGER_EDGE | - MSI_DATA_LEVEL_ASSERT | - MSI_DATA_DELIVERY_FIXED | - MSI_DATA_VECTOR(vector); - - write_msi_msg(irq, &msg); - set_irq_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq); - - return 0; -} - -void ia64_teardown_msi_irq(unsigned int irq) -{ - return; /* no-op */ -} - -static void ia64_ack_msi_irq(unsigned int irq) -{ - move_native_irq(irq); - ia64_eoi(); -} - -static int ia64_msi_retrigger_irq(unsigned int irq) -{ - unsigned int vector = irq; - ia64_resend_irq(vector); - - return 1; -} - -/* - * Generic ops used on most IA64 platforms. - */ -static struct irq_chip ia64_msi_chip = { - .name = "PCI-MSI", - .mask = mask_msi_irq, - .unmask = unmask_msi_irq, - .ack = ia64_ack_msi_irq, -#ifdef CONFIG_SMP - .set_affinity = ia64_set_msi_irq_affinity, -#endif - .retrigger = ia64_msi_retrigger_irq, -}; - - -int arch_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) -{ - if (platform_setup_msi_irq) - return platform_setup_msi_irq(irq, pdev); - - return ia64_setup_msi_irq(irq, pdev); -} - -void arch_teardown_msi_irq(unsigned int irq) -{ - if (platform_teardown_msi_irq) - return platform_teardown_msi_irq(irq); - - return ia64_teardown_msi_irq(irq); -} diff --git a/trunk/arch/ia64/pci/pci.c b/trunk/arch/ia64/pci/pci.c index b30be7c48ba8..15c7c670da39 100644 --- a/trunk/arch/ia64/pci/pci.c +++ b/trunk/arch/ia64/pci/pci.c @@ -810,3 +810,12 @@ pcibios_prep_mwi (struct pci_dev *dev) } return rc; } + +int pci_vector_resources(int last, int nr_released) +{ + int count = nr_released; + + count += (IA64_LAST_DEVICE_VECTOR - last); + + return count; +} diff --git a/trunk/arch/ia64/sn/kernel/Makefile b/trunk/arch/ia64/sn/kernel/Makefile index 2d78f34dd763..ab9c48c88012 100644 --- a/trunk/arch/ia64/sn/kernel/Makefile +++ b/trunk/arch/ia64/sn/kernel/Makefile @@ -19,4 +19,3 @@ xp-y := xp_main.o xp_nofault.o obj-$(CONFIG_IA64_SGI_SN_XP) += xpc.o xpc-y := xpc_main.o xpc_channel.o xpc_partition.o obj-$(CONFIG_IA64_SGI_SN_XP) += xpnet.o -obj-$(CONFIG_PCI_MSI) += msi_sn.o diff --git a/trunk/arch/m32r/mm/mmu.S b/trunk/arch/m32r/mm/mmu.S index 0c28f11d6677..9a4d40b3d6a2 100644 --- a/trunk/arch/m32r/mm/mmu.S +++ b/trunk/arch/m32r/mm/mmu.S @@ -6,7 +6,6 @@ /* $Id: mmu.S,v 1.15 2004/03/16 02:56:27 takata Exp $ */ -#include /* CONFIG_MMU */ #include #include #include diff --git a/trunk/arch/m68k/kernel/time.c b/trunk/arch/m68k/kernel/time.c index 6cfc984380d9..28b2fefa4513 100644 --- a/trunk/arch/m68k/kernel/time.c +++ b/trunk/arch/m68k/kernel/time.c @@ -10,7 +10,6 @@ * "A Kernel Model for Precision Timekeeping" by Dave Mills */ -#include /* CONFIG_HEARTBEAT */ #include #include #include diff --git a/trunk/arch/m68knommu/platform/532x/config.c b/trunk/arch/m68knommu/platform/532x/config.c index ceef9bc181ea..c7d6ad513820 100644 --- a/trunk/arch/m68knommu/platform/532x/config.c +++ b/trunk/arch/m68knommu/platform/532x/config.c @@ -17,7 +17,6 @@ /***************************************************************************/ -#include #include #include #include diff --git a/trunk/arch/m68knommu/platform/68328/romvec.S b/trunk/arch/m68knommu/platform/68328/romvec.S index 3e7fe1e14913..31084466eae8 100644 --- a/trunk/arch/m68knommu/platform/68328/romvec.S +++ b/trunk/arch/m68knommu/platform/68328/romvec.S @@ -10,8 +10,6 @@ * Copyright 2006 Greg Ungerer */ -#include - .global _start .global _buserr .global trap diff --git a/trunk/arch/parisc/Kconfig b/trunk/arch/parisc/Kconfig index d2101237442e..6dd0ea8f88e0 100644 --- a/trunk/arch/parisc/Kconfig +++ b/trunk/arch/parisc/Kconfig @@ -127,7 +127,7 @@ config PA11 config PREFETCH def_bool y - depends on PA8X00 || PA7200 + depends on PA8X00 config 64BIT bool "64-bit kernel" diff --git a/trunk/arch/parisc/hpux/fs.c b/trunk/arch/parisc/hpux/fs.c index 2d58b92b57e3..6e79dbf3f6bd 100644 --- a/trunk/arch/parisc/hpux/fs.c +++ b/trunk/arch/parisc/hpux/fs.c @@ -96,7 +96,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, put_user(namlen, &dirent->d_namlen); copy_to_user(dirent->d_name, name, namlen); put_user(0, dirent->d_name + namlen); - dirent = (void __user *)dirent + reclen; + ((char *) dirent) += reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; diff --git a/trunk/arch/parisc/kernel/binfmt_elf32.c b/trunk/arch/parisc/kernel/binfmt_elf32.c index 1e64e7b88110..d1833f164bbe 100644 --- a/trunk/arch/parisc/kernel/binfmt_elf32.c +++ b/trunk/arch/parisc/kernel/binfmt_elf32.c @@ -87,7 +87,7 @@ struct elf_prpsinfo32 */ #define SET_PERSONALITY(ex, ibcs2) \ - set_thread_flag(TIF_32BIT); \ + current->personality = PER_LINUX32; \ current->thread.map_base = DEFAULT_MAP_BASE32; \ current->thread.task_size = DEFAULT_TASK_SIZE32 \ @@ -102,3 +102,25 @@ cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) } #include "../../../fs/binfmt_elf.c" + +/* Set up a separate execution domain for ELF32 binaries running + * on an ELF64 kernel */ + +static struct exec_domain parisc32_exec_domain = { + .name = "Linux/ELF32", + .pers_low = PER_LINUX32, + .pers_high = PER_LINUX32, +}; + +static int __init parisc32_exec_init(void) +{ + /* steal the identity signal mappings from the default domain */ + parisc32_exec_domain.signal_map = default_exec_domain.signal_map; + parisc32_exec_domain.signal_invmap = default_exec_domain.signal_invmap; + + register_exec_domain(&parisc32_exec_domain); + + return 0; +} + +__initcall(parisc32_exec_init); diff --git a/trunk/arch/parisc/kernel/cache.c b/trunk/arch/parisc/kernel/cache.c index 0be51e92a2fc..bc7c4a4e26a1 100644 --- a/trunk/arch/parisc/kernel/cache.c +++ b/trunk/arch/parisc/kernel/cache.c @@ -35,12 +35,15 @@ int icache_stride __read_mostly; EXPORT_SYMBOL(dcache_stride); +#if defined(CONFIG_SMP) /* On some machines (e.g. ones with the Merced bus), there can be * only a single PxTLB broadcast at a time; this must be guaranteed * by software. We put a spinlock around all TLB flushes to * ensure this. */ DEFINE_SPINLOCK(pa_tlb_lock); +EXPORT_SYMBOL(pa_tlb_lock); +#endif struct pdc_cache_info cache_info __read_mostly; #ifndef CONFIG_PA20 @@ -88,8 +91,7 @@ update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) flush_kernel_dcache_page(page); clear_bit(PG_dcache_dirty, &page->flags); - } else if (parisc_requires_coherency()) - flush_kernel_dcache_page(page); + } } void @@ -368,45 +370,3 @@ void parisc_setup_cache_timing(void) printk(KERN_INFO "Setting cache flush threshold to %x (%d CPUs online)\n", parisc_cache_flush_threshold, num_online_cpus()); } - -extern void purge_kernel_dcache_page(unsigned long); -extern void clear_user_page_asm(void *page, unsigned long vaddr); - -void clear_user_page(void *page, unsigned long vaddr, struct page *pg) -{ - purge_kernel_dcache_page((unsigned long)page); - purge_tlb_start(); - pdtlb_kernel(page); - purge_tlb_end(); - clear_user_page_asm(page, vaddr); -} -EXPORT_SYMBOL(clear_user_page); - -void flush_kernel_dcache_page_addr(void *addr) -{ - flush_kernel_dcache_page_asm(addr); - purge_tlb_start(); - pdtlb_kernel(addr); - purge_tlb_end(); -} -EXPORT_SYMBOL(flush_kernel_dcache_page_addr); - -void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *pg) -{ - /* no coherency needed (all in kmap/kunmap) */ - copy_user_page_asm(vto, vfrom); - if (!parisc_requires_coherency()) - flush_kernel_dcache_page_asm(vto); -} -EXPORT_SYMBOL(copy_user_page); - -#ifdef CONFIG_PA8X00 - -void kunmap_parisc(void *addr) -{ - if (parisc_requires_coherency()) - flush_kernel_dcache_page_addr(addr); -} -EXPORT_SYMBOL(kunmap_parisc); -#endif diff --git a/trunk/arch/parisc/kernel/entry.S b/trunk/arch/parisc/kernel/entry.S index 340b5e8d67ba..192357a3b9fe 100644 --- a/trunk/arch/parisc/kernel/entry.S +++ b/trunk/arch/parisc/kernel/entry.S @@ -30,7 +30,6 @@ #include -#include /* for L1_CACHE_SHIFT */ #include /* for LDREG/STREG defines */ #include #include @@ -479,7 +478,11 @@ bb,>=,n \pmd,_PxD_PRESENT_BIT,\fault DEP %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */ copy \pmd,%r9 - SHLREG %r9,PxD_VALUE_SHIFT,\pmd +#ifdef CONFIG_64BIT + shld %r9,PxD_VALUE_SHIFT,\pmd +#else + shlw %r9,PxD_VALUE_SHIFT,\pmd +#endif EXTR \va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index DEP %r0,31,PAGE_SHIFT,\pmd /* clear offset */ shladd \index,BITS_PER_PTE_ENTRY,\pmd,\pmd @@ -967,7 +970,11 @@ intr_return: /* shift left ____cacheline_aligned (aka L1_CACHE_BYTES) amount ** irq_stat[] is defined using ____cacheline_aligned. */ - SHLREG %r1,L1_CACHE_SHIFT,%r20 +#ifdef CONFIG_64BIT + shld %r1, 6, %r20 +#else + shlw %r1, 5, %r20 +#endif add %r19,%r20,%r19 /* now have &irq_stat[smp_processor_id()] */ #endif /* CONFIG_SMP */ @@ -1069,7 +1076,7 @@ intr_do_preempt: BL preempt_schedule_irq, %r2 nop - b,n intr_restore /* ssm PSW_SM_I done by intr_restore */ + b intr_restore /* ssm PSW_SM_I done by intr_restore */ #endif /* CONFIG_PREEMPT */ .import do_signal,code @@ -2108,7 +2115,11 @@ syscall_check_bh: ldw TI_CPU-THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r26 /* cpu # */ /* shift left ____cacheline_aligned (aka L1_CACHE_BYTES) bits */ - SHLREG %r26,L1_CACHE_SHIFT,%r20 +#ifdef CONFIG_64BIT + shld %r26, 6, %r20 +#else + shlw %r26, 5, %r20 +#endif add %r19,%r20,%r19 /* now have &irq_stat[smp_processor_id()] */ #endif /* CONFIG_SMP */ diff --git a/trunk/arch/parisc/kernel/hardware.c b/trunk/arch/parisc/kernel/hardware.c index 18ba4cb9159b..3058bffd8a2c 100644 --- a/trunk/arch/parisc/kernel/hardware.c +++ b/trunk/arch/parisc/kernel/hardware.c @@ -231,7 +231,6 @@ static struct hp_hardware hp_hardware_list[] __initdata = { {HPHW_NPROC,0x5E6,0x4,0x91,"Keystone/Matterhorn W2 650"}, {HPHW_NPROC,0x5E7,0x4,0x91,"Caribe W2 800"}, {HPHW_NPROC,0x5E8,0x4,0x91,"Pikes Peak W2"}, - {HPHW_NPROC,0x5EB,0x4,0x91,"Perf/Leone 875 W2+"}, {HPHW_NPROC,0x5FF,0x4,0x91,"Hitachi W"}, {HPHW_NPROC,0x600,0x4,0x81,"Gecko (712/60)"}, {HPHW_NPROC,0x601,0x4,0x81,"Gecko 80 (712/80)"}, @@ -585,10 +584,8 @@ static struct hp_hardware hp_hardware_list[] __initdata = { {HPHW_CONSOLE, 0x01A, 0x0001F, 0x00, "Jason/Anole 64 Null Console"}, {HPHW_CONSOLE, 0x01B, 0x0001F, 0x00, "Jason/Anole 100 Null Console"}, {HPHW_FABRIC, 0x004, 0x000AA, 0x80, "Halfdome DNA Central Agent"}, - {HPHW_FABRIC, 0x005, 0x000AA, 0x80, "Keystone DNA Central Agent"}, {HPHW_FABRIC, 0x007, 0x000AA, 0x80, "Caribe DNA Central Agent"}, {HPHW_FABRIC, 0x004, 0x000AB, 0x00, "Halfdome TOGO Fabric Crossbar"}, - {HPHW_FABRIC, 0x005, 0x000AB, 0x00, "Keystone TOGO Fabric Crossbar"}, {HPHW_FABRIC, 0x004, 0x000AC, 0x00, "Halfdome Sakura Fabric Router"}, {HPHW_FIO, 0x025, 0x0002E, 0x80, "Armyknife Optional X.25"}, {HPHW_FIO, 0x004, 0x0004F, 0x0, "8-Port X.25 EISA-ACC (AMSO)"}, diff --git a/trunk/arch/parisc/kernel/head.S b/trunk/arch/parisc/kernel/head.S index 3e79e62f7b0b..eaad2328fea1 100644 --- a/trunk/arch/parisc/kernel/head.S +++ b/trunk/arch/parisc/kernel/head.S @@ -12,8 +12,6 @@ * Initial Version 04-23-1999 by Helge Deller */ -#include /* for CONFIG_SMP */ - #include #include #include diff --git a/trunk/arch/parisc/kernel/irq.c b/trunk/arch/parisc/kernel/irq.c index 9bdd0197ceb7..5b8803cc3d69 100644 --- a/trunk/arch/parisc/kernel/irq.c +++ b/trunk/arch/parisc/kernel/irq.c @@ -45,17 +45,6 @@ extern irqreturn_t ipi_interrupt(int, void *, struct pt_regs *); */ static volatile unsigned long cpu_eiem = 0; -/* -** ack bitmap ... habitually set to 1, but reset to zero -** between ->ack() and ->end() of the interrupt to prevent -** re-interruption of a processing interrupt. -*/ -static volatile unsigned long global_ack_eiem = ~0UL; -/* -** Local bitmap, same as above but for per-cpu interrupts -*/ -static DEFINE_PER_CPU(unsigned long, local_ack_eiem) = ~0UL; - static void cpu_disable_irq(unsigned int irq) { unsigned long eirr_bit = EIEM_MASK(irq); @@ -73,6 +62,13 @@ static void cpu_enable_irq(unsigned int irq) cpu_eiem |= eirr_bit; + /* FIXME: while our interrupts aren't nested, we cannot reset + * the eiem mask if we're already in an interrupt. Once we + * implement nested interrupts, this can go away + */ + if (!in_interrupt()) + set_eiem(cpu_eiem); + /* This is just a simple NOP IPI. But what it does is cause * all the other CPUs to do a set_eiem(cpu_eiem) at the end * of the interrupt handler */ @@ -88,45 +84,13 @@ static unsigned int cpu_startup_irq(unsigned int irq) void no_ack_irq(unsigned int irq) { } void no_end_irq(unsigned int irq) { } -void cpu_ack_irq(unsigned int irq) -{ - unsigned long mask = EIEM_MASK(irq); - int cpu = smp_processor_id(); - - /* Clear in EIEM so we can no longer process */ - if (CHECK_IRQ_PER_CPU(irq_desc[irq].status)) - per_cpu(local_ack_eiem, cpu) &= ~mask; - else - global_ack_eiem &= ~mask; - - /* disable the interrupt */ - set_eiem(cpu_eiem & global_ack_eiem & per_cpu(local_ack_eiem, cpu)); - /* and now ack it */ - mtctl(mask, 23); -} - -void cpu_end_irq(unsigned int irq) -{ - unsigned long mask = EIEM_MASK(irq); - int cpu = smp_processor_id(); - - /* set it in the eiems---it's no longer in process */ - if (CHECK_IRQ_PER_CPU(irq_desc[irq].status)) - per_cpu(local_ack_eiem, cpu) |= mask; - else - global_ack_eiem |= mask; - - /* enable the interrupt */ - set_eiem(cpu_eiem & global_ack_eiem & per_cpu(local_ack_eiem, cpu)); -} - #ifdef CONFIG_SMP int cpu_check_affinity(unsigned int irq, cpumask_t *dest) { int cpu_dest; /* timer and ipi have to always be received on all CPUs */ - if (CHECK_IRQ_PER_CPU(irq)) { + if (irq == TIMER_IRQ || irq == IPI_IRQ) { /* Bad linux design decision. The mask has already * been set; we must reset it */ irq_desc[irq].affinity = CPU_MASK_ALL; @@ -155,8 +119,8 @@ static struct hw_interrupt_type cpu_interrupt_type = { .shutdown = cpu_disable_irq, .enable = cpu_enable_irq, .disable = cpu_disable_irq, - .ack = cpu_ack_irq, - .end = cpu_end_irq, + .ack = no_ack_irq, + .end = no_end_irq, #ifdef CONFIG_SMP .set_affinity = cpu_set_affinity_irq, #endif @@ -245,7 +209,7 @@ int show_interrupts(struct seq_file *p, void *v) ** Then use that to get the Transaction address and data. */ -int cpu_claim_irq(unsigned int irq, struct irq_chip *type, void *data) +int cpu_claim_irq(unsigned int irq, struct hw_interrupt_type *type, void *data) { if (irq_desc[irq].action) return -EBUSY; @@ -334,69 +298,82 @@ unsigned int txn_alloc_data(unsigned int virt_irq) return virt_irq - CPU_IRQ_BASE; } -static inline int eirr_to_irq(unsigned long eirr) -{ -#ifdef CONFIG_64BIT - int bit = fls64(eirr); -#else - int bit = fls(eirr); -#endif - return (BITS_PER_LONG - bit) + TIMER_IRQ; -} - /* ONLY called from entry.S:intr_extint() */ void do_cpu_irq_mask(struct pt_regs *regs) { unsigned long eirr_val; - int irq, cpu = smp_processor_id(); -#ifdef CONFIG_SMP - cpumask_t dest; -#endif - local_irq_disable(); irq_enter(); - eirr_val = mfctl(23) & cpu_eiem & global_ack_eiem & - per_cpu(local_ack_eiem, cpu); - if (!eirr_val) - goto set_out; - irq = eirr_to_irq(eirr_val); + /* + * Don't allow TIMER or IPI nested interrupts. + * Allowing any single interrupt to nest can lead to that CPU + * handling interrupts with all enabled interrupts unmasked. + */ + set_eiem(0UL); + /* 1) only process IRQs that are enabled/unmasked (cpu_eiem) + * 2) We loop here on EIRR contents in order to avoid + * nested interrupts or having to take another interrupt + * when we could have just handled it right away. + */ + for (;;) { + unsigned long bit = (1UL << (BITS_PER_LONG - 1)); + unsigned int irq; + eirr_val = mfctl(23) & cpu_eiem; + if (!eirr_val) + break; + + mtctl(eirr_val, 23); /* reset bits we are going to process */ + + /* Work our way from MSb to LSb...same order we alloc EIRs */ + for (irq = TIMER_IRQ; eirr_val && bit; bit>>=1, irq++) { #ifdef CONFIG_SMP - dest = irq_desc[irq].affinity; - if (CHECK_IRQ_PER_CPU(irq_desc[irq].status) && - !cpu_isset(smp_processor_id(), dest)) { - int cpu = first_cpu(dest); - - printk(KERN_DEBUG "redirecting irq %d from CPU %d to %d\n", - irq, smp_processor_id(), cpu); - gsc_writel(irq + CPU_IRQ_BASE, - cpu_data[cpu].hpa); - goto set_out; - } + cpumask_t dest = irq_desc[irq].affinity; #endif - __do_IRQ(irq, regs); + if (!(bit & eirr_val)) + continue; - out: - irq_exit(); - return; + /* clear bit in mask - can exit loop sooner */ + eirr_val &= ~bit; - set_out: - set_eiem(cpu_eiem & global_ack_eiem & per_cpu(local_ack_eiem, cpu)); - goto out; +#ifdef CONFIG_SMP + /* FIXME: because generic set affinity mucks + * with the affinity before sending it to us + * we can get the situation where the affinity is + * wrong for our CPU type interrupts */ + if (irq != TIMER_IRQ && irq != IPI_IRQ && + !cpu_isset(smp_processor_id(), dest)) { + int cpu = first_cpu(dest); + + printk(KERN_DEBUG "redirecting irq %d from CPU %d to %d\n", + irq, smp_processor_id(), cpu); + gsc_writel(irq + CPU_IRQ_BASE, + cpu_data[cpu].hpa); + continue; + } +#endif + + __do_IRQ(irq, regs); + } + } + + set_eiem(cpu_eiem); /* restore original mask */ + irq_exit(); } + static struct irqaction timer_action = { .handler = timer_interrupt, .name = "timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_PERCPU, + .flags = IRQF_DISABLED, }; #ifdef CONFIG_SMP static struct irqaction ipi_action = { .handler = ipi_interrupt, .name = "IPI", - .flags = IRQF_DISABLED | IRQF_PERCPU, + .flags = IRQF_DISABLED, }; #endif diff --git a/trunk/arch/parisc/kernel/processor.c b/trunk/arch/parisc/kernel/processor.c index fb81e5687e7c..99d7fca93104 100644 --- a/trunk/arch/parisc/kernel/processor.c +++ b/trunk/arch/parisc/kernel/processor.c @@ -143,9 +143,8 @@ static int __init processor_probe(struct parisc_device *dev) p = &cpu_data[cpuid]; boot_cpu_data.cpu_count++; - /* initialize counters - CPU 0 gets it_value set in time_init() */ - if (cpuid) - memset(p, 0, sizeof(struct cpuinfo_parisc)); + /* initialize counters */ + memset(p, 0, sizeof(struct cpuinfo_parisc)); p->loops_per_jiffy = loops_per_jiffy; p->dev = dev; /* Save IODC data in case we need it */ diff --git a/trunk/arch/parisc/kernel/signal.c b/trunk/arch/parisc/kernel/signal.c index ee6653edeb7a..bb83880c5ee3 100644 --- a/trunk/arch/parisc/kernel/signal.c +++ b/trunk/arch/parisc/kernel/signal.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -432,13 +433,13 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, if (in_syscall) { regs->gr[31] = haddr; #ifdef __LP64__ - if (!test_thread_flag(TIF_32BIT)) + if (personality(current->personality) == PER_LINUX) sigframe_size |= 1; #endif } else { unsigned long psw = USER_PSW; #ifdef __LP64__ - if (!test_thread_flag(TIF_32BIT)) + if (personality(current->personality) == PER_LINUX) psw |= PSW_W; #endif diff --git a/trunk/arch/parisc/kernel/smp.c b/trunk/arch/parisc/kernel/smp.c index faad338f310e..98e40959a564 100644 --- a/trunk/arch/parisc/kernel/smp.c +++ b/trunk/arch/parisc/kernel/smp.c @@ -262,9 +262,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs) this_cpu, which); return IRQ_NONE; } /* Switch */ - /* let in any pending interrupts */ - local_irq_enable(); - local_irq_disable(); } /* while (ops) */ } return IRQ_HANDLED; @@ -433,9 +430,8 @@ smp_do_timer(struct pt_regs *regs) static void __init smp_cpu_init(int cpunum) { - extern int init_per_cpu(int); /* arch/parisc/kernel/processor.c */ + extern int init_per_cpu(int); /* arch/parisc/kernel/setup.c */ extern void init_IRQ(void); /* arch/parisc/kernel/irq.c */ - extern void start_cpu_itimer(void); /* arch/parisc/kernel/time.c */ /* Set modes and Enable floating point coprocessor */ (void) init_per_cpu(cpunum); @@ -461,7 +457,6 @@ smp_cpu_init(int cpunum) enter_lazy_tlb(&init_mm, current); init_IRQ(); /* make sure no IRQ's are enabled or pending */ - start_cpu_itimer(); } diff --git a/trunk/arch/parisc/kernel/sys_parisc.c b/trunk/arch/parisc/kernel/sys_parisc.c index 1db5588ceacf..8b5df98e2b31 100644 --- a/trunk/arch/parisc/kernel/sys_parisc.c +++ b/trunk/arch/parisc/kernel/sys_parisc.c @@ -31,8 +31,6 @@ #include #include #include -#include -#include int sys_pipe(int __user *fildes) { @@ -250,46 +248,3 @@ asmlinkage int sys_free_hugepages(unsigned long addr) { return -EINVAL; } - -long parisc_personality(unsigned long personality) -{ - long err; - - if (personality(current->personality) == PER_LINUX32 - && personality == PER_LINUX) - personality = PER_LINUX32; - - err = sys_personality(personality); - if (err == PER_LINUX32) - err = PER_LINUX; - - return err; -} - -static inline int override_machine(char __user *mach) { -#ifdef CONFIG_COMPAT - if (personality(current->personality) == PER_LINUX32) { - if (__put_user(0, mach + 6) || - __put_user(0, mach + 7)) - return -EFAULT; - } - - return 0; -#else /*!CONFIG_COMPAT*/ - return 0; -#endif /*CONFIG_COMPAT*/ -} - -long parisc_newuname(struct new_utsname __user *utsname) -{ - int err = 0; - - down_read(&uts_sem); - if (copy_to_user(utsname, &system_utsname, sizeof(*utsname))) - err = -EFAULT; - up_read(&uts_sem); - - err = override_machine(utsname->machine); - - return (long)err; -} diff --git a/trunk/arch/parisc/kernel/syscall.S b/trunk/arch/parisc/kernel/syscall.S index 9670a89c77fe..a05800429304 100644 --- a/trunk/arch/parisc/kernel/syscall.S +++ b/trunk/arch/parisc/kernel/syscall.S @@ -6,7 +6,6 @@ * thanks to Philipp Rumpf, Mike Shaver and various others * sorry about the wall, puffin.. */ -#include /* for CONFIG_SMP */ #include #include diff --git a/trunk/arch/parisc/kernel/syscall_table.S b/trunk/arch/parisc/kernel/syscall_table.S index 701d66a596e8..e27b432f90a8 100644 --- a/trunk/arch/parisc/kernel/syscall_table.S +++ b/trunk/arch/parisc/kernel/syscall_table.S @@ -132,7 +132,7 @@ ENTRY_SAME(socketpair) ENTRY_SAME(setpgid) ENTRY_SAME(send) - ENTRY_OURS(newuname) + ENTRY_SAME(newuname) ENTRY_SAME(umask) /* 60 */ ENTRY_SAME(chroot) ENTRY_SAME(ustat) @@ -221,7 +221,7 @@ ENTRY_SAME(fchdir) ENTRY_SAME(bdflush) ENTRY_SAME(sysfs) /* 135 */ - ENTRY_OURS(personality) + ENTRY_SAME(personality) ENTRY_SAME(ni_syscall) /* for afs_syscall */ ENTRY_SAME(setfsuid) ENTRY_SAME(setfsgid) diff --git a/trunk/arch/parisc/kernel/time.c b/trunk/arch/parisc/kernel/time.c index b3496b592a2d..ab641d67f551 100644 --- a/trunk/arch/parisc/kernel/time.c +++ b/trunk/arch/parisc/kernel/time.c @@ -32,7 +32,8 @@ #include -static unsigned long clocktick __read_mostly; /* timer cycles per tick */ +static long clocktick __read_mostly; /* timer cycles per tick */ +static long halftick __read_mostly; #ifdef CONFIG_SMP extern void smp_do_timer(struct pt_regs *regs); @@ -40,106 +41,46 @@ extern void smp_do_timer(struct pt_regs *regs); irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - unsigned long now; - unsigned long next_tick; - unsigned long cycles_elapsed; - unsigned long cycles_remainder; - unsigned int cpu = smp_processor_id(); - - /* gcc can optimize for "read-only" case with a local clocktick */ - unsigned long cpt = clocktick; + long now; + long next_tick; + int nticks; + int cpu = smp_processor_id(); profile_tick(CPU_PROFILING, regs); - /* Initialize next_tick to the expected tick time. */ - next_tick = cpu_data[cpu].it_value; - - /* Get current interval timer. - * CR16 reads as 64 bits in CPU wide mode. - * CR16 reads as 32 bits in CPU narrow mode. - */ now = mfctl(16); + /* initialize next_tick to time at last clocktick */ + next_tick = cpu_data[cpu].it_value; - cycles_elapsed = now - next_tick; - - if ((cycles_elapsed >> 5) < cpt) { - /* use "cheap" math (add/subtract) instead - * of the more expensive div/mul method - */ - cycles_remainder = cycles_elapsed; - while (cycles_remainder > cpt) { - cycles_remainder -= cpt; - } - } else { - cycles_remainder = cycles_elapsed % cpt; - } - - /* Can we differentiate between "early CR16" (aka Scenario 1) and - * "long delay" (aka Scenario 3)? I don't think so. + /* since time passes between the interrupt and the mfctl() + * above, it is never true that last_tick + clocktick == now. If we + * never miss a clocktick, we could set next_tick = last_tick + clocktick + * but maybe we'll miss ticks, hence the loop. * - * We expected timer_interrupt to be delivered at least a few hundred - * cycles after the IT fires. But it's arbitrary how much time passes - * before we call it "late". I've picked one second. + * Variables are *signed*. */ -/* aproximate HZ with shifts. Intended math is "(elapsed/clocktick) > HZ" */ -#if HZ == 1000 - if (cycles_elapsed > (cpt << 10) ) -#elif HZ == 250 - if (cycles_elapsed > (cpt << 8) ) -#elif HZ == 100 - if (cycles_elapsed > (cpt << 7) ) -#else -#warn WTF is HZ set to anyway? - if (cycles_elapsed > (HZ * cpt) ) -#endif - { - /* Scenario 3: very long delay? bad in any case */ - printk (KERN_CRIT "timer_interrupt(CPU %d): delayed!" - " cycles %lX rem %lX " - " next/now %lX/%lX\n", - cpu, - cycles_elapsed, cycles_remainder, - next_tick, now ); - } - - /* convert from "division remainder" to "remainder of clock tick" */ - cycles_remainder = cpt - cycles_remainder; - - /* Determine when (in CR16 cycles) next IT interrupt will fire. - * We want IT to fire modulo clocktick even if we miss/skip some. - * But those interrupts don't in fact get delivered that regularly. - */ - next_tick = now + cycles_remainder; - cpu_data[cpu].it_value = next_tick; - - /* Skip one clocktick on purpose if we are likely to miss next_tick. - * We want to avoid the new next_tick being less than CR16. - * If that happened, itimer wouldn't fire until CR16 wrapped. - * We'll catch the tick we missed on the tick after that. - */ - if (!(cycles_remainder >> 13)) - next_tick += cpt; - - /* Program the IT when to deliver the next interrupt. */ - /* Only bottom 32-bits of next_tick are written to cr16. */ + nticks = 0; + while((next_tick - now) < halftick) { + next_tick += clocktick; + nticks++; + } mtctl(next_tick, 16); + cpu_data[cpu].it_value = next_tick; - - /* Done mucking with unreliable delivery of interrupts. - * Go do system house keeping. - */ + while (nticks--) { #ifdef CONFIG_SMP - smp_do_timer(regs); + smp_do_timer(regs); #else - update_process_times(user_mode(regs)); + update_process_times(user_mode(regs)); #endif - if (cpu == 0) { - write_seqlock(&xtime_lock); - do_timer(regs); - write_sequnlock(&xtime_lock); + if (cpu == 0) { + write_seqlock(&xtime_lock); + do_timer(1); + write_sequnlock(&xtime_lock); + } } - + /* check soft power switch status */ if (cpu == 0 && !atomic_read(&power_tasklet.count)) tasklet_schedule(&power_tasklet); @@ -165,12 +106,14 @@ unsigned long profile_pc(struct pt_regs *regs) EXPORT_SYMBOL(profile_pc); +/*** converted from ia64 ***/ /* * Return the number of micro-seconds that elapsed since the last * update to wall time (aka xtime). The xtime_lock * must be at least read-locked when calling this routine. */ -static inline unsigned long gettimeoffset (void) +static inline unsigned long +gettimeoffset (void) { #ifndef CONFIG_SMP /* @@ -178,44 +121,21 @@ static inline unsigned long gettimeoffset (void) * Once parisc-linux learns the cr16 difference between processors, * this could be made to work. */ - unsigned long now; - unsigned long prev_tick; - unsigned long next_tick; - unsigned long elapsed_cycles; - unsigned long usec; - unsigned long cpuid = smp_processor_id(); - unsigned long cpt = clocktick; - - next_tick = cpu_data[cpuid].it_value; - now = mfctl(16); /* Read the hardware interval timer. */ - - prev_tick = next_tick - cpt; - - /* Assume Scenario 1: "now" is later than prev_tick. */ - elapsed_cycles = now - prev_tick; - -/* aproximate HZ with shifts. Intended math is "(elapsed/clocktick) > HZ" */ -#if HZ == 1000 - if (elapsed_cycles > (cpt << 10) ) -#elif HZ == 250 - if (elapsed_cycles > (cpt << 8) ) -#elif HZ == 100 - if (elapsed_cycles > (cpt << 7) ) -#else -#warn WTF is HZ set to anyway? - if (elapsed_cycles > (HZ * cpt) ) -#endif - { - /* Scenario 3: clock ticks are missing. */ - printk (KERN_CRIT "gettimeoffset(CPU %ld): missing %ld ticks!" - " cycles %lX prev/now/next %lX/%lX/%lX clock %lX\n", - cpuid, elapsed_cycles / cpt, - elapsed_cycles, prev_tick, now, next_tick, cpt); - } + long last_tick; + long elapsed_cycles; + + /* it_value is the intended time of the next tick */ + last_tick = cpu_data[smp_processor_id()].it_value; - /* FIXME: Can we improve the precision? Not with PAGE0. */ - usec = (elapsed_cycles * 10000) / PAGE0->mem_10msec; - return usec; + /* Subtract one tick and account for possible difference between + * when we expected the tick and when it actually arrived. + * (aka wall vs real) + */ + last_tick -= clocktick * (jiffies - wall_jiffies + 1); + elapsed_cycles = mfctl(16) - last_tick; + + /* the precision of this math could be improved */ + return elapsed_cycles / (PAGE0->mem_10msec / 10000); #else return 0; #endif @@ -226,7 +146,6 @@ do_gettimeofday (struct timeval *tv) { unsigned long flags, seq, usec, sec; - /* Hold xtime_lock and adjust timeval. */ do { seq = read_seqbegin_irqsave(&xtime_lock, flags); usec = gettimeoffset(); @@ -234,13 +153,25 @@ do_gettimeofday (struct timeval *tv) usec += (xtime.tv_nsec / 1000); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); - /* Move adjusted usec's into sec's. */ + if (unlikely(usec > LONG_MAX)) { + /* This can happen if the gettimeoffset adjustment is + * negative and xtime.tv_nsec is smaller than the + * adjustment */ + printk(KERN_ERR "do_gettimeofday() spurious xtime.tv_nsec of %ld\n", usec); + usec += USEC_PER_SEC; + --sec; + /* This should never happen, it means the negative + * time adjustment was more than a second, so there's + * something seriously wrong */ + BUG_ON(usec > LONG_MAX); + } + + while (usec >= USEC_PER_SEC) { usec -= USEC_PER_SEC; ++sec; } - /* Return adjusted result. */ tv->tv_sec = sec; tv->tv_usec = usec; } @@ -292,23 +223,22 @@ unsigned long long sched_clock(void) } -void __init start_cpu_itimer(void) -{ - unsigned int cpu = smp_processor_id(); - unsigned long next_tick = mfctl(16) + clocktick; - - mtctl(next_tick, 16); /* kick off Interval Timer (CR16) */ - - cpu_data[cpu].it_value = next_tick; -} - void __init time_init(void) { + unsigned long next_tick; static struct pdc_tod tod_data; clocktick = (100 * PAGE0->mem_10msec) / HZ; + halftick = clocktick / 2; - start_cpu_itimer(); /* get CPU 0 started */ + /* Setup clock interrupt timing */ + + next_tick = mfctl(16); + next_tick += clocktick; + cpu_data[smp_processor_id()].it_value = next_tick; + + /* kick off Itimer (CR16) */ + mtctl(next_tick, 16); if(pdc_tod_read(&tod_data) == 0) { write_seqlock_irq(&xtime_lock); diff --git a/trunk/arch/parisc/kernel/traps.c b/trunk/arch/parisc/kernel/traps.c index 65cd6ca32fed..77b28cb8aca6 100644 --- a/trunk/arch/parisc/kernel/traps.c +++ b/trunk/arch/parisc/kernel/traps.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -246,15 +245,6 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) current->comm, current->pid, str, err); show_regs(regs); - if (in_interrupt()) - panic("Fatal exception in interrupt"); - - if (panic_on_oops) { - printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); - ssleep(5); - panic("Fatal exception"); - } - /* Wot's wrong wif bein' racy? */ if (current->thread.flags & PARISC_KERNEL_DEATH) { printk(KERN_CRIT "%s() recursion detected.\n", __FUNCTION__); diff --git a/trunk/arch/parisc/mm/init.c b/trunk/arch/parisc/mm/init.c index 0667f2b4f977..25ad28d63e88 100644 --- a/trunk/arch/parisc/mm/init.c +++ b/trunk/arch/parisc/mm/init.c @@ -31,7 +31,10 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); +extern char _text; /* start of kernel code, defined by linker */ extern int data_start; +extern char _end; /* end of BSS, defined by linker */ +extern char __init_begin, __init_end; #ifdef CONFIG_DISCONTIGMEM struct node_map_data node_data[MAX_NUMNODES] __read_mostly; @@ -316,8 +319,8 @@ static void __init setup_bootmem(void) reserve_bootmem_node(NODE_DATA(0), 0UL, (unsigned long)(PAGE0->mem_free + PDC_CONSOLE_IO_IODC_SIZE)); - reserve_bootmem_node(NODE_DATA(0), __pa((unsigned long)_text), - (unsigned long)(_end - _text)); + reserve_bootmem_node(NODE_DATA(0),__pa((unsigned long)&_text), + (unsigned long)(&_end - &_text)); reserve_bootmem_node(NODE_DATA(0), (bootmap_start_pfn << PAGE_SHIFT), ((bootmap_pfn - bootmap_start_pfn) << PAGE_SHIFT)); @@ -352,8 +355,8 @@ static void __init setup_bootmem(void) #endif data_resource.start = virt_to_phys(&data_start); - data_resource.end = virt_to_phys(_end) - 1; - code_resource.start = virt_to_phys(_text); + data_resource.end = virt_to_phys(&_end)-1; + code_resource.start = virt_to_phys(&_text); code_resource.end = virt_to_phys(&data_start)-1; /* We don't know which region the kernel will be in, so try @@ -382,12 +385,12 @@ void free_initmem(void) */ local_irq_disable(); - memset(__init_begin, 0x00, - (unsigned long)__init_end - (unsigned long)__init_begin); + memset(&__init_begin, 0x00, + (unsigned long)&__init_end - (unsigned long)&__init_begin); flush_data_cache(); asm volatile("sync" : : ); - flush_icache_range((unsigned long)__init_begin, (unsigned long)__init_end); + flush_icache_range((unsigned long)&__init_begin, (unsigned long)&__init_end); asm volatile("sync" : : ); local_irq_enable(); @@ -395,8 +398,8 @@ void free_initmem(void) /* align __init_begin and __init_end to page size, ignoring linker script where we might have tried to save RAM */ - init_begin = PAGE_ALIGN((unsigned long)(__init_begin)); - init_end = PAGE_ALIGN((unsigned long)(__init_end)); + init_begin = PAGE_ALIGN((unsigned long)(&__init_begin)); + init_end = PAGE_ALIGN((unsigned long)(&__init_end)); for (addr = init_begin; addr < init_end; addr += PAGE_SIZE) { ClearPageReserved(virt_to_page(addr)); init_page_count(virt_to_page(addr)); @@ -575,7 +578,7 @@ static void __init map_pages(unsigned long start_vaddr, unsigned long start_padd extern const unsigned long fault_vector_20; extern void * const linux_gateway_page; - ro_start = __pa((unsigned long)_text); + ro_start = __pa((unsigned long)&_text); ro_end = __pa((unsigned long)&data_start); fv_addr = __pa((unsigned long)&fault_vector_20) & PAGE_MASK; gw_addr = __pa((unsigned long)&linux_gateway_page) & PAGE_MASK; diff --git a/trunk/arch/parisc/mm/ioremap.c b/trunk/arch/parisc/mm/ioremap.c index 47a1d2ac9419..27384567a1d0 100644 --- a/trunk/arch/parisc/mm/ioremap.c +++ b/trunk/arch/parisc/mm/ioremap.c @@ -188,7 +188,7 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l } EXPORT_SYMBOL(__ioremap); -void iounmap(const volatile void __iomem *addr) +void iounmap(void __iomem *addr) { if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long __force) addr)); diff --git a/trunk/arch/powerpc/Kconfig b/trunk/arch/powerpc/Kconfig index 8b6910465578..96ef656e4669 100644 --- a/trunk/arch/powerpc/Kconfig +++ b/trunk/arch/powerpc/Kconfig @@ -338,6 +338,10 @@ config PPC_MULTIPLATFORM RS/6000 machine, an Apple machine, or a PReP, CHRP, Maple or Cell-based machine. +config PPC_ISERIES + bool "IBM Legacy iSeries" + depends on PPC64 + config EMBEDDED6xx bool "Embedded 6xx/7xx/7xxx-based board" depends on PPC32 && (BROKEN||BROKEN_ON_SMP) @@ -351,16 +355,6 @@ config APUS . endchoice -config QUICC_ENGINE - bool - depends on PPC_MPC836x || PPC_MPC832x - default y - help - The QUICC Engine (QE) is a new generation of communications - coprocessors on Freescale embedded CPUs (akin to CPM in older chips). - Selecting this option means that you wish to build a kernel - for a machine with a QE coprocessor. - config PPC_PSERIES depends on PPC_MULTIPLATFORM && PPC64 bool "IBM pSeries & new (POWER5-based) iSeries" @@ -371,10 +365,6 @@ config PPC_PSERIES select PPC_UDBG_16550 default y -config PPC_ISERIES - bool "IBM Legacy iSeries" - depends on PPC_MULTIPLATFORM && PPC64 - config PPC_CHRP bool "Common Hardware Reference Platform (CHRP) based machines" depends on PPC_MULTIPLATFORM && PPC32 @@ -604,7 +594,6 @@ endmenu source arch/powerpc/platforms/embedded6xx/Kconfig source arch/powerpc/platforms/4xx/Kconfig -source arch/powerpc/platforms/82xx/Kconfig source arch/powerpc/platforms/83xx/Kconfig source arch/powerpc/platforms/85xx/Kconfig source arch/powerpc/platforms/86xx/Kconfig @@ -1069,8 +1058,6 @@ source "fs/Kconfig" # XXX source "arch/ppc/8260_io/Kconfig" -source "arch/powerpc/sysdev/qe_lib/Kconfig" - source "arch/powerpc/platforms/iseries/Kconfig" source "lib/Kconfig" diff --git a/trunk/arch/powerpc/boot/Makefile b/trunk/arch/powerpc/boot/Makefile index 003520b56303..c383d56bbe18 100644 --- a/trunk/arch/powerpc/boot/Makefile +++ b/trunk/arch/powerpc/boot/Makefile @@ -113,7 +113,7 @@ endif endif quiet_cmd_wrap = WRAP $@ - cmd_wrap =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) vmlinux + cmd_wrap =$(wrapper) -c -o $@ -p $2 $(CROSSWRAP) vmlinux quiet_cmd_wrap_initrd = WRAP $@ cmd_wrap_initrd =$(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \ -i $(obj)/ramdisk.image.gz vmlinux diff --git a/trunk/arch/powerpc/boot/dts/mpc8272ads.dts b/trunk/arch/powerpc/boot/dts/mpc8272ads.dts deleted file mode 100644 index 34efdd028c4f..000000000000 --- a/trunk/arch/powerpc/boot/dts/mpc8272ads.dts +++ /dev/null @@ -1,223 +0,0 @@ -/* - * MPC8272 ADS Device Tree Source - * - * Copyright 2005 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -/ { - model = "MPC8272ADS"; - compatible = "MPC8260ADS"; - #address-cells = <1>; - #size-cells = <1>; - linux,phandle = <100>; - - cpus { - #cpus = <1>; - #address-cells = <1>; - #size-cells = <0>; - linux,phandle = <200>; - - PowerPC,8272@0 { - device_type = "cpu"; - reg = <0>; - d-cache-line-size = <20>; // 32 bytes - i-cache-line-size = <20>; // 32 bytes - d-cache-size = <4000>; // L1, 16K - i-cache-size = <4000>; // L1, 16K - timebase-frequency = <0>; - bus-frequency = <0>; - clock-frequency = <0>; - 32-bit; - linux,phandle = <201>; - linux,boot-cpu; - }; - }; - - interrupt-controller@f8200000 { - linux,phandle = ; - #address-cells = <0>; - #interrupt-cells = <2>; - interrupt-controller; - reg = ; - built-in; - device_type = "pci-pic"; - }; - memory { - device_type = "memory"; - linux,phandle = <300>; - reg = <00000000 4000000 f4500000 00000020>; - }; - - soc8272@f0000000 { - #address-cells = <1>; - #size-cells = <1>; - #interrupt-cells = <2>; - device_type = "soc"; - ranges = < 0 0 2 00000000 f0000000 00053000>; - reg = ; - - mdio@0 { - device_type = "mdio"; - compatible = "fs_enet"; - reg = <0 0>; - linux,phandle = <24520>; - #address-cells = <1>; - #size-cells = <0>; - ethernet-phy@0 { - linux,phandle = <2452000>; - interrupt-parent = <10c00>; - interrupts = <19 1>; - reg = <0>; - bitbang = [ 12 12 13 02 02 01 ]; - device_type = "ethernet-phy"; - }; - ethernet-phy@1 { - linux,phandle = <2452001>; - interrupt-parent = <10c00>; - interrupts = <19 1>; - bitbang = [ 12 12 13 02 02 01 ]; - reg = <3>; - device_type = "ethernet-phy"; - }; - }; - - ethernet@24000 { - #address-cells = <1>; - #size-cells = <0>; - device_type = "network"; - device-id = <2>; - compatible = "fs_enet"; - model = "FCC"; - reg = <11300 20 8400 100 11380 30>; - mac-address = [ 00 11 2F 99 43 54 ]; - interrupts = <20 2>; - interrupt-parent = <10c00>; - phy-handle = <2452000>; - rx-clock = <13>; - tx-clock = <12>; - }; - - ethernet@25000 { - device_type = "network"; - device-id = <3>; - compatible = "fs_enet"; - model = "FCC"; - reg = <11320 20 8500 100 113b0 30>; - mac-address = [ 00 11 2F 99 44 54 ]; - interrupts = <21 2>; - interrupt-parent = <10c00>; - phy-handle = <2452001>; - rx-clock = <17>; - tx-clock = <18>; - }; - - cpm@f0000000 { - linux,phandle = ; - #address-cells = <1>; - #size-cells = <1>; - #interrupt-cells = <2>; - device_type = "cpm"; - model = "CPM2"; - ranges = <00000000 00000000 3ffff>; - reg = <10d80 3280>; - command-proc = <119c0>; - brg-frequency = <17D7840>; - cpm_clk = ; - - scc@11a00 { - device_type = "serial"; - compatible = "cpm_uart"; - model = "SCC"; - device-id = <2>; - reg = <11a00 20 8000 100>; - current-speed = <1c200>; - interrupts = <28 2>; - interrupt-parent = <10c00>; - clock-setup = <0 00ffffff>; - rx-clock = <1>; - tx-clock = <1>; - }; - - scc@11a60 { - device_type = "serial"; - compatible = "cpm_uart"; - model = "SCC"; - device-id = <5>; - reg = <11a60 20 8300 100>; - current-speed = <1c200>; - interrupts = <2b 2>; - interrupt-parent = <10c00>; - clock-setup = <1b ffffff00>; - rx-clock = <4>; - tx-clock = <4>; - }; - - }; - interrupt-controller@10c00 { - linux,phandle = <10c00>; - #address-cells = <0>; - #interrupt-cells = <2>; - interrupt-controller; - reg = <10c00 80>; - built-in; - device_type = "cpm-pic"; - compatible = "CPM2"; - }; - pci@0500 { - linux,phandle = <0500>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - compatible = "8272"; - device_type = "pci"; - reg = <10430 4dc>; - clock-frequency = <3f940aa>; - interrupt-map-mask = ; - interrupt-map = < - - /* IDSEL 0x16 */ - b000 0 0 1 f8200000 40 0 - b000 0 0 2 f8200000 41 0 - b000 0 0 3 f8200000 42 0 - b000 0 0 4 f8200000 43 0 - - /* IDSEL 0x17 */ - b800 0 0 1 f8200000 43 0 - b800 0 0 2 f8200000 40 0 - b800 0 0 3 f8200000 41 0 - b800 0 0 4 f8200000 42 0 - - /* IDSEL 0x18 */ - c000 0 0 1 f8200000 42 0 - c000 0 0 2 f8200000 43 0 - c000 0 0 3 f8200000 40 0 - c000 0 0 4 f8200000 41 0>; - interrupt-parent = <10c00>; - interrupts = <14 3>; - bus-range = <0 0>; - ranges = <02000000 0 80000000 80000000 0 40000000 - 01000000 0 00000000 f6000000 0 02000000>; - }; - -/* May need to remove if on a part without crypto engine */ - crypto@30000 { - device_type = "crypto"; - model = "SEC2"; - compatible = "talitos"; - reg = <30000 10000>; - interrupts = ; - interrupt-parent = <10c00>; - num-channels = <4>; - channel-fifo-len = <18>; - exec-units-mask = <0000007e>; -/* desc mask is for rev1.x, we need runtime fixup for >=2.x */ - descriptor-types-mask = <01010ebf>; - }; - - }; -}; diff --git a/trunk/arch/powerpc/boot/dts/mpc8360emds.dts b/trunk/arch/powerpc/boot/dts/mpc8360emds.dts deleted file mode 100644 index 9022192155b9..000000000000 --- a/trunk/arch/powerpc/boot/dts/mpc8360emds.dts +++ /dev/null @@ -1,375 +0,0 @@ -/* - * MPC8360E EMDS Device Tree Source - * - * Copyright 2006 Freescale Semiconductor Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - - -/* -/memreserve/ 00000000 1000000; -*/ - -/ { - model = "MPC8360EPB"; - compatible = "MPC83xx"; - #address-cells = <1>; - #size-cells = <1>; - linux,phandle = <100>; - - cpus { - #cpus = <1>; - #address-cells = <1>; - #size-cells = <0>; - linux,phandle = <200>; - - PowerPC,8360@0 { - device_type = "cpu"; - reg = <0>; - d-cache-line-size = <20>; // 32 bytes - i-cache-line-size = <20>; // 32 bytes - d-cache-size = <8000>; // L1, 32K - i-cache-size = <8000>; // L1, 32K - timebase-frequency = <3EF1480>; - bus-frequency = ; - clock-frequency = <1F78A400>; - 32-bit; - linux,phandle = <201>; - linux,boot-cpu; - }; - }; - - memory { - device_type = "memory"; - linux,phandle = <300>; - reg = <00000000 10000000>; - }; - - bcsr@f8000000 { - device_type = "board-control"; - reg = ; - }; - - soc8360@e0000000 { - #address-cells = <1>; - #size-cells = <1>; - #interrupt-cells = <2>; - device_type = "soc"; - ranges = <0 e0000000 00100000>; - reg = ; - bus-frequency = ; - - wdt@200 { - device_type = "watchdog"; - compatible = "mpc83xx_wdt"; - reg = <200 100>; - }; - - i2c@3000 { - device_type = "i2c"; - compatible = "fsl-i2c"; - reg = <3000 100>; - interrupts = ; - interrupt-parent = <700>; - dfsrr; - }; - - i2c@3100 { - device_type = "i2c"; - compatible = "fsl-i2c"; - reg = <3100 100>; - interrupts = ; - interrupt-parent = <700>; - dfsrr; - }; - - serial@4500 { - device_type = "serial"; - compatible = "ns16550"; - reg = <4500 100>; - clock-frequency = ; - interrupts = <9 8>; - interrupt-parent = <700>; - }; - - serial@4600 { - device_type = "serial"; - compatible = "ns16550"; - reg = <4600 100>; - clock-frequency = ; - interrupts = ; - interrupt-parent = <700>; - }; - - crypto@30000 { - device_type = "crypto"; - model = "SEC2"; - compatible = "talitos"; - reg = <30000 10000>; - interrupts = ; - interrupt-parent = <700>; - num-channels = <4>; - channel-fifo-len = <18>; - exec-units-mask = <0000007e>; - /* desc mask is for rev1.x, we need runtime fixup for >=2.x */ - descriptor-types-mask = <01010ebf>; - }; - - pci@8500 { - linux,phandle = <8500>; - interrupt-map-mask = ; - interrupt-map = < - - /* IDSEL 0x11 AD17 */ - 8800 0 0 1 700 14 8 - 8800 0 0 2 700 15 8 - 8800 0 0 3 700 16 8 - 8800 0 0 4 700 17 8 - - /* IDSEL 0x12 AD18 */ - 9000 0 0 1 700 16 8 - 9000 0 0 2 700 17 8 - 9000 0 0 3 700 14 8 - 9000 0 0 4 700 15 8 - - /* IDSEL 0x13 AD19 */ - 9800 0 0 1 700 17 8 - 9800 0 0 2 700 14 8 - 9800 0 0 3 700 15 8 - 9800 0 0 4 700 16 8 - - /* IDSEL 0x15 AD21*/ - a800 0 0 1 700 14 8 - a800 0 0 2 700 15 8 - a800 0 0 3 700 16 8 - a800 0 0 4 700 17 8 - - /* IDSEL 0x16 AD22*/ - b000 0 0 1 700 17 8 - b000 0 0 2 700 14 8 - b000 0 0 3 700 15 8 - b000 0 0 4 700 16 8 - - /* IDSEL 0x17 AD23*/ - b800 0 0 1 700 16 8 - b800 0 0 2 700 17 8 - b800 0 0 3 700 14 8 - b800 0 0 4 700 15 8 - - /* IDSEL 0x18 AD24*/ - c000 0 0 1 700 15 8 - c000 0 0 2 700 16 8 - c000 0 0 3 700 17 8 - c000 0 0 4 700 14 8>; - interrupt-parent = <700>; - interrupts = <42 8>; - bus-range = <0 0>; - ranges = <02000000 0 a0000000 a0000000 0 10000000 - 42000000 0 80000000 80000000 0 10000000 - 01000000 0 00000000 e2000000 0 00100000>; - clock-frequency = <3f940aa>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = <8500 100>; - compatible = "83xx"; - device_type = "pci"; - }; - - pic@700 { - linux,phandle = <700>; - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <2>; - reg = <700 100>; - built-in; - device_type = "ipic"; - }; - - par_io@1400 { - reg = <1400 100>; - device_type = "par_io"; - num-ports = <7>; - - ucc_pin@01 { - linux,phandle = <140001>; - pio-map = < - /* port pin dir open_drain assignment has_irq */ - 0 3 1 0 1 0 /* TxD0 */ - 0 4 1 0 1 0 /* TxD1 */ - 0 5 1 0 1 0 /* TxD2 */ - 0 6 1 0 1 0 /* TxD3 */ - 1 6 1 0 3 0 /* TxD4 */ - 1 7 1 0 1 0 /* TxD5 */ - 1 9 1 0 2 0 /* TxD6 */ - 1 a 1 0 2 0 /* TxD7 */ - 0 9 2 0 1 0 /* RxD0 */ - 0 a 2 0 1 0 /* RxD1 */ - 0 b 2 0 1 0 /* RxD2 */ - 0 c 2 0 1 0 /* RxD3 */ - 0 d 2 0 1 0 /* RxD4 */ - 1 1 2 0 2 0 /* RxD5 */ - 1 0 2 0 2 0 /* RxD6 */ - 1 4 2 0 2 0 /* RxD7 */ - 0 7 1 0 1 0 /* TX_EN */ - 0 8 1 0 1 0 /* TX_ER */ - 0 f 2 0 1 0 /* RX_DV */ - 0 10 2 0 1 0 /* RX_ER */ - 0 0 2 0 1 0 /* RX_CLK */ - 2 9 1 0 3 0 /* GTX_CLK - CLK10 */ - 2 8 2 0 1 0>; /* GTX125 - CLK9 */ - }; - ucc_pin@02 { - linux,phandle = <140002>; - pio-map = < - /* port pin dir open_drain assignment has_irq */ - 0 11 1 0 1 0 /* TxD0 */ - 0 12 1 0 1 0 /* TxD1 */ - 0 13 1 0 1 0 /* TxD2 */ - 0 14 1 0 1 0 /* TxD3 */ - 1 2 1 0 1 0 /* TxD4 */ - 1 3 1 0 2 0 /* TxD5 */ - 1 5 1 0 3 0 /* TxD6 */ - 1 8 1 0 3 0 /* TxD7 */ - 0 17 2 0 1 0 /* RxD0 */ - 0 18 2 0 1 0 /* RxD1 */ - 0 19 2 0 1 0 /* RxD2 */ - 0 1a 2 0 1 0 /* RxD3 */ - 0 1b 2 0 1 0 /* RxD4 */ - 1 c 2 0 2 0 /* RxD5 */ - 1 d 2 0 3 0 /* RxD6 */ - 1 b 2 0 2 0 /* RxD7 */ - 0 15 1 0 1 0 /* TX_EN */ - 0 16 1 0 1 0 /* TX_ER */ - 0 1d 2 0 1 0 /* RX_DV */ - 0 1e 2 0 1 0 /* RX_ER */ - 0 1f 2 0 1 0 /* RX_CLK */ - 2 2 1 0 2 0 /* GTX_CLK - CLK10 */ - 2 3 2 0 1 0 /* GTX125 - CLK4 */ - 0 1 3 0 2 0 /* MDIO */ - 0 2 1 0 1 0>; /* MDC */ - }; - - }; - }; - - qe@e0100000 { - #address-cells = <1>; - #size-cells = <1>; - device_type = "qe"; - model = "QE"; - ranges = <0 e0100000 00100000>; - reg = ; - brg-frequency = <0>; - bus-frequency = <179A7B00>; - - muram@10000 { - device_type = "muram"; - ranges = <0 00010000 0000c000>; - - data-only@0{ - reg = <0 c000>; - }; - }; - - spi@4c0 { - device_type = "spi"; - compatible = "fsl_spi"; - reg = <4c0 40>; - interrupts = <2>; - interrupt-parent = <80>; - mode = "cpu"; - }; - - spi@500 { - device_type = "spi"; - compatible = "fsl_spi"; - reg = <500 40>; - interrupts = <1>; - interrupt-parent = <80>; - mode = "cpu"; - }; - - usb@6c0 { - device_type = "usb"; - compatible = "qe_udc"; - reg = <6c0 40 8B00 100>; - interrupts = ; - interrupt-parent = <80>; - mode = "slave"; - }; - - ucc@2000 { - device_type = "network"; - compatible = "ucc_geth"; - model = "UCC"; - device-id = <1>; - reg = <2000 200>; - interrupts = <20>; - interrupt-parent = <80>; - mac-address = [ 00 04 9f 00 23 23 ]; - rx-clock = <0>; - tx-clock = <19>; - phy-handle = <212000>; - pio-handle = <140001>; - }; - - ucc@3000 { - device_type = "network"; - compatible = "ucc_geth"; - model = "UCC"; - device-id = <2>; - reg = <3000 200>; - interrupts = <21>; - interrupt-parent = <80>; - mac-address = [ 00 11 22 33 44 55 ]; - rx-clock = <0>; - tx-clock = <14>; - phy-handle = <212001>; - pio-handle = <140002>; - }; - - mdio@2120 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2120 18>; - device_type = "mdio"; - compatible = "ucc_geth_phy"; - - ethernet-phy@00 { - linux,phandle = <212000>; - interrupt-parent = <700>; - interrupts = <11 2>; - reg = <0>; - device_type = "ethernet-phy"; - interface = <6>; //ENET_1000_GMII - }; - ethernet-phy@01 { - linux,phandle = <212001>; - interrupt-parent = <700>; - interrupts = <12 2>; - reg = <1>; - device_type = "ethernet-phy"; - interface = <6>; - }; - }; - - qeic@80 { - linux,phandle = <80>; - interrupt-controller; - device_type = "qeic"; - #address-cells = <0>; - #interrupt-cells = <1>; - reg = <80 80>; - built-in; - big-endian; - interrupts = <20 8 21 8>; //high:32 low:33 - interrupt-parent = <700>; - }; - - }; -}; diff --git a/trunk/arch/powerpc/boot/zImage.coff.lds.S b/trunk/arch/powerpc/boot/zImage.coff.lds.S index 05f32388b953..6016251a1a2c 100644 --- a/trunk/arch/powerpc/boot/zImage.coff.lds.S +++ b/trunk/arch/powerpc/boot/zImage.coff.lds.S @@ -15,7 +15,6 @@ SECTIONS { *(.rodata*) *(.data*) - *(__builtin_*) *(.sdata*) __got2_start = .; *(.got2) diff --git a/trunk/arch/powerpc/configs/mpc8360emds_defconfig b/trunk/arch/powerpc/configs/mpc8360emds_defconfig deleted file mode 100644 index c0703415d608..000000000000 --- a/trunk/arch/powerpc/configs/mpc8360emds_defconfig +++ /dev/null @@ -1,1018 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.18 -# Thu Sep 21 18:14:27 2006 -# -# CONFIG_PPC64 is not set -CONFIG_PPC32=y -CONFIG_PPC_MERGE=y -CONFIG_MMU=y -CONFIG_GENERIC_HARDIRQS=y -CONFIG_IRQ_PER_CPU=y -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_GENERIC_HWEIGHT=y -CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_FIND_NEXT_BIT=y -CONFIG_PPC=y -CONFIG_EARLY_PRINTK=y -CONFIG_GENERIC_NVRAM=y -CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y -CONFIG_PPC_OF=y -CONFIG_PPC_UDBG_16550=y -# CONFIG_GENERIC_TBSYNC is not set -CONFIG_AUDIT_ARCH=y -CONFIG_DEFAULT_UIMAGE=y - -# -# Processor support -# -# CONFIG_CLASSIC32 is not set -# CONFIG_PPC_52xx is not set -# CONFIG_PPC_82xx is not set -CONFIG_PPC_83xx=y -# CONFIG_PPC_85xx is not set -# CONFIG_PPC_86xx is not set -# CONFIG_40x is not set -# CONFIG_44x is not set -# CONFIG_8xx is not set -# CONFIG_E200 is not set -CONFIG_6xx=y -CONFIG_83xx=y -CONFIG_PPC_FPU=y -CONFIG_PPC_STD_MMU=y -CONFIG_PPC_STD_MMU_32=y -# CONFIG_SMP is not set -CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" - -# -# Code maturity level options -# -CONFIG_EXPERIMENTAL=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -CONFIG_LOCALVERSION_AUTO=y -CONFIG_SWAP=y -CONFIG_SYSVIPC=y -# CONFIG_POSIX_MQUEUE is not set -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_TASKSTATS is not set -# CONFIG_AUDIT is not set -# CONFIG_IKCONFIG is not set -# CONFIG_RELAY is not set -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y -CONFIG_SYSCTL=y -# CONFIG_KALLSYMS is not set -CONFIG_HOTPLUG=y -CONFIG_PRINTK=y -CONFIG_BUG=y -CONFIG_ELF_CORE=y -CONFIG_BASE_FULL=y -CONFIG_FUTEX=y -# CONFIG_EPOLL is not set -CONFIG_SHMEM=y -CONFIG_SLAB=y -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_RT_MUTEXES=y -# CONFIG_TINY_SHMEM is not set -CONFIG_BASE_SMALL=0 -# CONFIG_SLOB is not set - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set -# CONFIG_MODVERSIONS is not set -# CONFIG_MODULE_SRCVERSION_ALL is not set -# CONFIG_KMOD is not set - -# -# Block layer -# -# CONFIG_LBD is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_LSF is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y -CONFIG_DEFAULT_AS=y -# CONFIG_DEFAULT_DEADLINE is not set -# CONFIG_DEFAULT_CFQ is not set -# CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="anticipatory" -CONFIG_QUICC_ENGINE=y -CONFIG_PPC_GEN550=y -# CONFIG_WANT_EARLY_SERIAL is not set - -# -# Platform support -# -# CONFIG_MPC834x_SYS is not set -# CONFIG_MPC834x_ITX is not set -CONFIG_MPC8360E_PB=y -CONFIG_PPC_MPC836x=y -# CONFIG_MPIC is not set - -# -# Kernel options -# -# CONFIG_HIGHMEM is not set -# CONFIG_HZ_100 is not set -CONFIG_HZ_250=y -# CONFIG_HZ_1000 is not set -CONFIG_HZ=250 -CONFIG_PREEMPT_NONE=y -# CONFIG_PREEMPT_VOLUNTARY is not set -# CONFIG_PREEMPT is not set -CONFIG_BINFMT_ELF=y -# CONFIG_BINFMT_MISC is not set -CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y -CONFIG_ARCH_FLATMEM_ENABLE=y -CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_FLATMEM_MANUAL=y -# CONFIG_DISCONTIGMEM_MANUAL is not set -# CONFIG_SPARSEMEM_MANUAL is not set -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y -# CONFIG_SPARSEMEM_STATIC is not set -CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_RESOURCES_64BIT is not set -CONFIG_PROC_DEVICETREE=y -# CONFIG_CMDLINE_BOOL is not set -# CONFIG_PM is not set -CONFIG_SECCOMP=y -CONFIG_ISA_DMA_API=y - -# -# Bus options -# -CONFIG_GENERIC_ISA_DMA=y -# CONFIG_MPIC_WEIRD is not set -# CONFIG_PPC_I8259 is not set -CONFIG_PPC_INDIRECT_PCI=y -CONFIG_FSL_SOC=y -CONFIG_PCI=y -CONFIG_PCI_DOMAINS=y -# CONFIG_PCIEPORTBUS is not set - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# -# CONFIG_HOTPLUG_PCI is not set - -# -# Advanced setup -# -# CONFIG_ADVANCED_OPTIONS is not set - -# -# Default settings for advanced configuration options are used -# -CONFIG_HIGHMEM_START=0xfe000000 -CONFIG_LOWMEM_SIZE=0x30000000 -CONFIG_KERNEL_START=0xc0000000 -CONFIG_TASK_SIZE=0x80000000 -CONFIG_BOOT_LOAD=0x00800000 - -# -# Networking -# -CONFIG_NET=y - -# -# Networking options -# -# CONFIG_NETDEBUG is not set -CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set -CONFIG_UNIX=y -CONFIG_XFRM=y -# CONFIG_XFRM_USER is not set -# CONFIG_NET_KEY is not set -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -# CONFIG_IP_ADVANCED_ROUTER is not set -CONFIG_IP_FIB_HASH=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -# CONFIG_IP_PNP_RARP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_IP_MROUTE is not set -# CONFIG_ARPD is not set -CONFIG_SYN_COOKIES=y -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_XFRM_TUNNEL is not set -# CONFIG_INET_TUNNEL is not set -CONFIG_INET_XFRM_MODE_TRANSPORT=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_INET_DIAG=y -CONFIG_INET_TCP_DIAG=y -# CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_BIC=y -# CONFIG_IPV6 is not set -# CONFIG_INET6_XFRM_TUNNEL is not set -# CONFIG_INET6_TUNNEL is not set -# CONFIG_NETWORK_SECMARK is not set -# CONFIG_NETFILTER is not set - -# -# DCCP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_DCCP is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_SCTP is not set - -# -# TIPC Configuration (EXPERIMENTAL) -# -# CONFIG_TIPC is not set -# CONFIG_ATM is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_ECONET is not set -# CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# -# CONFIG_NET_SCHED is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set -# CONFIG_IEEE80211 is not set - -# -# Device Drivers -# - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_SYS_HYPERVISOR is not set - -# -# Connector - unified userspace <-> kernelspace linker -# -# CONFIG_CONNECTOR is not set - -# -# Memory Technology Devices (MTD) -# -# CONFIG_MTD is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Plug and Play support -# - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_CPQ_DA is not set -# CONFIG_BLK_CPQ_CISS_DA is not set -# CONFIG_BLK_DEV_DAC960 is not set -# CONFIG_BLK_DEV_UMEM is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set -# CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_SX8 is not set -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_BLK_DEV_RAM_SIZE=32768 -CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 -CONFIG_BLK_DEV_INITRD=y -# CONFIG_CDROM_PKTCDVD is not set -# CONFIG_ATA_OVER_ETH is not set - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_RAID_ATTRS is not set -CONFIG_SCSI=y -CONFIG_SCSI_PROC_FS=y - -# -# SCSI support type (disk, tape, CD-ROM) -# -# CONFIG_BLK_DEV_SD is not set -# CONFIG_CHR_DEV_ST is not set -# CONFIG_CHR_DEV_OSST is not set -# CONFIG_BLK_DEV_SR is not set -# CONFIG_CHR_DEV_SG is not set -# CONFIG_CHR_DEV_SCH is not set - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# -# CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_CONSTANTS is not set -# CONFIG_SCSI_LOGGING is not set - -# -# SCSI Transport Attributes -# -# CONFIG_SCSI_SPI_ATTRS is not set -# CONFIG_SCSI_FC_ATTRS is not set -# CONFIG_SCSI_ISCSI_ATTRS is not set -# CONFIG_SCSI_SAS_ATTRS is not set - -# -# SCSI low-level drivers -# -# CONFIG_ISCSI_TCP is not set -# CONFIG_BLK_DEV_3W_XXXX_RAID is not set -# CONFIG_SCSI_3W_9XXX is not set -# CONFIG_SCSI_ACARD is not set -# CONFIG_SCSI_AACRAID is not set -# CONFIG_SCSI_AIC7XXX is not set -# CONFIG_SCSI_AIC7XXX_OLD is not set -# CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_DPT_I2O is not set -# CONFIG_MEGARAID_NEWGEN is not set -# CONFIG_MEGARAID_LEGACY is not set -# CONFIG_MEGARAID_SAS is not set -# CONFIG_SCSI_SATA is not set -# CONFIG_SCSI_HPTIOP is not set -# CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_DMX3191D is not set -# CONFIG_SCSI_EATA is not set -# CONFIG_SCSI_FUTURE_DOMAIN is not set -# CONFIG_SCSI_GDTH is not set -# CONFIG_SCSI_IPS is not set -# CONFIG_SCSI_INITIO is not set -# CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_SYM53C8XX_2 is not set -# CONFIG_SCSI_IPR is not set -# CONFIG_SCSI_QLOGIC_1280 is not set -# CONFIG_SCSI_QLA_FC is not set -# CONFIG_SCSI_LPFC is not set -# CONFIG_SCSI_DC395x is not set -# CONFIG_SCSI_DC390T is not set -# CONFIG_SCSI_NSP32 is not set -# CONFIG_SCSI_DEBUG is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set -# CONFIG_FUSION_SPI is not set -# CONFIG_FUSION_FC is not set -# CONFIG_FUSION_SAS is not set - -# -# IEEE 1394 (FireWire) support -# -# CONFIG_IEEE1394 is not set - -# -# I2O device support -# -# CONFIG_I2O is not set - -# -# Macintosh device drivers -# -# CONFIG_WINDFARM is not set - -# -# Network device support -# -CONFIG_NETDEVICES=y -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set - -# -# ARCnet devices -# -# CONFIG_ARCNET is not set - -# -# PHY device support -# -# CONFIG_PHYLIB is not set - -# -# Ethernet (10 or 100Mbit) -# -CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNGEM is not set -# CONFIG_CASSINI is not set -# CONFIG_NET_VENDOR_3COM is not set - -# -# Tulip family network device support -# -# CONFIG_NET_TULIP is not set -# CONFIG_HP100 is not set -# CONFIG_NET_PCI is not set - -# -# Ethernet (1000 Mbit) -# -# CONFIG_ACENIC is not set -# CONFIG_DL2K is not set -# CONFIG_E1000 is not set -# CONFIG_NS83820 is not set -# CONFIG_HAMACHI is not set -# CONFIG_YELLOWFIN is not set -# CONFIG_R8169 is not set -# CONFIG_SIS190 is not set -# CONFIG_SKGE is not set -# CONFIG_SKY2 is not set -# CONFIG_SK98LIN is not set -# CONFIG_TIGON3 is not set -# CONFIG_BNX2 is not set -# CONFIG_GIANFAR is not set -CONFIG_UCC_GETH=y -# CONFIG_UGETH_NAPI is not set -# CONFIG_UGETH_MAGIC_PACKET is not set -# CONFIG_UGETH_FILTERING is not set -# CONFIG_UGETH_TX_ON_DEMOND is not set - -# -# Ethernet (10000 Mbit) -# -# CONFIG_CHELSIO_T1 is not set -# CONFIG_IXGB is not set -# CONFIG_S2IO is not set -# CONFIG_MYRI10GE is not set - -# -# Token Ring devices -# -# CONFIG_TR is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set -# CONFIG_NET_FC is not set -# CONFIG_SHAPER is not set -# CONFIG_NETCONSOLE is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Telephony Support -# -# CONFIG_PHONE is not set - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -# CONFIG_SERIO is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_PCI=y -CONFIG_SERIAL_8250_NR_UARTS=4 -CONFIG_SERIAL_8250_RUNTIME_UARTS=4 -# CONFIG_SERIAL_8250_EXTENDED is not set - -# -# Non-8250 serial port support -# -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_SERIAL_JSM is not set -CONFIG_UNIX98_PTYS=y -CONFIG_LEGACY_PTYS=y -CONFIG_LEGACY_PTY_COUNT=256 - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -CONFIG_WATCHDOG=y -# CONFIG_WATCHDOG_NOWAYOUT is not set - -# -# Watchdog Device Drivers -# -# CONFIG_SOFT_WATCHDOG is not set -CONFIG_83xx_WDT=y - -# -# PCI-based Watchdog Cards -# -# CONFIG_PCIPCWATCHDOG is not set -# CONFIG_WDTPCI is not set -CONFIG_HW_RANDOM=y -# CONFIG_NVRAM is not set -CONFIG_GEN_RTC=y -# CONFIG_GEN_RTC_X is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_AGP is not set -# CONFIG_DRM is not set -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# -# CONFIG_TCG_TPM is not set -# CONFIG_TELCLOCK is not set - -# -# I2C support -# -CONFIG_I2C=y -CONFIG_I2C_CHARDEV=y - -# -# I2C Algorithms -# -# CONFIG_I2C_ALGOBIT is not set -# CONFIG_I2C_ALGOPCF is not set -# CONFIG_I2C_ALGOPCA is not set - -# -# I2C Hardware Bus support -# -# CONFIG_I2C_ALI1535 is not set -# CONFIG_I2C_ALI1563 is not set -# CONFIG_I2C_ALI15X3 is not set -# CONFIG_I2C_AMD756 is not set -# CONFIG_I2C_AMD8111 is not set -# CONFIG_I2C_I801 is not set -# CONFIG_I2C_I810 is not set -# CONFIG_I2C_PIIX4 is not set -CONFIG_I2C_MPC=y -# CONFIG_I2C_NFORCE2 is not set -# CONFIG_I2C_OCORES is not set -# CONFIG_I2C_PARPORT_LIGHT is not set -# CONFIG_I2C_PROSAVAGE is not set -# CONFIG_I2C_SAVAGE4 is not set -# CONFIG_I2C_SIS5595 is not set -# CONFIG_I2C_SIS630 is not set -# CONFIG_I2C_SIS96X is not set -# CONFIG_I2C_STUB is not set -# CONFIG_I2C_VIA is not set -# CONFIG_I2C_VIAPRO is not set -# CONFIG_I2C_VOODOO3 is not set -# CONFIG_I2C_PCA_ISA is not set - -# -# Miscellaneous I2C Chip support -# -# CONFIG_SENSORS_DS1337 is not set -# CONFIG_SENSORS_DS1374 is not set -# CONFIG_SENSORS_EEPROM is not set -# CONFIG_SENSORS_PCF8574 is not set -# CONFIG_SENSORS_PCA9539 is not set -# CONFIG_SENSORS_PCF8591 is not set -# CONFIG_SENSORS_M41T00 is not set -# CONFIG_SENSORS_MAX6875 is not set -# CONFIG_I2C_DEBUG_CORE is not set -# CONFIG_I2C_DEBUG_ALGO is not set -# CONFIG_I2C_DEBUG_BUS is not set -# CONFIG_I2C_DEBUG_CHIP is not set - -# -# SPI support -# -# CONFIG_SPI is not set -# CONFIG_SPI_MASTER is not set - -# -# Dallas's 1-wire bus -# - -# -# Hardware Monitoring support -# -CONFIG_HWMON=y -# CONFIG_HWMON_VID is not set -# CONFIG_SENSORS_ABITUGURU is not set -# CONFIG_SENSORS_ADM1021 is not set -# CONFIG_SENSORS_ADM1025 is not set -# CONFIG_SENSORS_ADM1026 is not set -# CONFIG_SENSORS_ADM1031 is not set -# CONFIG_SENSORS_ADM9240 is not set -# CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_ATXP1 is not set -# CONFIG_SENSORS_DS1621 is not set -# CONFIG_SENSORS_F71805F is not set -# CONFIG_SENSORS_FSCHER is not set -# CONFIG_SENSORS_FSCPOS is not set -# CONFIG_SENSORS_GL518SM is not set -# CONFIG_SENSORS_GL520SM is not set -# CONFIG_SENSORS_IT87 is not set -# CONFIG_SENSORS_LM63 is not set -# CONFIG_SENSORS_LM75 is not set -# CONFIG_SENSORS_LM77 is not set -# CONFIG_SENSORS_LM78 is not set -# CONFIG_SENSORS_LM80 is not set -# CONFIG_SENSORS_LM83 is not set -# CONFIG_SENSORS_LM85 is not set -# CONFIG_SENSORS_LM87 is not set -# CONFIG_SENSORS_LM90 is not set -# CONFIG_SENSORS_LM92 is not set -# CONFIG_SENSORS_MAX1619 is not set -# CONFIG_SENSORS_PC87360 is not set -# CONFIG_SENSORS_SIS5595 is not set -# CONFIG_SENSORS_SMSC47M1 is not set -# CONFIG_SENSORS_SMSC47M192 is not set -# CONFIG_SENSORS_SMSC47B397 is not set -# CONFIG_SENSORS_VIA686A is not set -# CONFIG_SENSORS_VT8231 is not set -# CONFIG_SENSORS_W83781D is not set -# CONFIG_SENSORS_W83791D is not set -# CONFIG_SENSORS_W83792D is not set -# CONFIG_SENSORS_W83L785TS is not set -# CONFIG_SENSORS_W83627HF is not set -# CONFIG_SENSORS_W83627EHF is not set -# CONFIG_HWMON_DEBUG_CHIP is not set - -# -# Misc devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set -CONFIG_VIDEO_V4L2=y - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set - -# -# Graphics support -# -CONFIG_FIRMWARE_EDID=y -# CONFIG_FB is not set -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB_ARCH_HAS_OHCI=y -CONFIG_USB_ARCH_HAS_EHCI=y -# CONFIG_USB is not set - -# -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' -# - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# MMC/SD Card support -# -# CONFIG_MMC is not set - -# -# LED devices -# -# CONFIG_NEW_LEDS is not set - -# -# LED drivers -# - -# -# LED Triggers -# - -# -# InfiniBand support -# -# CONFIG_INFINIBAND is not set - -# -# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) -# - -# -# Real Time Clock -# -# CONFIG_RTC_CLASS is not set - -# -# DMA Engine support -# -# CONFIG_DMA_ENGINE is not set - -# -# DMA Clients -# - -# -# DMA Devices -# - -# -# File systems -# -CONFIG_EXT2_FS=y -# CONFIG_EXT2_FS_XATTR is not set -# CONFIG_EXT2_FS_XIP is not set -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_XATTR=y -# CONFIG_EXT3_FS_POSIX_ACL is not set -# CONFIG_EXT3_FS_SECURITY is not set -CONFIG_JBD=y -# CONFIG_JBD_DEBUG is not set -CONFIG_FS_MBCACHE=y -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set -# CONFIG_XFS_FS is not set -# CONFIG_OCFS2_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_ROMFS_FS is not set -CONFIG_INOTIFY=y -CONFIG_INOTIFY_USER=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set -# CONFIG_FUSE_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_PROC_KCORE=y -CONFIG_SYSFS=y -CONFIG_TMPFS=y -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y -# CONFIG_CONFIGFS_FS is not set - -# -# Miscellaneous filesystems -# -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_HFSPLUS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_EFS_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Network File Systems -# -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -CONFIG_NFS_V4=y -# CONFIG_NFS_DIRECTIO is not set -# CONFIG_NFSD is not set -CONFIG_ROOT_NFS=y -CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=y -CONFIG_SUNRPC_GSS=y -CONFIG_RPCSEC_GSS_KRB5=y -# CONFIG_RPCSEC_GSS_SPKM3 is not set -# CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set -# CONFIG_AFS_FS is not set -# CONFIG_9P_FS is not set - -# -# Partition Types -# -CONFIG_PARTITION_ADVANCED=y -# CONFIG_ACORN_PARTITION is not set -# CONFIG_OSF_PARTITION is not set -# CONFIG_AMIGA_PARTITION is not set -# CONFIG_ATARI_PARTITION is not set -# CONFIG_MAC_PARTITION is not set -# CONFIG_MSDOS_PARTITION is not set -# CONFIG_LDM_PARTITION is not set -# CONFIG_SGI_PARTITION is not set -# CONFIG_ULTRIX_PARTITION is not set -# CONFIG_SUN_PARTITION is not set -# CONFIG_KARMA_PARTITION is not set -# CONFIG_EFI_PARTITION is not set - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# QE Options -# -# CONFIG_UCC_SLOW is not set -CONFIG_UCC_FAST=y -CONFIG_UCC=y - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC16 is not set -CONFIG_CRC32=y -# CONFIG_LIBCRC32C is not set -CONFIG_PLIST=y - -# -# Instrumentation Support -# -# CONFIG_PROFILING is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -# CONFIG_MAGIC_SYSRQ is not set -# CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_DEBUG_KERNEL is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_DEBUG_FS is not set -# CONFIG_BOOTX_TEXT is not set -# CONFIG_SERIAL_TEXT_DEBUG is not set -# CONFIG_PPC_EARLY_DEBUG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -CONFIG_CRYPTO=y -# CONFIG_CRYPTO_HMAC is not set -# CONFIG_CRYPTO_NULL is not set -# CONFIG_CRYPTO_MD4 is not set -CONFIG_CRYPTO_MD5=y -# CONFIG_CRYPTO_SHA1 is not set -# CONFIG_CRYPTO_SHA256 is not set -# CONFIG_CRYPTO_SHA512 is not set -# CONFIG_CRYPTO_WP512 is not set -# CONFIG_CRYPTO_TGR192 is not set -CONFIG_CRYPTO_DES=y -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_TWOFISH is not set -# CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES is not set -# CONFIG_CRYPTO_CAST5 is not set -# CONFIG_CRYPTO_CAST6 is not set -# CONFIG_CRYPTO_TEA is not set -# CONFIG_CRYPTO_ARC4 is not set -# CONFIG_CRYPTO_KHAZAD is not set -# CONFIG_CRYPTO_ANUBIS is not set -# CONFIG_CRYPTO_DEFLATE is not set -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_CRC32C is not set -# CONFIG_CRYPTO_TEST is not set - -# -# Hardware crypto devices -# diff --git a/trunk/arch/powerpc/kernel/cputable.c b/trunk/arch/powerpc/kernel/cputable.c index 47a613cdd775..190a57e20765 100644 --- a/trunk/arch/powerpc/kernel/cputable.c +++ b/trunk/arch/powerpc/kernel/cputable.c @@ -763,10 +763,10 @@ struct cpu_spec cpu_specs[] = { .cpu_setup = __setup_cpu_603, .platform = "ppc603", }, - { /* e300c1 (a 603e core, plus some) on 83xx */ + { /* e300 (a 603e core, plus some) on 83xx */ .pvr_mask = 0x7fff0000, .pvr_value = 0x00830000, - .cpu_name = "e300c1", + .cpu_name = "e300", .cpu_features = CPU_FTRS_E300, .cpu_user_features = COMMON_USER, .icache_bsize = 32, @@ -774,17 +774,6 @@ struct cpu_spec cpu_specs[] = { .cpu_setup = __setup_cpu_603, .platform = "ppc603", }, - { /* e300c2 (an e300c1 core, plus some, minus FPU) on 83xx */ - .pvr_mask = 0x7fff0000, - .pvr_value = 0x00840000, - .cpu_name = "e300c2", - .cpu_features = CPU_FTRS_E300, - .cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, - .icache_bsize = 32, - .dcache_bsize = 32, - .cpu_setup = __setup_cpu_603, - .platform = "ppc603", - }, { /* default match, we assume split I/D cache & TB (non-601)... */ .pvr_mask = 0x00000000, .pvr_value = 0x00000000, diff --git a/trunk/arch/powerpc/kernel/entry_64.S b/trunk/arch/powerpc/kernel/entry_64.S index 748e74fcf541..2cd872b5283b 100644 --- a/trunk/arch/powerpc/kernel/entry_64.S +++ b/trunk/arch/powerpc/kernel/entry_64.S @@ -27,7 +27,10 @@ #include #include #include -#include + +#ifdef CONFIG_PPC_ISERIES +#define DO_SOFT_DISABLE +#endif /* * System calls. @@ -88,7 +91,6 @@ system_call_common: ld r11,exception_marker@toc(r2) std r11,-16(r9) /* "regshere" marker */ #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION /* Hack for handling interrupts when soft-enabling on iSeries */ cmpdi cr1,r0,0x5555 /* syscall 0x5555 */ andi. r10,r12,MSR_PR /* from kernel */ @@ -96,7 +98,6 @@ BEGIN_FW_FTR_SECTION beq hardware_interrupt_entry lbz r10,PACAPROCENABLED(r13) std r10,SOFTE(r1) -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif mfmsr r11 ori r11,r11,MSR_EE @@ -461,7 +462,6 @@ _GLOBAL(ret_from_except_lite) restore: #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION ld r5,SOFTE(r1) cmpdi 0,r5,0 beq 4f @@ -480,7 +480,6 @@ BEGIN_FW_FTR_SECTION b .ret_from_except_lite /* loop back and handle more */ 4: stb r5,PACAPROCENABLED(r13) -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif ld r3,_MSR(r1) @@ -539,23 +538,18 @@ do_work: lwz r8,TI_PREEMPT(r9) cmpwi cr1,r8,0 #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION ld r0,SOFTE(r1) cmpdi r0,0 -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif -BEGIN_FW_FTR_SECTION +#else andi. r0,r3,MSR_EE -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) +#endif crandc eq,cr1*4+eq,eq bne restore /* here we are preempting the current task */ 1: #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION li r0,1 stb r0,PACAPROCENABLED(r13) -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif ori r10,r10,MSR_EE mtmsrd r10,1 /* reenable interrupts */ diff --git a/trunk/arch/powerpc/kernel/head_64.S b/trunk/arch/powerpc/kernel/head_64.S index 645c7f10fb28..3065b472b95d 100644 --- a/trunk/arch/powerpc/kernel/head_64.S +++ b/trunk/arch/powerpc/kernel/head_64.S @@ -33,7 +33,6 @@ #include #include #include -#include #ifdef CONFIG_PPC_ISERIES #define DO_SOFT_DISABLE @@ -366,28 +365,19 @@ label##_iSeries: \ #ifdef DO_SOFT_DISABLE #define DISABLE_INTS \ -BEGIN_FW_FTR_SECTION; \ lbz r10,PACAPROCENABLED(r13); \ li r11,0; \ std r10,SOFTE(r1); \ mfmsr r10; \ stb r11,PACAPROCENABLED(r13); \ ori r10,r10,MSR_EE; \ - mtmsrd r10,1; \ -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) + mtmsrd r10,1 #define ENABLE_INTS \ -BEGIN_FW_FTR_SECTION; \ lbz r10,PACAPROCENABLED(r13); \ mfmsr r11; \ std r10,SOFTE(r1); \ ori r11,r11,MSR_EE; \ -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES); \ -BEGIN_FW_FTR_SECTION; \ - ld r12,_MSR(r1); \ - mfmsr r11; \ - rlwimi r11,r12,0,MSR_EE; \ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES); \ mtmsrd r11,1 #else /* hard enable/disable interrupts */ @@ -1081,10 +1071,8 @@ _GLOBAL(slb_miss_realmode) ld r3,PACA_EXSLB+EX_R3(r13) lwz r9,PACA_EXSLB+EX_CCR(r13) /* get saved CR */ #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION ld r11,PACALPPACAPTR(r13) ld r11,LPPACASRR0(r11) /* get SRR0 value */ -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif /* CONFIG_PPC_ISERIES */ mtlr r10 @@ -1099,10 +1087,8 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) .machine pop #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION mtspr SPRN_SRR0,r11 mtspr SPRN_SRR1,r12 -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif /* CONFIG_PPC_ISERIES */ ld r9,PACA_EXSLB+EX_R9(r13) ld r10,PACA_EXSLB+EX_R10(r13) @@ -1315,7 +1301,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB) cmpdi r3,0 /* see if hash_page succeeded */ #ifdef DO_SOFT_DISABLE -BEGIN_FW_FTR_SECTION /* * If we had interrupts soft-enabled at the point where the * DSI/ISI occurred, and an interrupt came in during hash_page, @@ -1336,14 +1321,12 @@ BEGIN_FW_FTR_SECTION ld r3,SOFTE(r1) bl .local_irq_restore b 11f -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif -BEGIN_FW_FTR_SECTION +#else beq fast_exception_return /* Return from exception on success */ ble- 12f /* Failure return from hash_page */ /* fall through */ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) +#endif /* Here we have a page fault that hash_page can't handle. */ _GLOBAL(handle_page_fault) @@ -1878,9 +1861,7 @@ _GLOBAL(__secondary_start) LOAD_REG_ADDR(r3, .start_secondary_prolog) LOAD_REG_IMMEDIATE(r4, MSR_KERNEL) #ifdef DO_SOFT_DISABLE -BEGIN_FW_FTR_SECTION ori r4,r4,MSR_EE -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif mtspr SPRN_SRR0,r3 mtspr SPRN_SRR1,r4 @@ -2005,7 +1986,6 @@ _STATIC(start_here_common) */ li r3,0 bl .do_cpu_ftr_fixups - bl .do_fw_ftr_fixups /* ptr to current */ LOAD_REG_IMMEDIATE(r4, init_task) @@ -2020,13 +2000,11 @@ _STATIC(start_here_common) /* Load up the kernel context */ 5: #ifdef DO_SOFT_DISABLE -BEGIN_FW_FTR_SECTION li r5,0 stb r5,PACAPROCENABLED(r13) /* Soft Disabled */ mfmsr r5 ori r5,r5,MSR_EE /* Hard Enabled */ mtmsrd r5 -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif bl .start_kernel diff --git a/trunk/arch/powerpc/kernel/misc_64.S b/trunk/arch/powerpc/kernel/misc_64.S index 41521b30c3cd..9c54eccad993 100644 --- a/trunk/arch/powerpc/kernel/misc_64.S +++ b/trunk/arch/powerpc/kernel/misc_64.S @@ -325,52 +325,6 @@ _GLOBAL(do_cpu_ftr_fixups) isync b 1b -/* - * do_fw_ftr_fixups - goes through the list of firmware feature fixups - * and writes nop's over sections of code that don't apply for this firmware. - * r3 = data offset (not changed) - */ -_GLOBAL(do_fw_ftr_fixups) - /* Get firmware features */ - LOAD_REG_IMMEDIATE(r6,powerpc_firmware_features) - sub r6,r6,r3 - ld r4,0(r6) - /* Get the fixup table */ - LOAD_REG_IMMEDIATE(r6,__start___fw_ftr_fixup) - sub r6,r6,r3 - LOAD_REG_IMMEDIATE(r7,__stop___fw_ftr_fixup) - sub r7,r7,r3 - /* Do the fixup */ -1: cmpld r6,r7 - bgelr - addi r6,r6,32 - ld r8,-32(r6) /* mask */ - and r8,r8,r4 - ld r9,-24(r6) /* value */ - cmpld r8,r9 - beq 1b - ld r8,-16(r6) /* section begin */ - ld r9,-8(r6) /* section end */ - subf. r9,r8,r9 - beq 1b - /* write nops over the section of code */ - /* todo: if large section, add a branch at the start of it */ - srwi r9,r9,2 - mtctr r9 - sub r8,r8,r3 - lis r0,0x60000000@h /* nop */ -3: stw r0,0(r8) -BEGIN_FTR_SECTION - dcbst 0,r8 /* suboptimal, but simpler */ - sync - icbi 0,r8 -END_FTR_SECTION_IFSET(CPU_FTR_SPLIT_ID_CACHE) - addi r8,r8,4 - bdnz 3b - sync /* additional sync needed on g4 */ - isync - b 1b - #if defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) /* * Do an IO access in real mode diff --git a/trunk/arch/powerpc/kernel/pci_64.c b/trunk/arch/powerpc/kernel/pci_64.c index 78d3c0fc8dfb..c1b1e14775e4 100644 --- a/trunk/arch/powerpc/kernel/pci_64.c +++ b/trunk/arch/powerpc/kernel/pci_64.c @@ -30,7 +30,6 @@ #include #include #include -#include #ifdef DEBUG #include @@ -210,6 +209,7 @@ void pcibios_free_controller(struct pci_controller *phb) kfree(phb); } +#ifndef CONFIG_PPC_ISERIES void __devinit pcibios_claim_one_bus(struct pci_bus *b) { struct pci_dev *dev; @@ -238,12 +238,10 @@ static void __init pcibios_claim_of_setup(void) { struct pci_bus *b; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return; - list_for_each_entry(b, &pci_root_buses, node) pcibios_claim_one_bus(b); } +#endif #ifdef CONFIG_PPC_MULTIPLATFORM static u32 get_int_prop(struct device_node *np, const char *name, u32 def) @@ -556,8 +554,9 @@ static int __init pcibios_init(void) */ ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - iSeries_pcibios_init(); +#ifdef CONFIG_PPC_ISERIES + iSeries_pcibios_init(); +#endif printk(KERN_DEBUG "PCI: Probing PCI hardware\n"); @@ -567,15 +566,15 @@ static int __init pcibios_init(void) pci_bus_add_devices(hose->bus); } - if (!firmware_has_feature(FW_FEATURE_ISERIES)) { - if (pci_probe_only) - pcibios_claim_of_setup(); - else - /* FIXME: `else' will be removed when - pci_assign_unassigned_resources() is able to work - correctly with [partially] allocated PCI tree. */ - pci_assign_unassigned_resources(); - } +#ifndef CONFIG_PPC_ISERIES + if (pci_probe_only) + pcibios_claim_of_setup(); + else + /* FIXME: `else' will be removed when + pci_assign_unassigned_resources() is able to work + correctly with [partially] allocated PCI tree. */ + pci_assign_unassigned_resources(); +#endif /* !CONFIG_PPC_ISERIES */ /* Call machine dependent final fixup */ if (ppc_md.pcibios_fixup) @@ -587,9 +586,8 @@ static int __init pcibios_init(void) printk(KERN_DEBUG "ISA bridge at %s\n", pci_name(ppc64_isabridge_dev)); #ifdef CONFIG_PPC_MULTIPLATFORM - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - /* map in PCI I/O space */ - phbs_remap_io(); + /* map in PCI I/O space */ + phbs_remap_io(); #endif printk(KERN_DEBUG "PCI: Probing PCI hardware done\n"); @@ -639,13 +637,13 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) */ int pci_domain_nr(struct pci_bus *bus) { - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - else { - struct pci_controller *hose = pci_bus_to_host(bus); +#ifdef CONFIG_PPC_ISERIES + return 0; +#else + struct pci_controller *hose = pci_bus_to_host(bus); - return hose->global_number; - } + return hose->global_number; +#endif } EXPORT_SYMBOL(pci_domain_nr); @@ -653,12 +651,12 @@ EXPORT_SYMBOL(pci_domain_nr); /* Decide whether to display the domain number in /proc */ int pci_proc_domain(struct pci_bus *bus) { - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - else { - struct pci_controller *hose = pci_bus_to_host(bus); - return hose->buid; - } +#ifdef CONFIG_PPC_ISERIES + return 0; +#else + struct pci_controller *hose = pci_bus_to_host(bus); + return hose->buid; +#endif } /* diff --git a/trunk/arch/powerpc/kernel/setup-common.c b/trunk/arch/powerpc/kernel/setup-common.c index 89cfaf49d3de..0af3fc1bdcc9 100644 --- a/trunk/arch/powerpc/kernel/setup-common.c +++ b/trunk/arch/powerpc/kernel/setup-common.c @@ -442,6 +442,31 @@ void __init smp_setup_cpu_maps(void) } #endif /* CONFIG_SMP */ +int __initdata do_early_xmon; +#ifdef CONFIG_XMON +extern int xmon_no_auto_backtrace; + +static int __init early_xmon(char *p) +{ + /* ensure xmon is enabled */ + if (p) { + if (strncmp(p, "on", 2) == 0) + xmon_init(1); + if (strncmp(p, "off", 3) == 0) + xmon_init(0); + if (strncmp(p, "nobt", 4) == 0) + xmon_no_auto_backtrace = 1; + if (strncmp(p, "early", 5) != 0) + return 0; + } + xmon_init(1); + do_early_xmon = 1; + + return 0; +} +early_param("xmon", early_xmon); +#endif + static __init int add_pcspkr(void) { struct device_node *np; diff --git a/trunk/arch/powerpc/kernel/setup_32.c b/trunk/arch/powerpc/kernel/setup_32.c index 191d0ab09222..79a17795d17b 100644 --- a/trunk/arch/powerpc/kernel/setup_32.c +++ b/trunk/arch/powerpc/kernel/setup_32.c @@ -238,11 +238,12 @@ void __init setup_arch(char **cmdline_p) smp_setup_cpu_maps(); +#ifdef CONFIG_XMON_DEFAULT + xmon_init(1); +#endif /* Register early console */ register_early_udbg_console(); - xmon_setup(); - #if defined(CONFIG_KGDB) if (ppc_md.kgdb_map_scc) ppc_md.kgdb_map_scc(); @@ -279,6 +280,9 @@ void __init setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long) _edata; init_mm.brk = klimit; + if (do_early_xmon) + debugger(NULL); + /* set up the bootmem stuff with available memory */ do_init_bootmem(); if ( ppc_md.progress ) ppc_md.progress("setup_arch: bootmem", 0x3eab); diff --git a/trunk/arch/powerpc/kernel/setup_64.c b/trunk/arch/powerpc/kernel/setup_64.c index 4b2e32eab9dc..cda2dbe70a76 100644 --- a/trunk/arch/powerpc/kernel/setup_64.c +++ b/trunk/arch/powerpc/kernel/setup_64.c @@ -390,15 +390,19 @@ void __init setup_system(void) */ find_legacy_serial_ports(); + /* + * Initialize xmon + */ +#ifdef CONFIG_XMON_DEFAULT + xmon_init(1); +#endif /* * Register early console */ register_early_udbg_console(); - /* - * Initialize xmon - */ - xmon_setup(); + if (do_early_xmon) + debugger(NULL); check_smt_enabled(); smp_setup_cpu_maps(); diff --git a/trunk/arch/powerpc/kernel/vmlinux.lds.S b/trunk/arch/powerpc/kernel/vmlinux.lds.S index cb0e8d46c3e8..02665a02130d 100644 --- a/trunk/arch/powerpc/kernel/vmlinux.lds.S +++ b/trunk/arch/powerpc/kernel/vmlinux.lds.S @@ -132,14 +132,6 @@ SECTIONS *(__ftr_fixup) __stop___ftr_fixup = .; } -#ifdef CONFIG_PPC64 - . = ALIGN(8); - __fw_ftr_fixup : { - __start___fw_ftr_fixup = .; - *(__fw_ftr_fixup) - __stop___fw_ftr_fixup = .; - } -#endif . = ALIGN(PAGE_SIZE); .init.ramfs : { diff --git a/trunk/arch/powerpc/mm/pgtable_64.c b/trunk/arch/powerpc/mm/pgtable_64.c index ac64f4aaa509..b1da03165496 100644 --- a/trunk/arch/powerpc/mm/pgtable_64.c +++ b/trunk/arch/powerpc/mm/pgtable_64.c @@ -63,13 +63,32 @@ #include #include #include -#include #include "mmu_decl.h" unsigned long ioremap_bot = IMALLOC_BASE; static unsigned long phbs_io_bot = PHBS_IO_BASE; +#ifdef CONFIG_PPC_ISERIES + +void __iomem *ioremap(unsigned long addr, unsigned long size) +{ + return (void __iomem *)addr; +} + +extern void __iomem *__ioremap(unsigned long addr, unsigned long size, + unsigned long flags) +{ + return (void __iomem *)addr; +} + +void iounmap(volatile void __iomem *addr) +{ + return; +} + +#else + /* * map_io_page currently only called by __ioremap * map_io_page adds an entry to the ioremap page table @@ -142,9 +161,6 @@ void __iomem * __ioremap(unsigned long addr, unsigned long size, unsigned long pa, ea; void __iomem *ret; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return (void __iomem *)addr; - /* * Choose an address to map it to. * Once the imalloc system is running, we use it. @@ -239,9 +255,6 @@ void iounmap(volatile void __iomem *token) { void *addr; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return; - if (!mem_init_done) return; @@ -302,6 +315,8 @@ int iounmap_explicit(volatile void __iomem *start, unsigned long size) return 0; } +#endif + EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); diff --git a/trunk/arch/powerpc/mm/slb_low.S b/trunk/arch/powerpc/mm/slb_low.S index b10e4707d7c1..dbc1abbde038 100644 --- a/trunk/arch/powerpc/mm/slb_low.S +++ b/trunk/arch/powerpc/mm/slb_low.S @@ -21,7 +21,6 @@ #include #include #include -#include /* void slb_allocate_realmode(unsigned long ea); * @@ -184,7 +183,6 @@ slb_finish_load: * dont have any LRU information to help us choose a slot. */ #ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION /* * On iSeries, the "bolted" stack segment can be cast out on * shared processor switch so we need to check for a miss on @@ -196,7 +194,6 @@ BEGIN_FW_FTR_SECTION li r10,SLB_NUM_BOLTED-1 /* Stack goes in last bolted slot */ cmpld r9,r3 beq 3f -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) #endif /* CONFIG_PPC_ISERIES */ ld r10,PACASTABRR(r13) diff --git a/trunk/arch/powerpc/platforms/82xx/Kconfig b/trunk/arch/powerpc/platforms/82xx/Kconfig deleted file mode 100644 index 47d841ecf2e2..000000000000 --- a/trunk/arch/powerpc/platforms/82xx/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -menu "Platform support" - depends on PPC_82xx - -choice - prompt "Machine Type" - default MPC82xx_ADS - -config MPC82xx_ADS - bool "Freescale MPC82xx ADS" - select DEFAULT_UIMAGE - select PQ2ADS - select 8272 - select 8260 - select CPM2 - select FSL_SOC - help - This option enables support for the MPC8272 ADS board - -endchoice - -endmenu diff --git a/trunk/arch/powerpc/platforms/82xx/Makefile b/trunk/arch/powerpc/platforms/82xx/Makefile deleted file mode 100644 index d9fd4c84d2e0..000000000000 --- a/trunk/arch/powerpc/platforms/82xx/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the PowerPC 82xx linux kernel. -# -obj-$(CONFIG_PPC_82xx) += mpc82xx.o -obj-$(CONFIG_MPC82xx_ADS) += mpc82xx_ads.o diff --git a/trunk/arch/powerpc/platforms/82xx/m82xx_pci.h b/trunk/arch/powerpc/platforms/82xx/m82xx_pci.h deleted file mode 100644 index 9cd8893b5a32..000000000000 --- a/trunk/arch/powerpc/platforms/82xx/m82xx_pci.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _PPC_KERNEL_M82XX_PCI_H -#define _PPC_KERNEL_M82XX_PCI_H - -/* - * 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 - -#define SIU_INT_IRQ1 ((uint)0x13 + CPM_IRQ_OFFSET) - -#ifndef _IO_BASE -#define _IO_BASE isa_io_base -#endif - -#endif /* _PPC_KERNEL_M8260_PCI_H */ diff --git a/trunk/arch/powerpc/platforms/82xx/mpc82xx.c b/trunk/arch/powerpc/platforms/82xx/mpc82xx.c deleted file mode 100644 index 89d702de4863..000000000000 --- a/trunk/arch/powerpc/platforms/82xx/mpc82xx.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * MPC82xx setup and early boot code plus other random bits. - * - * Author: Vitaly Bordug - * - * Copyright (c) 2006 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#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 -#include -#include -#include - -#include -#include - -#include "pq2ads_pd.h" - -static int __init get_freq(char *name, unsigned long *val) -{ - struct device_node *cpu; - unsigned int *fp; - int found = 0; - - /* The cpu node should have timebase and clock frequency properties */ - cpu = of_find_node_by_type(NULL, "cpu"); - - if (cpu) { - fp = (unsigned int *)get_property(cpu, name, NULL); - if (fp) { - found = 1; - *val = *fp++; - } - - of_node_put(cpu); - } - - return found; -} - -void __init m82xx_calibrate_decr(void) -{ - ppc_tb_freq = 125000000; - if (!get_freq("bus-frequency", &ppc_tb_freq)) { - printk(KERN_ERR "WARNING: Estimating decrementer frequency " - "(not found)\n"); - } - ppc_tb_freq /= 4; - ppc_proc_freq = 1000000000; - if (!get_freq("clock-frequency", &ppc_proc_freq)) - printk(KERN_ERR "WARNING: Estimating processor frequency" - "(not found)\n"); -} - -void mpc82xx_ads_show_cpuinfo(struct seq_file *m) -{ - uint pvid, svid, phid1; - uint memsize = total_memory; - - pvid = mfspr(SPRN_PVR); - svid = mfspr(SPRN_SVR); - - seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); - seq_printf(m, "Machine\t\t: %s\n", CPUINFO_MACHINE); - seq_printf(m, "PVR\t\t: 0x%x\n", pvid); - seq_printf(m, "SVR\t\t: 0x%x\n", svid); - - /* Display cpu Pll setting */ - phid1 = mfspr(SPRN_HID1); - seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); - - /* Display the amount of memory */ - seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); -} diff --git a/trunk/arch/powerpc/platforms/82xx/mpc82xx_ads.c b/trunk/arch/powerpc/platforms/82xx/mpc82xx_ads.c deleted file mode 100644 index 4276f087f26e..000000000000 --- a/trunk/arch/powerpc/platforms/82xx/mpc82xx_ads.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * MPC82xx_ads setup and early boot code plus other random bits. - * - * Author: Vitaly Bordug - * m82xx_restart fix by Wade Farnsworth - * - * Copyright (c) 2006 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - - -#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 -#include -#include -#include - -#include -#include <../sysdev/cpm2_pic.h> - -#include "pq2ads_pd.h" - -#ifdef CONFIG_PCI -static uint pci_clk_frq; -static struct { - unsigned long *pci_int_stat_reg; - unsigned long *pci_int_mask_reg; -} pci_regs; - -static unsigned long pci_int_base; -static struct irq_host *pci_pic_host; -static struct device_node *pci_pic_node; -#endif - -static void __init mpc82xx_ads_pic_init(void) -{ - struct device_node *np = of_find_compatible_node(NULL, "cpm-pic", "CPM2"); - struct resource r; - cpm2_map_t *cpm_reg; - - if (np == NULL) { - printk(KERN_ERR "PIC init: can not find cpm-pic node\n"); - return; - } - if (of_address_to_resource(np, 0, &r)) { - printk(KERN_ERR "PIC init: invalid resource\n"); - of_node_put(np); - return; - } - cpm2_pic_init(np); - of_node_put(np); - - /* Initialize the default interrupt mapping priorities, - * in case the boot rom changed something on us. - */ - cpm_reg = (cpm2_map_t *) ioremap(get_immrbase(), sizeof(cpm2_map_t)); - cpm_reg->im_intctl.ic_siprr = 0x05309770; - iounmap(cpm_reg); -#ifdef CONFIG_PCI - /* Initialize stuff for the 82xx CPLD IC and install demux */ - m82xx_pci_init_irq(); -#endif -} - -static void init_fcc1_ioports(struct fs_platform_info *fpi) -{ - struct io_port *io; - u32 tempval; - cpm2_map_t *immap = ioremap(get_immrbase(), sizeof(cpm2_map_t)); - struct device_node *np; - struct resource r; - u32 *bcsr; - - np = of_find_node_by_type(NULL, "memory"); - if (!np) { - printk(KERN_INFO "No memory node in device tree\n"); - return; - } - if (of_address_to_resource(np, 1, &r)) { - printk(KERN_INFO "No memory reg property [1] in devicetree\n"); - return; - } - of_node_put(np); - bcsr = ioremap(r.start + 4, sizeof(u32)); - io = &immap->im_ioport; - - /* Enable the PHY */ - clrbits32(bcsr, BCSR1_FETHIEN); - setbits32(bcsr, BCSR1_FETH_RST); - - /* FCC1 pins are on port A/C. */ - /* Configure port A and C pins for FCC1 Ethernet. */ - - tempval = in_be32(&io->iop_pdira); - tempval &= ~PA1_DIRA0; - tempval |= PA1_DIRA1; - out_be32(&io->iop_pdira, tempval); - - tempval = in_be32(&io->iop_psora); - tempval &= ~PA1_PSORA0; - tempval |= PA1_PSORA1; - out_be32(&io->iop_psora, tempval); - - setbits32(&io->iop_ppara, PA1_DIRA0 | PA1_DIRA1); - - /* Alter clocks */ - tempval = PC_CLK(fpi->clk_tx - 8) | PC_CLK(fpi->clk_rx - 8); - - clrbits32(&io->iop_psorc, tempval); - clrbits32(&io->iop_pdirc, tempval); - setbits32(&io->iop_pparc, tempval); - - cpm2_clk_setup(CPM_CLK_FCC1, fpi->clk_rx, CPM_CLK_RX); - cpm2_clk_setup(CPM_CLK_FCC1, fpi->clk_tx, CPM_CLK_TX); - - iounmap(bcsr); - iounmap(immap); -} - -static void init_fcc2_ioports(struct fs_platform_info *fpi) -{ - cpm2_map_t *immap = ioremap(get_immrbase(), sizeof(cpm2_map_t)); - struct device_node *np; - struct resource r; - u32 *bcsr; - - struct io_port *io; - u32 tempval; - - np = of_find_node_by_type(NULL, "memory"); - if (!np) { - printk(KERN_INFO "No memory node in device tree\n"); - return; - } - if (of_address_to_resource(np, 1, &r)) { - printk(KERN_INFO "No memory reg property [1] in devicetree\n"); - return; - } - of_node_put(np); - io = &immap->im_ioport; - bcsr = ioremap(r.start + 12, sizeof(u32)); - - /* Enable the PHY */ - clrbits32(bcsr, BCSR3_FETHIEN2); - setbits32(bcsr, BCSR3_FETH2_RST); - - /* FCC2 are port B/C. */ - /* Configure port A and C pins for FCC2 Ethernet. */ - - tempval = in_be32(&io->iop_pdirb); - tempval &= ~PB2_DIRB0; - tempval |= PB2_DIRB1; - out_be32(&io->iop_pdirb, tempval); - - tempval = in_be32(&io->iop_psorb); - tempval &= ~PB2_PSORB0; - tempval |= PB2_PSORB1; - out_be32(&io->iop_psorb, tempval); - - setbits32(&io->iop_pparb, PB2_DIRB0 | PB2_DIRB1); - - tempval = PC_CLK(fpi->clk_tx - 8) | PC_CLK(fpi->clk_rx - 8); - - /* Alter clocks */ - clrbits32(&io->iop_psorc, tempval); - clrbits32(&io->iop_pdirc, tempval); - setbits32(&io->iop_pparc, tempval); - - cpm2_clk_setup(CPM_CLK_FCC2, fpi->clk_rx, CPM_CLK_RX); - cpm2_clk_setup(CPM_CLK_FCC2, fpi->clk_tx, CPM_CLK_TX); - - iounmap(bcsr); - iounmap(immap); -} - -void init_fcc_ioports(struct fs_platform_info *fpi) -{ - int fcc_no = fs_get_fcc_index(fpi->fs_no); - - switch (fcc_no) { - case 0: - init_fcc1_ioports(fpi); - break; - case 1: - init_fcc2_ioports(fpi); - break; - default: - printk(KERN_ERR "init_fcc_ioports: invalid FCC number\n"); - return; - } -} - -static void init_scc1_uart_ioports(struct fs_uart_platform_info *data) -{ - cpm2_map_t *immap = ioremap(get_immrbase(), sizeof(cpm2_map_t)); - - /* SCC1 is only on port D */ - setbits32(&immap->im_ioport.iop_ppard, 0x00000003); - clrbits32(&immap->im_ioport.iop_psord, 0x00000001); - setbits32(&immap->im_ioport.iop_psord, 0x00000002); - clrbits32(&immap->im_ioport.iop_pdird, 0x00000001); - setbits32(&immap->im_ioport.iop_pdird, 0x00000002); - - clrbits32(&immap->im_cpmux.cmx_scr, (0x00000007 << (4 - data->clk_tx))); - clrbits32(&immap->im_cpmux.cmx_scr, (0x00000038 << (4 - data->clk_rx))); - setbits32(&immap->im_cpmux.cmx_scr, - ((data->clk_tx - 1) << (4 - data->clk_tx))); - setbits32(&immap->im_cpmux.cmx_scr, - ((data->clk_rx - 1) << (4 - data->clk_rx))); - - iounmap(immap); -} - -static void init_scc4_uart_ioports(struct fs_uart_platform_info *data) -{ - cpm2_map_t *immap = ioremap(get_immrbase(), sizeof(cpm2_map_t)); - - setbits32(&immap->im_ioport.iop_ppard, 0x00000600); - clrbits32(&immap->im_ioport.iop_psord, 0x00000600); - clrbits32(&immap->im_ioport.iop_pdird, 0x00000200); - setbits32(&immap->im_ioport.iop_pdird, 0x00000400); - - clrbits32(&immap->im_cpmux.cmx_scr, (0x00000007 << (4 - data->clk_tx))); - clrbits32(&immap->im_cpmux.cmx_scr, (0x00000038 << (4 - data->clk_rx))); - setbits32(&immap->im_cpmux.cmx_scr, - ((data->clk_tx - 1) << (4 - data->clk_tx))); - setbits32(&immap->im_cpmux.cmx_scr, - ((data->clk_rx - 1) << (4 - data->clk_rx))); - - iounmap(immap); -} - -void init_scc_ioports(struct fs_uart_platform_info *data) -{ - int scc_no = fs_get_scc_index(data->fs_no); - - switch (scc_no) { - case 0: - init_scc1_uart_ioports(data); - data->brg = data->clk_rx; - break; - case 3: - init_scc4_uart_ioports(data); - data->brg = data->clk_rx; - break; - default: - printk(KERN_ERR "init_scc_ioports: invalid SCC number\n"); - return; - } -} - -void __init m82xx_board_setup(void) -{ - cpm2_map_t *immap = ioremap(get_immrbase(), sizeof(cpm2_map_t)); - struct device_node *np; - struct resource r; - u32 *bcsr; - - np = of_find_node_by_type(NULL, "memory"); - if (!np) { - printk(KERN_INFO "No memory node in device tree\n"); - return; - } - if (of_address_to_resource(np, 1, &r)) { - printk(KERN_INFO "No memory reg property [1] in devicetree\n"); - return; - } - of_node_put(np); - bcsr = ioremap(r.start + 4, sizeof(u32)); - /* Enable the 2nd UART port */ - clrbits32(bcsr, BCSR1_RS232_EN2); - -#ifdef CONFIG_SERIAL_CPM_SCC1 - clrbits32((u32 *) & immap->im_scc[0].scc_sccm, - UART_SCCM_TX | UART_SCCM_RX); - clrbits32((u32 *) & immap->im_scc[0].scc_gsmrl, - SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#endif - -#ifdef CONFIG_SERIAL_CPM_SCC2 - clrbits32((u32 *) & immap->im_scc[1].scc_sccm, - UART_SCCM_TX | UART_SCCM_RX); - clrbits32((u32 *) & immap->im_scc[1].scc_gsmrl, - SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#endif - -#ifdef CONFIG_SERIAL_CPM_SCC3 - clrbits32((u32 *) & immap->im_scc[2].scc_sccm, - UART_SCCM_TX | UART_SCCM_RX); - clrbits32((u32 *) & immap->im_scc[2].scc_gsmrl, - SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#endif - -#ifdef CONFIG_SERIAL_CPM_SCC4 - clrbits32((u32 *) & immap->im_scc[3].scc_sccm, - UART_SCCM_TX | UART_SCCM_RX); - clrbits32((u32 *) & immap->im_scc[3].scc_gsmrl, - SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#endif - - iounmap(bcsr); - iounmap(immap); -} - -#ifdef CONFIG_PCI -static void m82xx_pci_mask_irq(unsigned int irq) -{ - int bit = irq - pci_int_base; - - *pci_regs.pci_int_mask_reg |= (1 << (31 - bit)); - return; -} - -static void m82xx_pci_unmask_irq(unsigned int irq) -{ - int bit = irq - pci_int_base; - - *pci_regs.pci_int_mask_reg &= ~(1 << (31 - bit)); - return; -} - -static void m82xx_pci_mask_and_ack(unsigned int irq) -{ - int bit = irq - pci_int_base; - - *pci_regs.pci_int_mask_reg |= (1 << (31 - bit)); - return; -} - -static void m82xx_pci_end_irq(unsigned int irq) -{ - int bit = irq - pci_int_base; - - *pci_regs.pci_int_mask_reg &= ~(1 << (31 - bit)); - return; -} - -struct hw_interrupt_type m82xx_pci_ic = { - .typename = "MPC82xx ADS PCI", - .name = "MPC82xx ADS PCI", - .enable = m82xx_pci_unmask_irq, - .disable = m82xx_pci_mask_irq, - .ack = m82xx_pci_mask_and_ack, - .end = m82xx_pci_end_irq, - .mask = m82xx_pci_mask_irq, - .mask_ack = m82xx_pci_mask_and_ack, - .unmask = m82xx_pci_unmask_irq, - .eoi = m82xx_pci_end_irq, -}; - -static void -m82xx_pci_irq_demux(unsigned int irq, struct irq_desc *desc, - struct pt_regs *regs) -{ - unsigned long stat, mask, pend; - int bit; - - for (;;) { - stat = *pci_regs.pci_int_stat_reg; - mask = *pci_regs.pci_int_mask_reg; - pend = stat & ~mask & 0xf0000000; - if (!pend) - break; - for (bit = 0; pend != 0; ++bit, pend <<= 1) { - if (pend & 0x80000000) - __do_IRQ(pci_int_base + bit, regs); - } - } -} - -static int pci_pic_host_match(struct irq_host *h, struct device_node *node) -{ - return node == pci_pic_node; -} - -static int pci_pic_host_map(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) -{ - get_irq_desc(virq)->status |= IRQ_LEVEL; - set_irq_chip(virq, &m82xx_pci_ic); - return 0; -} - -static void pci_host_unmap(struct irq_host *h, unsigned int virq) -{ - /* remove chip and handler */ - set_irq_chip(virq, NULL); -} - -static struct irq_host_ops pci_pic_host_ops = { - .match = pci_pic_host_match, - .map = pci_pic_host_map, - .unmap = pci_host_unmap, -}; - -void m82xx_pci_init_irq(void) -{ - int irq; - cpm2_map_t *immap; - struct device_node *np; - struct resource r; - const u32 *regs; - unsigned int size; - const u32 *irq_map; - int i; - unsigned int irq_max, irq_min; - - if ((np = of_find_node_by_type(NULL, "soc")) == NULL) { - printk(KERN_INFO "No SOC node in device tree\n"); - return; - } - memset(&r, 0, sizeof(r)); - if (of_address_to_resource(np, 0, &r)) { - printk(KERN_INFO "No SOC reg property in device tree\n"); - return; - } - immap = ioremap(r.start, sizeof(*immap)); - of_node_put(np); - - /* install the demultiplexer for the PCI cascade interrupt */ - np = of_find_node_by_type(NULL, "pci"); - if (!np) { - printk(KERN_INFO "No pci node on device tree\n"); - iounmap(immap); - return; - } - irq_map = get_property(np, "interrupt-map", &size); - if ((!irq_map) || (size <= 7)) { - printk(KERN_INFO "No interrupt-map property of pci node\n"); - iounmap(immap); - return; - } - size /= sizeof(irq_map[0]); - for (i = 0, irq_max = 0, irq_min = 512; i < size; i += 7, irq_map += 7) { - if (irq_map[5] < irq_min) - irq_min = irq_map[5]; - if (irq_map[5] > irq_max) - irq_max = irq_map[5]; - } - pci_int_base = irq_min; - irq = irq_of_parse_and_map(np, 0); - set_irq_chained_handler(irq, m82xx_pci_irq_demux); - of_node_put(np); - np = of_find_node_by_type(NULL, "pci-pic"); - if (!np) { - printk(KERN_INFO "No pci pic node on device tree\n"); - iounmap(immap); - return; - } - pci_pic_node = of_node_get(np); - /* PCI interrupt controller registers: status and mask */ - regs = get_property(np, "reg", &size); - if ((!regs) || (size <= 2)) { - printk(KERN_INFO "No reg property in pci pic node\n"); - iounmap(immap); - return; - } - pci_regs.pci_int_stat_reg = - ioremap(regs[0], sizeof(*pci_regs.pci_int_stat_reg)); - pci_regs.pci_int_mask_reg = - ioremap(regs[1], sizeof(*pci_regs.pci_int_mask_reg)); - of_node_put(np); - /* configure chip select for PCI interrupt controller */ - immap->im_memctl.memc_br3 = regs[0] | 0x00001801; - immap->im_memctl.memc_or3 = 0xffff8010; - /* make PCI IRQ level sensitive */ - immap->im_intctl.ic_siexr &= ~(1 << (14 - (irq - SIU_INT_IRQ1))); - - /* mask all PCI interrupts */ - *pci_regs.pci_int_mask_reg |= 0xfff00000; - iounmap(immap); - pci_pic_host = - irq_alloc_host(IRQ_HOST_MAP_LINEAR, irq_max - irq_min + 1, - &pci_pic_host_ops, irq_max + 1); - return; -} - -static int m82xx_pci_exclude_device(u_char bus, u_char devfn) -{ - if (bus == 0 && PCI_SLOT(devfn) == 0) - return PCIBIOS_DEVICE_NOT_FOUND; - else - return PCIBIOS_SUCCESSFUL; -} - -static void -__init mpc82xx_pcibios_fixup(void) -{ - struct pci_dev *dev = NULL; - - for_each_pci_dev(dev) { - pci_read_irq_line(dev); - } -} - -void __init add_bridge(struct device_node *np) -{ - int len; - struct pci_controller *hose; - struct resource r; - const int *bus_range; - const void *ptr; - - memset(&r, 0, sizeof(r)); - if (of_address_to_resource(np, 0, &r)) { - printk(KERN_INFO "No PCI reg property in device tree\n"); - return; - } - if (!(ptr = get_property(np, "clock-frequency", NULL))) { - printk(KERN_INFO "No clock-frequency property in PCI node"); - return; - } - pci_clk_frq = *(uint *) ptr; - of_node_put(np); - bus_range = get_property(np, "bus-range", &len); - if (bus_range == NULL || len < 2 * sizeof(int)) { - printk(KERN_WARNING "Can't get bus-range for %s, assume" - " bus 0\n", np->full_name); - } - - pci_assign_all_buses = 1; - - hose = pcibios_alloc_controller(); - - if (!hose) - return; - - hose->arch_data = np; - hose->set_cfg_type = 1; - - hose->first_busno = bus_range ? bus_range[0] : 0; - hose->last_busno = bus_range ? bus_range[1] : 0xff; - hose->bus_offset = 0; - - hose->set_cfg_type = 1; - - setup_indirect_pci(hose, - r.start + offsetof(pci_cpm2_t, pci_cfg_addr), - r.start + offsetof(pci_cpm2_t, pci_cfg_data)); - - pci_process_bridge_OF_ranges(hose, np, 1); -} -#endif - -/* - * Setup the architecture - */ -static void __init mpc82xx_ads_setup_arch(void) -{ -#ifdef CONFIG_PCI - struct device_node *np; -#endif - - if (ppc_md.progress) - ppc_md.progress("mpc82xx_ads_setup_arch()", 0); - cpm2_reset(); - - /* Map I/O region to a 256MB BAT */ - - m82xx_board_setup(); - -#ifdef CONFIG_PCI - ppc_md.pci_exclude_device = m82xx_pci_exclude_device; - for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); - - of_node_put(np); - ppc_md.pci_map_irq = NULL; - ppc_md.pcibios_fixup = mpc82xx_pcibios_fixup; - ppc_md.pcibios_fixup_bus = NULL; -#endif - -#ifdef CONFIG_ROOT_NFS - ROOT_DEV = Root_NFS; -#else - ROOT_DEV = Root_HDA1; -#endif - - if (ppc_md.progress) - ppc_md.progress("mpc82xx_ads_setup_arch(), finish", 0); -} - -/* - * Called very early, device-tree isn't unflattened - */ -static int __init mpc82xx_ads_probe(void) -{ - /* We always match for now, eventually we should look at - * the flat dev tree to ensure this is the board we are - * supposed to run on - */ - return 1; -} - -#define RMR_CSRE 0x00000001 -static void m82xx_restart(char *cmd) -{ - __volatile__ unsigned char dummy; - - local_irq_disable(); - ((cpm2_map_t *) cpm2_immr)->im_clkrst.car_rmr |= RMR_CSRE; - - /* Clear the ME,EE,IR & DR bits in MSR to cause checkstop */ - mtmsr(mfmsr() & ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR)); - dummy = ((cpm2_map_t *) cpm2_immr)->im_clkrst.res[0]; - printk("Restart failed\n"); - while (1) ; -} - -static void m82xx_halt(void) -{ - local_irq_disable(); - while (1) ; -} - -define_machine(mpc82xx_ads) -{ - .name = "MPC82xx ADS", - .probe = mpc82xx_ads_probe, - .setup_arch = mpc82xx_ads_setup_arch, - .init_IRQ = mpc82xx_ads_pic_init, - .show_cpuinfo = mpc82xx_ads_show_cpuinfo, - .get_irq = cpm2_get_irq, - .calibrate_decr = m82xx_calibrate_decr, - .restart = m82xx_restart,.halt = m82xx_halt, -}; diff --git a/trunk/arch/powerpc/platforms/82xx/pq2ads.h b/trunk/arch/powerpc/platforms/82xx/pq2ads.h deleted file mode 100644 index a7348213508f..000000000000 --- a/trunk/arch/powerpc/platforms/82xx/pq2ads.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * PQ2/mpc8260 board-specific stuff - * - * A collection of structures, addresses, and values associated with - * the Freescale MPC8260ADS/MPC8266ADS-PCI boards. - * Copied from the RPX-Classic and SBS8260 stuff. - * - * Author: Vitaly Bordug - * - * Originally written by Dan Malek for Motorola MPC8260 family - * - * Copyright (c) 2001 Dan Malek - * Copyright (c) 2006 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifdef __KERNEL__ -#ifndef __MACH_ADS8260_DEFS -#define __MACH_ADS8260_DEFS - -#include - -#include - -/* For our show_cpuinfo hooks. */ -#define CPUINFO_VENDOR "Freescale Semiconductor" -#define CPUINFO_MACHINE "PQ2 ADS PowerPC" - -/* Backword-compatibility stuff for the drivers */ -#define CPM_MAP_ADDR ((uint)0xf0000000) -#define CPM_IRQ_OFFSET 0 - -/* The ADS8260 has 16, 32-bit wide control/status registers, accessed - * only on word boundaries. - * Not all are used (yet), or are interesting to us (yet). - */ - -/* Things of interest in the CSR. - */ -#define BCSR0_LED0 ((uint)0x02000000) /* 0 == on */ -#define BCSR0_LED1 ((uint)0x01000000) /* 0 == on */ -#define BCSR1_FETHIEN ((uint)0x08000000) /* 0 == enable*/ -#define BCSR1_FETH_RST ((uint)0x04000000) /* 0 == reset */ -#define BCSR1_RS232_EN1 ((uint)0x02000000) /* 0 ==enable */ -#define BCSR1_RS232_EN2 ((uint)0x01000000) /* 0 ==enable */ -#define BCSR3_FETHIEN2 ((uint)0x10000000) /* 0 == enable*/ -#define BCSR3_FETH2_RS ((uint)0x80000000) /* 0 == reset */ - -/* cpm serial driver works with constants below */ - -#define SIU_INT_SMC1 ((uint)0x04+CPM_IRQ_OFFSET) -#define SIU_INT_SMC2i ((uint)0x05+CPM_IRQ_OFFSET) -#define SIU_INT_SCC1 ((uint)0x28+CPM_IRQ_OFFSET) -#define SIU_INT_SCC2 ((uint)0x29+CPM_IRQ_OFFSET) -#define SIU_INT_SCC3 ((uint)0x2a+CPM_IRQ_OFFSET) -#define SIU_INT_SCC4 ((uint)0x2b+CPM_IRQ_OFFSET) - -void m82xx_pci_init_irq(void); -void mpc82xx_ads_show_cpuinfo(struct seq_file*); -void m82xx_calibrate_decr(void); - -#endif /* __MACH_ADS8260_DEFS */ -#endif /* __KERNEL__ */ diff --git a/trunk/arch/powerpc/platforms/83xx/Kconfig b/trunk/arch/powerpc/platforms/83xx/Kconfig index 0975e94ac7c4..5fe7b7faf45f 100644 --- a/trunk/arch/powerpc/platforms/83xx/Kconfig +++ b/trunk/arch/powerpc/platforms/83xx/Kconfig @@ -5,13 +5,6 @@ choice prompt "Machine Type" default MPC834x_SYS -config MPC832x_MDS - bool "Freescale MPC832x MDS" - select DEFAULT_UIMAGE - select QUICC_ENGINE - help - This option enables support for the MPC832x MDS evaluation board. - config MPC834x_SYS bool "Freescale MPC834x SYS" select DEFAULT_UIMAGE @@ -34,12 +27,6 @@ config MPC834x_ITX endchoice -config PPC_MPC832x - bool - select PPC_UDBG_16550 - select PPC_INDIRECT_PCI - default y if MPC832x_MDS - config MPC834x bool select PPC_UDBG_16550 diff --git a/trunk/arch/powerpc/platforms/83xx/mpc832x_mds.c b/trunk/arch/powerpc/platforms/83xx/mpc832x_mds.c deleted file mode 100644 index 54dea9d42dc9..000000000000 --- a/trunk/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Description: - * MPC832xE MDS board specific 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. - */ - -#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 "mpc83xx.h" -#include "mpc832x_mds.h" - -#undef DEBUG -#ifdef DEBUG -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif - -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - -static u8 *bcsr_regs = NULL; - -u8 *get_bcsr(void) -{ - return bcsr_regs; -} - -/* ************************************************************************ - * - * Setup the architecture - * - */ -static void __init mpc832x_sys_setup_arch(void) -{ - struct device_node *np; - - if (ppc_md.progress) - ppc_md.progress("mpc832x_sys_setup_arch()", 0); - - np = of_find_node_by_type(NULL, "cpu"); - if (np != 0) { - unsigned int *fp = - (int *)get_property(np, "clock-frequency", NULL); - if (fp != 0) - loops_per_jiffy = *fp / HZ; - else - loops_per_jiffy = 50000000 / HZ; - of_node_put(np); - } - - /* Map BCSR area */ - np = of_find_node_by_name(NULL, "bcsr"); - if (np != 0) { - struct resource res; - - of_address_to_resource(np, 0, &res); - bcsr_regs = ioremap(res.start, res.end - res.start +1); - of_node_put(np); - } - -#ifdef CONFIG_PCI - for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); - - ppc_md.pci_swizzle = common_swizzle; - ppc_md.pci_exclude_device = mpc83xx_exclude_device; -#endif - -#ifdef CONFIG_QUICC_ENGINE - qe_reset(); - - if ((np = of_find_node_by_name(np, "par_io")) != NULL) { - par_io_init(np); - of_node_put(np); - - for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) - par_io_of_config(np); - } - - if ((np = of_find_compatible_node(NULL, "network", "ucc_geth")) - != NULL){ - /* Reset the Ethernet PHY */ - bcsr_regs[9] &= ~0x20; - udelay(1000); - bcsr_regs[9] |= 0x20; - iounmap(bcsr_regs); - of_node_put(np); - } - -#endif /* CONFIG_QUICC_ENGINE */ - -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) - ROOT_DEV = Root_RAM0; - else -#endif -#ifdef CONFIG_ROOT_NFS - ROOT_DEV = Root_NFS; -#else - ROOT_DEV = Root_HDA1; -#endif -} - -void __init mpc832x_sys_init_IRQ(void) -{ - - struct device_node *np; - - np = of_find_node_by_type(NULL, "ipic"); - if (!np) - return; - - ipic_init(np, 0); - - /* Initialize the default interrupt mapping priorities, - * in case the boot rom changed something on us. - */ - ipic_set_default_priority(); - of_node_put(np); - -#ifdef CONFIG_QUICC_ENGINE - np = of_find_node_by_type(NULL, "qeic"); - if (!np) - return; - - qe_ic_init(np, 0); - of_node_put(np); -#endif /* CONFIG_QUICC_ENGINE */ -} - -#if defined(CONFIG_I2C_MPC) && defined(CONFIG_SENSORS_DS1374) -extern ulong ds1374_get_rtc_time(void); -extern int ds1374_set_rtc_time(ulong); - -static int __init mpc832x_rtc_hookup(void) -{ - struct timespec tv; - - ppc_md.get_rtc_time = ds1374_get_rtc_time; - ppc_md.set_rtc_time = ds1374_set_rtc_time; - - tv.tv_nsec = 0; - tv.tv_sec = (ppc_md.get_rtc_time) (); - do_settimeofday(&tv); - - return 0; -} - -late_initcall(mpc832x_rtc_hookup); -#endif - -/* - * Called very early, MMU is off, device-tree isn't unflattened - */ -static int __init mpc832x_sys_probe(void) -{ - char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), - "model", NULL); - - if (model == NULL) - return 0; - if (strcmp(model, "MPC8323EMDS")) - return 0; - - DBG("%s found\n", model); - - return 1; -} - -define_machine(mpc832x_mds) { - .name = "MPC832x MDS", - .probe = mpc832x_sys_probe, - .setup_arch = mpc832x_sys_setup_arch, - .init_IRQ = mpc832x_sys_init_IRQ, - .get_irq = ipic_get_irq, - .restart = mpc83xx_restart, - .time_init = mpc83xx_time_init, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -}; diff --git a/trunk/arch/powerpc/platforms/83xx/mpc832x_mds.h b/trunk/arch/powerpc/platforms/83xx/mpc832x_mds.h deleted file mode 100644 index a49588904f8a..000000000000 --- a/trunk/arch/powerpc/platforms/83xx/mpc832x_mds.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Description: - * MPC832x MDS board specific header. - * - * 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. - * - */ - -#ifndef __MACH_MPC832x_MDS_H__ -#define __MACH_MPC832x_MDS_H__ - -extern u8 *get_bcsr(void); - -#endif /* __MACH_MPC832x_MDS_H__ */ diff --git a/trunk/arch/powerpc/platforms/83xx/mpc834x_itx.c b/trunk/arch/powerpc/platforms/83xx/mpc834x_itx.c index 8c676d763bb0..5446bab08eca 100644 --- a/trunk/arch/powerpc/platforms/83xx/mpc834x_itx.c +++ b/trunk/arch/powerpc/platforms/83xx/mpc834x_itx.c @@ -11,7 +11,6 @@ * option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/powerpc/platforms/83xx/mpc8360e_pb.c b/trunk/arch/powerpc/platforms/83xx/mpc8360e_pb.c deleted file mode 100644 index c0191900fc25..000000000000 --- a/trunk/arch/powerpc/platforms/83xx/mpc8360e_pb.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Author: Li Yang - * Yin Olivia - * - * Description: - * MPC8360E MDS PB board specific routines. - * - * Changelog: - * Jun 21, 2006 Initial version - * - * 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 -#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 "mpc83xx.h" - -#undef DEBUG -#ifdef DEBUG -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif - -#ifndef CONFIG_PCI -unsigned long isa_io_base = 0; -unsigned long isa_mem_base = 0; -#endif - -static u8 *bcsr_regs = NULL; - -u8 *get_bcsr(void) -{ - return bcsr_regs; -} - -/* ************************************************************************ - * - * Setup the architecture - * - */ -static void __init mpc8360_sys_setup_arch(void) -{ - struct device_node *np; - - if (ppc_md.progress) - ppc_md.progress("mpc8360_sys_setup_arch()", 0); - - np = of_find_node_by_type(NULL, "cpu"); - if (np != 0) { - const unsigned int *fp = - get_property(np, "clock-frequency", NULL); - if (fp != 0) - loops_per_jiffy = *fp / HZ; - else - loops_per_jiffy = 50000000 / HZ; - of_node_put(np); - } - - /* Map BCSR area */ - np = of_find_node_by_name(NULL, "bcsr"); - if (np != 0) { - struct resource res; - - of_address_to_resource(np, 0, &res); - bcsr_regs = ioremap(res.start, res.end - res.start +1); - of_node_put(np); - } - -#ifdef CONFIG_PCI - for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;) - add_bridge(np); - - ppc_md.pci_swizzle = common_swizzle; - ppc_md.pci_exclude_device = mpc83xx_exclude_device; -#endif - -#ifdef CONFIG_QUICC_ENGINE - qe_reset(); - - if ((np = of_find_node_by_name(np, "par_io")) != NULL) { - par_io_init(np); - of_node_put(np); - - for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) - par_io_of_config(np); - } - - if ((np = of_find_compatible_node(NULL, "network", "ucc_geth")) - != NULL){ - /* Reset the Ethernet PHY */ - bcsr_regs[9] &= ~0x20; - udelay(1000); - bcsr_regs[9] |= 0x20; - iounmap(bcsr_regs); - of_node_put(np); - } - -#endif /* CONFIG_QUICC_ENGINE */ - -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) - ROOT_DEV = Root_RAM0; - else -#endif -#ifdef CONFIG_ROOT_NFS - ROOT_DEV = Root_NFS; -#else - ROOT_DEV = Root_HDA1; -#endif -} - -void __init mpc8360_sys_init_IRQ(void) -{ - - struct device_node *np; - - np = of_find_node_by_type(NULL, "ipic"); - if (!np) - return; - - ipic_init(np, 0); - - /* Initialize the default interrupt mapping priorities, - * in case the boot rom changed something on us. - */ - ipic_set_default_priority(); - of_node_put(np); - -#ifdef CONFIG_QUICC_ENGINE - np = of_find_node_by_type(NULL, "qeic"); - if (!np) - return; - - qe_ic_init(np, 0); - of_node_put(np); -#endif /* CONFIG_QUICC_ENGINE */ -} - -#if defined(CONFIG_I2C_MPC) && defined(CONFIG_SENSORS_DS1374) -extern ulong ds1374_get_rtc_time(void); -extern int ds1374_set_rtc_time(ulong); - -static int __init mpc8360_rtc_hookup(void) -{ - struct timespec tv; - - ppc_md.get_rtc_time = ds1374_get_rtc_time; - ppc_md.set_rtc_time = ds1374_set_rtc_time; - - tv.tv_nsec = 0; - tv.tv_sec = (ppc_md.get_rtc_time) (); - do_settimeofday(&tv); - - return 0; -} - -late_initcall(mpc8360_rtc_hookup); -#endif - -/* - * Called very early, MMU is off, device-tree isn't unflattened - */ -static int __init mpc8360_sys_probe(void) -{ - char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), - "model", NULL); - if (model == NULL) - return 0; - if (strcmp(model, "MPC8360EPB")) - return 0; - - DBG("MPC8360EMDS-PB found\n"); - - return 1; -} - -define_machine(mpc8360_sys) { - .name = "MPC8360E PB", - .probe = mpc8360_sys_probe, - .setup_arch = mpc8360_sys_setup_arch, - .init_IRQ = mpc8360_sys_init_IRQ, - .get_irq = ipic_get_irq, - .restart = mpc83xx_restart, - .time_init = mpc83xx_time_init, - .calibrate_decr = generic_calibrate_decr, - .progress = udbg_progress, -}; diff --git a/trunk/arch/powerpc/platforms/85xx/mpc85xx_ads.h b/trunk/arch/powerpc/platforms/85xx/mpc85xx_ads.h index effcbf78f851..46c3532992aa 100644 --- a/trunk/arch/powerpc/platforms/85xx/mpc85xx_ads.h +++ b/trunk/arch/powerpc/platforms/85xx/mpc85xx_ads.h @@ -18,7 +18,6 @@ #ifndef __MACH_MPC85XXADS_H #define __MACH_MPC85XXADS_H -#include #include #include diff --git a/trunk/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/trunk/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 4c1fede6470e..193a5d7921b5 100644 --- a/trunk/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/trunk/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -11,7 +11,6 @@ * option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/powerpc/platforms/cell/cbe_regs.c b/trunk/arch/powerpc/platforms/cell/cbe_regs.c index 3f3859d12e00..2f194ba29899 100644 --- a/trunk/arch/powerpc/platforms/cell/cbe_regs.c +++ b/trunk/arch/powerpc/platforms/cell/cbe_regs.c @@ -6,8 +6,6 @@ * (c) 2006 Benjamin Herrenschmidt , IBM Corp. */ - -#include #include #include diff --git a/trunk/arch/powerpc/platforms/cell/interrupt.c b/trunk/arch/powerpc/platforms/cell/interrupt.c index 6cc59e0b4582..6b57a47c5d37 100644 --- a/trunk/arch/powerpc/platforms/cell/interrupt.c +++ b/trunk/arch/powerpc/platforms/cell/interrupt.c @@ -21,12 +21,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * TODO: - * - Fix various assumptions related to HW CPU numbers vs. linux CPU numbers - * vs node numbers in the setup code - * - Implement proper handling of maxcpus=1/2 (that is, routing of irqs from - * a non-active node to the active node) */ #include @@ -50,25 +44,24 @@ struct iic { u8 target_id; u8 eoi_stack[16]; int eoi_ptr; - struct device_node *node; + struct irq_host *host; }; static DEFINE_PER_CPU(struct iic, iic); #define IIC_NODE_COUNT 2 -static struct irq_host *iic_host; +static struct irq_host *iic_hosts[IIC_NODE_COUNT]; /* Convert between "pending" bits and hw irq number */ static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits) { unsigned char unit = bits.source & 0xf; - unsigned char node = bits.source >> 4; - unsigned char class = bits.class & 3; - /* Decode IPIs */ if (bits.flags & CBE_IIC_IRQ_IPI) - return IIC_IRQ_TYPE_IPI | (bits.prio >> 4); + return IIC_IRQ_IPI0 | (bits.prio >> 4); + else if (bits.class <= 3) + return (bits.class << 4) | unit; else - return (node << IIC_IRQ_NODE_SHIFT) | (class << 4) | unit; + return IIC_IRQ_INVALID; } static void iic_mask(unsigned int irq) @@ -93,70 +86,21 @@ static struct irq_chip iic_chip = { .eoi = iic_eoi, }; - -static void iic_ioexc_eoi(unsigned int irq) -{ -} - -static void iic_ioexc_cascade(unsigned int irq, struct irq_desc *desc, - struct pt_regs *regs) -{ - struct cbe_iic_regs *node_iic = desc->handler_data; - unsigned int base = (irq & 0xffffff00) | IIC_IRQ_TYPE_IOEXC; - unsigned long bits, ack; - int cascade; - - for (;;) { - bits = in_be64(&node_iic->iic_is); - if (bits == 0) - break; - /* pre-ack edge interrupts */ - ack = bits & IIC_ISR_EDGE_MASK; - if (ack) - out_be64(&node_iic->iic_is, ack); - /* handle them */ - for (cascade = 63; cascade >= 0; cascade--) - if (bits & (0x8000000000000000UL >> cascade)) { - unsigned int cirq = - irq_linear_revmap(iic_host, - base | cascade); - if (cirq != NO_IRQ) - generic_handle_irq(cirq, regs); - } - /* post-ack level interrupts */ - ack = bits & ~IIC_ISR_EDGE_MASK; - if (ack) - out_be64(&node_iic->iic_is, ack); - } - desc->chip->eoi(irq); -} - - -static struct irq_chip iic_ioexc_chip = { - .typename = " CELL-IOEX", - .mask = iic_mask, - .unmask = iic_unmask, - .eoi = iic_ioexc_eoi, -}; - /* Get an IRQ number from the pending state register of the IIC */ static unsigned int iic_get_irq(struct pt_regs *regs) { struct cbe_iic_pending_bits pending; struct iic *iic; - unsigned int virq; iic = &__get_cpu_var(iic); *(unsigned long *) &pending = in_be64((unsigned long __iomem *) &iic->regs->pending_destr); - if (!(pending.flags & CBE_IIC_IRQ_VALID)) - return NO_IRQ; - virq = irq_linear_revmap(iic_host, iic_pending_to_hwnum(pending)); - if (virq == NO_IRQ) - return NO_IRQ; iic->eoi_stack[++iic->eoi_ptr] = pending.prio; BUG_ON(iic->eoi_ptr > 15); - return virq; + if (pending.flags & CBE_IIC_IRQ_VALID) + return irq_linear_revmap(iic->host, + iic_pending_to_hwnum(pending)); + return NO_IRQ; } #ifdef CONFIG_SMP @@ -164,7 +108,12 @@ static unsigned int iic_get_irq(struct pt_regs *regs) /* Use the highest interrupt priorities for IPI */ static inline int iic_ipi_to_irq(int ipi) { - return IIC_IRQ_TYPE_IPI + 0xf - ipi; + return IIC_IRQ_IPI0 + IIC_NUM_IPIS - 1 - ipi; +} + +static inline int iic_irq_to_ipi(int irq) +{ + return IIC_NUM_IPIS - 1 - (irq - IIC_IRQ_IPI0); } void iic_setup_cpu(void) @@ -174,7 +123,7 @@ void iic_setup_cpu(void) void iic_cause_IPI(int cpu, int mesg) { - out_be64(&per_cpu(iic, cpu).regs->generate, (0xf - mesg) << 4); + out_be64(&per_cpu(iic, cpu).regs->generate, (IIC_NUM_IPIS - 1 - mesg) << 4); } u8 iic_get_target_id(int cpu) @@ -185,7 +134,9 @@ EXPORT_SYMBOL_GPL(iic_get_target_id); struct irq_host *iic_get_irq_host(int node) { - return iic_host; + if (node < 0 || node >= IIC_NODE_COUNT) + return NULL; + return iic_hosts[node]; } EXPORT_SYMBOL_GPL(iic_get_irq_host); @@ -198,20 +149,34 @@ static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) return IRQ_HANDLED; } + static void iic_request_ipi(int ipi, const char *name) { - int virq; + int node, virq; - virq = irq_create_mapping(iic_host, iic_ipi_to_irq(ipi)); - if (virq == NO_IRQ) { - printk(KERN_ERR - "iic: failed to map IPI %s\n", name); - return; + for (node = 0; node < IIC_NODE_COUNT; node++) { + char *rname; + if (iic_hosts[node] == NULL) + continue; + virq = irq_create_mapping(iic_hosts[node], + iic_ipi_to_irq(ipi)); + if (virq == NO_IRQ) { + printk(KERN_ERR + "iic: failed to map IPI %s on node %d\n", + name, node); + continue; + } + rname = kzalloc(strlen(name) + 16, GFP_KERNEL); + if (rname) + sprintf(rname, "%s node %d", name, node); + else + rname = (char *)name; + if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, + rname, (void *)(long)ipi)) + printk(KERN_ERR + "iic: failed to request IPI %s on node %d\n", + name, node); } - if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, name, - (void *)(long)ipi)) - printk(KERN_ERR - "iic: failed to request IPI %s\n", name); } void iic_request_IPIs(void) @@ -228,24 +193,16 @@ void iic_request_IPIs(void) static int iic_host_match(struct irq_host *h, struct device_node *node) { - return device_is_compatible(node, - "IBM,CBEA-Internal-Interrupt-Controller"); + return h->host_data != NULL && node == h->host_data; } static int iic_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) { - switch (hw & IIC_IRQ_TYPE_MASK) { - case IIC_IRQ_TYPE_IPI: - set_irq_chip_and_handler(virq, &iic_chip, handle_percpu_irq); - break; - case IIC_IRQ_TYPE_IOEXC: - set_irq_chip_and_handler(virq, &iic_ioexc_chip, - handle_fasteoi_irq); - break; - default: + if (hw < IIC_IRQ_IPI0) set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq); - } + else + set_irq_chip_and_handler(virq, &iic_chip, handle_percpu_irq); return 0; } @@ -254,39 +211,11 @@ static int iic_host_xlate(struct irq_host *h, struct device_node *ct, irq_hw_number_t *out_hwirq, unsigned int *out_flags) { - unsigned int node, ext, unit, class; - const u32 *val; - - if (!device_is_compatible(ct, - "IBM,CBEA-Internal-Interrupt-Controller")) - return -ENODEV; - if (intsize != 1) - return -ENODEV; - val = get_property(ct, "#interrupt-cells", NULL); - if (val == NULL || *val != 1) - return -ENODEV; - - node = intspec[0] >> 24; - ext = (intspec[0] >> 16) & 0xff; - class = (intspec[0] >> 8) & 0xff; - unit = intspec[0] & 0xff; - - /* Check if node is in supported range */ - if (node > 1) - return -EINVAL; - - /* Build up interrupt number, special case for IO exceptions */ - *out_hwirq = (node << IIC_IRQ_NODE_SHIFT); - if (unit == IIC_UNIT_IIC && class == 1) - *out_hwirq |= IIC_IRQ_TYPE_IOEXC | ext; - else - *out_hwirq |= IIC_IRQ_TYPE_NORMAL | - (class << IIC_IRQ_CLASS_SHIFT) | unit; - - /* Dummy flags, ignored by iic code */ - *out_flags = IRQ_TYPE_EDGE_RISING; - - return 0; + /* Currently, we don't translate anything. That needs to be fixed as + * we get better defined device-trees. iic interrupts have to be + * explicitely mapped by whoever needs them + */ + return -ENODEV; } static struct irq_host_ops iic_host_ops = { @@ -296,7 +225,7 @@ static struct irq_host_ops iic_host_ops = { }; static void __init init_one_iic(unsigned int hw_cpu, unsigned long addr, - struct device_node *node) + struct irq_host *host) { /* XXX FIXME: should locate the linux CPU number from the HW cpu * number properly. We are lucky for now @@ -308,19 +237,19 @@ static void __init init_one_iic(unsigned int hw_cpu, unsigned long addr, iic->target_id = ((hw_cpu & 2) << 3) | ((hw_cpu & 1) ? 0xf : 0xe); iic->eoi_stack[0] = 0xff; - iic->node = of_node_get(node); + iic->host = host; out_be64(&iic->regs->prio, 0); - printk(KERN_INFO "IIC for CPU %d target id 0x%x : %s\n", - hw_cpu, iic->target_id, node->full_name); + printk(KERN_INFO "IIC for CPU %d at %lx mapped to %p, target id 0x%x\n", + hw_cpu, addr, iic->regs, iic->target_id); } static int __init setup_iic(void) { struct device_node *dn; struct resource r0, r1; - unsigned int node, cascade, found = 0; - struct cbe_iic_regs *node_iic; + struct irq_host *host; + int found = 0; const u32 *np; for (dn = NULL; @@ -340,33 +269,19 @@ static int __init setup_iic(void) of_node_put(dn); return -ENODEV; } - found++; - init_one_iic(np[0], r0.start, dn); - init_one_iic(np[1], r1.start, dn); - - /* Setup cascade for IO exceptions. XXX cleanup tricks to get - * node vs CPU etc... - * Note that we configure the IIC_IRR here with a hard coded - * priority of 1. We might want to improve that later. - */ - node = np[0] >> 1; - node_iic = cbe_get_cpu_iic_regs(np[0]); - cascade = node << IIC_IRQ_NODE_SHIFT; - cascade |= 1 << IIC_IRQ_CLASS_SHIFT; - cascade |= IIC_UNIT_IIC; - cascade = irq_create_mapping(iic_host, cascade); - if (cascade == NO_IRQ) - continue; - set_irq_data(cascade, node_iic); - set_irq_chained_handler(cascade , iic_ioexc_cascade); - out_be64(&node_iic->iic_ir, - (1 << 12) /* priority */ | - (node << 4) /* dest node */ | - IIC_UNIT_THREAD_0 /* route them to thread 0 */); - /* Flush pending (make sure it triggers if there is - * anything pending - */ - out_be64(&node_iic->iic_is, 0xfffffffffffffffful); + host = NULL; + if (found < IIC_NODE_COUNT) { + host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, + IIC_SOURCE_COUNT, + &iic_host_ops, + IIC_IRQ_INVALID); + iic_hosts[found] = host; + BUG_ON(iic_hosts[found] == NULL); + iic_hosts[found]->host_data = of_node_get(dn); + found++; + } + init_one_iic(np[0], r0.start, host); + init_one_iic(np[1], r1.start, host); } if (found) @@ -377,12 +292,6 @@ static int __init setup_iic(void) void __init iic_init_IRQ(void) { - /* Setup an irq host data structure */ - iic_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, IIC_SOURCE_COUNT, - &iic_host_ops, IIC_IRQ_INVALID); - BUG_ON(iic_host == NULL); - irq_set_default_host(iic_host); - /* Discover and initialize iics */ if (setup_iic() < 0) panic("IIC: Failed to initialize !\n"); diff --git a/trunk/arch/powerpc/platforms/cell/interrupt.h b/trunk/arch/powerpc/platforms/cell/interrupt.h index 9ba1d3c17b4b..5560a92ec3ab 100644 --- a/trunk/arch/powerpc/platforms/cell/interrupt.h +++ b/trunk/arch/powerpc/platforms/cell/interrupt.h @@ -2,76 +2,48 @@ #define ASM_CELL_PIC_H #ifdef __KERNEL__ /* - * Mapping of IIC pending bits into per-node interrupt numbers. + * Mapping of IIC pending bits into per-node + * interrupt numbers. * - * Interrupt numbers are in the range 0...0x1ff where the top bit - * (0x100) represent the source node. Only 2 nodes are supported with - * the current code though it's trivial to extend that if necessary using - * higher level bits + * IRQ FF CC SS PP FF CC SS PP Description * - * The bottom 8 bits are split into 2 type bits and 6 data bits that - * depend on the type: + * 00-3f 80 02 +0 00 - 80 02 +0 3f South Bridge + * 00-3f 80 02 +b 00 - 80 02 +b 3f South Bridge + * 41-4a 80 00 +1 ** - 80 00 +a ** SPU Class 0 + * 51-5a 80 01 +1 ** - 80 01 +a ** SPU Class 1 + * 61-6a 80 02 +1 ** - 80 02 +a ** SPU Class 2 + * 70-7f C0 ** ** 00 - C0 ** ** 0f IPI * - * 00 (0x00 | data) : normal interrupt. data is (class << 4) | source - * 01 (0x40 | data) : IO exception. data is the exception number as - * defined by bit numbers in IIC_SR - * 10 (0x80 | data) : IPI. data is the IPI number (obtained from the priority) - * and node is always 0 (IPIs are per-cpu, their source is - * not relevant) - * 11 (0xc0 | data) : reserved + * F flags + * C class + * S source + * P Priority + * + node number + * * don't care * - * In addition, interrupt number 0x80000000 is defined as always invalid - * (that is the node field is expected to never extend to move than 23 bits) + * A node consists of a Cell Broadband Engine and an optional + * south bridge device providing a maximum of 64 IRQs. + * The south bridge may be connected to either IOIF0 + * or IOIF1. + * Each SPE is represented as three IRQ lines, one per + * interrupt class. + * 16 IRQ numbers are reserved for inter processor + * interruptions, although these are only used in the + * range of the first node. * + * This scheme needs 128 IRQ numbers per BIF node ID, + * which means that with the total of 512 lines + * available, we can have a maximum of four nodes. */ enum { - IIC_IRQ_INVALID = 0x80000000u, - IIC_IRQ_NODE_MASK = 0x100, - IIC_IRQ_NODE_SHIFT = 8, - IIC_IRQ_MAX = 0x1ff, - IIC_IRQ_TYPE_MASK = 0xc0, - IIC_IRQ_TYPE_NORMAL = 0x00, - IIC_IRQ_TYPE_IOEXC = 0x40, - IIC_IRQ_TYPE_IPI = 0x80, - IIC_IRQ_CLASS_SHIFT = 4, - IIC_IRQ_CLASS_0 = 0x00, - IIC_IRQ_CLASS_1 = 0x10, - IIC_IRQ_CLASS_2 = 0x20, - IIC_SOURCE_COUNT = 0x200, - - /* Here are defined the various source/dest units. Avoid using those - * definitions if you can, they are mostly here for reference - */ - IIC_UNIT_SPU_0 = 0x4, - IIC_UNIT_SPU_1 = 0x7, - IIC_UNIT_SPU_2 = 0x3, - IIC_UNIT_SPU_3 = 0x8, - IIC_UNIT_SPU_4 = 0x2, - IIC_UNIT_SPU_5 = 0x9, - IIC_UNIT_SPU_6 = 0x1, - IIC_UNIT_SPU_7 = 0xa, - IIC_UNIT_IOC_0 = 0x0, - IIC_UNIT_IOC_1 = 0xb, - IIC_UNIT_THREAD_0 = 0xe, /* target only */ - IIC_UNIT_THREAD_1 = 0xf, /* target only */ - IIC_UNIT_IIC = 0xe, /* source only (IO exceptions) */ - - /* Base numbers for the external interrupts */ - IIC_IRQ_EXT_IOIF0 = - IIC_IRQ_TYPE_NORMAL | IIC_IRQ_CLASS_2 | IIC_UNIT_IOC_0, - IIC_IRQ_EXT_IOIF1 = - IIC_IRQ_TYPE_NORMAL | IIC_IRQ_CLASS_2 | IIC_UNIT_IOC_1, - - /* Base numbers for the IIC_ISR interrupts */ - IIC_IRQ_IOEX_TMI = IIC_IRQ_TYPE_IOEXC | IIC_IRQ_CLASS_1 | 63, - IIC_IRQ_IOEX_PMI = IIC_IRQ_TYPE_IOEXC | IIC_IRQ_CLASS_1 | 62, - IIC_IRQ_IOEX_ATI = IIC_IRQ_TYPE_IOEXC | IIC_IRQ_CLASS_1 | 61, - IIC_IRQ_IOEX_MATBFI = IIC_IRQ_TYPE_IOEXC | IIC_IRQ_CLASS_1 | 60, - IIC_IRQ_IOEX_ELDI = IIC_IRQ_TYPE_IOEXC | IIC_IRQ_CLASS_1 | 59, - - /* Which bits in IIC_ISR are edge sensitive */ - IIC_ISR_EDGE_MASK = 0x4ul, + IIC_IRQ_INVALID = 0xff, + IIC_IRQ_MAX = 0x3f, + IIC_IRQ_EXT_IOIF0 = 0x20, + IIC_IRQ_EXT_IOIF1 = 0x2b, + IIC_IRQ_IPI0 = 0x40, + IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */ + IIC_SOURCE_COUNT = 0x50, }; extern void iic_init_IRQ(void); @@ -80,6 +52,7 @@ extern void iic_request_IPIs(void); extern void iic_setup_cpu(void); extern u8 iic_get_target_id(int cpu); +extern struct irq_host *iic_get_irq_host(int node); extern void spider_init_IRQ(void); diff --git a/trunk/arch/powerpc/platforms/cell/ras.c b/trunk/arch/powerpc/platforms/cell/ras.c index 033ad6e2827b..0984c7071695 100644 --- a/trunk/arch/powerpc/platforms/cell/ras.c +++ b/trunk/arch/powerpc/platforms/cell/ras.c @@ -1,6 +1,5 @@ #define DEBUG -#include #include #include #include diff --git a/trunk/arch/powerpc/platforms/cell/spider-pic.c b/trunk/arch/powerpc/platforms/cell/spider-pic.c index 608b1ebc56b2..742a03282b44 100644 --- a/trunk/arch/powerpc/platforms/cell/spider-pic.c +++ b/trunk/arch/powerpc/platforms/cell/spider-pic.c @@ -243,6 +243,7 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) const u32 *imap, *tmp; int imaplen, intsize, unit; struct device_node *iic; + struct irq_host *iic_host; #if 0 /* Enable that when we have a way to retreive the node as well */ /* First, we check wether we have a real "interrupts" in the device @@ -288,11 +289,11 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) * the iic host from the iic OF node, but that way I'm still compatible * with really really old old firmwares for which we don't have a node */ + iic_host = iic_get_irq_host(pic->node_id); + if (iic_host == NULL) + return NO_IRQ; /* Manufacture an IIC interrupt number of class 2 */ - virq = irq_create_mapping(NULL, - (pic->node_id << IIC_IRQ_NODE_SHIFT) | - (2 << IIC_IRQ_CLASS_SHIFT) | - unit); + virq = irq_create_mapping(iic_host, 0x20 | unit); if (virq == NO_IRQ) printk(KERN_ERR "spider_pic: failed to map cascade !"); return virq; diff --git a/trunk/arch/powerpc/platforms/cell/spu_base.c b/trunk/arch/powerpc/platforms/cell/spu_base.c index f78680346e5f..0f5c8ebc7fc3 100644 --- a/trunk/arch/powerpc/platforms/cell/spu_base.c +++ b/trunk/arch/powerpc/platforms/cell/spu_base.c @@ -568,23 +568,24 @@ static void spu_unmap(struct spu *spu) /* This function shall be abstracted for HV platforms */ static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) { + struct irq_host *host; unsigned int isrc; const u32 *tmp; - /* Get the interrupt source unit from the device-tree */ + host = iic_get_irq_host(spu->node); + if (host == NULL) + return -ENODEV; + + /* Get the interrupt source from the device-tree */ tmp = get_property(np, "isrc", NULL); if (!tmp) return -ENODEV; - isrc = tmp[0]; - - /* Add the node number */ - isrc |= spu->node << IIC_IRQ_NODE_SHIFT; - spu->isrc = isrc; + spu->isrc = isrc = tmp[0]; /* Now map interrupts of all 3 classes */ - spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); - spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); - spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); + spu->irqs[0] = irq_create_mapping(host, 0x00 | isrc); + spu->irqs[1] = irq_create_mapping(host, 0x10 | isrc); + spu->irqs[2] = irq_create_mapping(host, 0x20 | isrc); /* Right now, we only fail if class 2 failed */ return spu->irqs[2] == NO_IRQ ? -EINVAL : 0; diff --git a/trunk/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/trunk/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index e4f2b9df5e17..cb6f084844f2 100644 --- a/trunk/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/trunk/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -18,7 +18,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/powerpc/platforms/iseries/pci.c b/trunk/arch/powerpc/platforms/iseries/pci.c index 4aa165e010d9..3eb12065df23 100644 --- a/trunk/arch/powerpc/platforms/iseries/pci.c +++ b/trunk/arch/powerpc/platforms/iseries/pci.c @@ -262,6 +262,14 @@ void __init iSeries_pci_final_fixup(void) mf_display_src(0xC9000200); } +void pcibios_fixup_bus(struct pci_bus *PciBus) +{ +} + +void pcibios_fixup_resources(struct pci_dev *pdev) +{ +} + /* * Look down the chain to find the matching Device Device */ diff --git a/trunk/arch/powerpc/platforms/iseries/setup.c b/trunk/arch/powerpc/platforms/iseries/setup.c index a0ff7ba7d666..7f1953066ff8 100644 --- a/trunk/arch/powerpc/platforms/iseries/setup.c +++ b/trunk/arch/powerpc/platforms/iseries/setup.c @@ -649,21 +649,15 @@ static void iseries_dedicated_idle(void) void __init iSeries_init_IRQ(void) { } #endif -/* - * iSeries has no legacy IO, anything calling this function has to - * fail or bad things will happen - */ -static int iseries_check_legacy_ioport(unsigned int baseport) -{ - return -ENODEV; -} - static int __init iseries_probe(void) { unsigned long root = of_get_flat_dt_root(); if (!of_flat_dt_is_compatible(root, "IBM,iSeries")) return 0; + powerpc_firmware_features |= FW_FEATURE_ISERIES; + powerpc_firmware_features |= FW_FEATURE_LPAR; + hpte_init_iSeries(); return 1; @@ -686,7 +680,6 @@ define_machine(iseries) { .calibrate_decr = generic_calibrate_decr, .progress = iSeries_progress, .probe = iseries_probe, - .check_legacy_ioport = iseries_check_legacy_ioport, /* XXX Implement enable_pmcs for iSeries */ }; @@ -694,9 +687,6 @@ void * __init iSeries_early_setup(void) { unsigned long phys_mem_size; - powerpc_firmware_features |= FW_FEATURE_ISERIES; - powerpc_firmware_features |= FW_FEATURE_LPAR; - iSeries_fixup_klimit(); /* diff --git a/trunk/arch/powerpc/platforms/pasemi/setup.c b/trunk/arch/powerpc/platforms/pasemi/setup.c index 628482671c15..106896c3b60a 100644 --- a/trunk/arch/powerpc/platforms/pasemi/setup.c +++ b/trunk/arch/powerpc/platforms/pasemi/setup.c @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/powerpc/platforms/pasemi/time.c b/trunk/arch/powerpc/platforms/pasemi/time.c index 9bd410b8fec6..fa54351ac268 100644 --- a/trunk/arch/powerpc/platforms/pasemi/time.c +++ b/trunk/arch/powerpc/platforms/pasemi/time.c @@ -17,7 +17,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include diff --git a/trunk/arch/powerpc/platforms/powermac/udbg_scc.c b/trunk/arch/powerpc/platforms/powermac/udbg_scc.c index 379db05b0082..ce1a235855f7 100644 --- a/trunk/arch/powerpc/platforms/powermac/udbg_scc.c +++ b/trunk/arch/powerpc/platforms/powermac/udbg_scc.c @@ -111,6 +111,8 @@ void udbg_scc_init(int force_scc) pmac_call_feature(PMAC_FTR_SCC_ENABLE, ch, PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); + + /* Setup for 57600 8N1 */ if (ch == ch_a) addr += 0x20; sccc = ioremap(addr & PAGE_MASK, PAGE_SIZE) ; @@ -123,21 +125,9 @@ void udbg_scc_init(int force_scc) x = in_8(sccc); out_8(sccc, 0x09); /* reset A or B side */ out_8(sccc, 0xc0); - - /* If SCC was the OF output port, read the BRG value, else - * Setup for 57600 8N1 - */ - if (ch_def != NULL) { - out_8(sccc, 13); - scc_inittab[1] = in_8(sccc); - out_8(sccc, 12); - scc_inittab[3] = in_8(sccc); - } - for (i = 0; i < sizeof(scc_inittab); ++i) out_8(sccc, scc_inittab[i]); - udbg_putc = udbg_scc_putc; udbg_getc = udbg_scc_getc; udbg_getc_poll = udbg_scc_getc_poll; diff --git a/trunk/arch/powerpc/platforms/pseries/setup.c b/trunk/arch/powerpc/platforms/pseries/setup.c index f82b13e531a3..43dbf737698c 100644 --- a/trunk/arch/powerpc/platforms/pseries/setup.c +++ b/trunk/arch/powerpc/platforms/pseries/setup.c @@ -180,7 +180,7 @@ static void __init pseries_mpic_init_IRQ(void) cascade_irq = irq_of_parse_and_map(cascade, 0); if (cascade == NO_IRQ) { - printk(KERN_ERR "mpic: failed to map cascade interrupt"); + printk(KERN_ERR "xics: failed to map cascade interrupt"); return; } diff --git a/trunk/arch/powerpc/sysdev/Makefile b/trunk/arch/powerpc/sysdev/Makefile index 91f052d8cce0..f15f4d78aee9 100644 --- a/trunk/arch/powerpc/sysdev/Makefile +++ b/trunk/arch/powerpc/sysdev/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o obj-$(CONFIG_FSL_SOC) += fsl_soc.o obj-$(CONFIG_PPC_TODC) += todc.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o -obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ ifeq ($(CONFIG_PPC_MERGE),y) obj-$(CONFIG_PPC_I8259) += i8259.o diff --git a/trunk/arch/powerpc/sysdev/cpm2_pic.c b/trunk/arch/powerpc/sysdev/cpm2_pic.c index 28b018994746..51752990f7b9 100644 --- a/trunk/arch/powerpc/sysdev/cpm2_pic.c +++ b/trunk/arch/powerpc/sysdev/cpm2_pic.c @@ -147,7 +147,7 @@ static struct irq_chip cpm2_pic = { .end = cpm2_end_irq, }; -unsigned int cpm2_get_irq(struct pt_regs *regs) +int cpm2_get_irq(struct pt_regs *regs) { int irq; unsigned long bits; diff --git a/trunk/arch/powerpc/sysdev/cpm2_pic.h b/trunk/arch/powerpc/sysdev/cpm2_pic.h index 3c513e5a688e..d63e45d4df58 100644 --- a/trunk/arch/powerpc/sysdev/cpm2_pic.h +++ b/trunk/arch/powerpc/sysdev/cpm2_pic.h @@ -3,7 +3,7 @@ extern intctl_cpm2_t *cpm2_intctl; -extern unsigned int cpm2_get_irq(struct pt_regs *regs); +extern int cpm2_get_irq(struct pt_regs *regs); extern void cpm2_pic_init(struct device_node*); diff --git a/trunk/arch/powerpc/sysdev/fsl_soc.c b/trunk/arch/powerpc/sysdev/fsl_soc.c index 7d759f1c26b1..022ed275ea68 100644 --- a/trunk/arch/powerpc/sysdev/fsl_soc.c +++ b/trunk/arch/powerpc/sysdev/fsl_soc.c @@ -37,7 +37,6 @@ #include extern void init_fcc_ioports(struct fs_platform_info*); -extern void init_scc_ioports(struct fs_uart_platform_info*); static phys_addr_t immrbase = -1; phys_addr_t get_immrbase(void) @@ -567,7 +566,7 @@ static int __init fs_enet_of_init(void) struct resource r[4]; struct device_node *phy, *mdio; struct fs_platform_info fs_enet_data; - const unsigned int *id, *phy_addr, phy_irq; + const unsigned int *id, *phy_addr; const void *mac_addr; const phandle *ph; const char *model; @@ -589,7 +588,6 @@ static int __init fs_enet_of_init(void) if (ret) goto err; r[2].name = fcc_regs_c; - fs_enet_data.fcc_regs_c = r[2].start; r[3].start = r[3].end = irq_of_parse_and_map(np, 0); r[3].flags = IORESOURCE_IRQ; @@ -622,8 +620,6 @@ static int __init fs_enet_of_init(void) phy_addr = get_property(phy, "reg", NULL); fs_enet_data.phy_addr = *phy_addr; - phy_irq = get_property(phy, "interrupts", NULL); - id = get_property(np, "device-id", NULL); fs_enet_data.fs_no = *id; strcpy(fs_enet_data.fs_type, model); @@ -641,7 +637,6 @@ static int __init fs_enet_of_init(void) if (strstr(model, "FCC")) { int fcc_index = *id - 1; - unsigned char* mdio_bb_prop; fs_enet_data.dpram_offset = (u32)cpm_dpram_addr(0); fs_enet_data.rx_ring = 32; @@ -657,57 +652,14 @@ static int __init fs_enet_of_init(void) (u32)res.start, fs_enet_data.phy_addr); fs_enet_data.bus_id = (char*)&bus_id[(*id)]; fs_enet_data.init_ioports = init_fcc_ioports; + } - mdio_bb_prop = get_property(phy, "bitbang", NULL); - if (mdio_bb_prop) { - struct platform_device *fs_enet_mdio_bb_dev; - struct fs_mii_bb_platform_info fs_enet_mdio_bb_data; - - fs_enet_mdio_bb_dev = - platform_device_register_simple("fsl-bb-mdio", - i, NULL, 0); - memset(&fs_enet_mdio_bb_data, 0, - sizeof(struct fs_mii_bb_platform_info)); - fs_enet_mdio_bb_data.mdio_dat.bit = - mdio_bb_prop[0]; - fs_enet_mdio_bb_data.mdio_dir.bit = - mdio_bb_prop[1]; - fs_enet_mdio_bb_data.mdc_dat.bit = - mdio_bb_prop[2]; - fs_enet_mdio_bb_data.mdio_port = - mdio_bb_prop[3]; - fs_enet_mdio_bb_data.mdc_port = - mdio_bb_prop[4]; - fs_enet_mdio_bb_data.delay = - mdio_bb_prop[5]; - - fs_enet_mdio_bb_data.irq[0] = phy_irq[0]; - fs_enet_mdio_bb_data.irq[1] = -1; - fs_enet_mdio_bb_data.irq[2] = -1; - fs_enet_mdio_bb_data.irq[3] = phy_irq[0]; - fs_enet_mdio_bb_data.irq[31] = -1; - - fs_enet_mdio_bb_data.mdio_dat.offset = - (u32)&cpm2_immr->im_ioport.iop_pdatc; - fs_enet_mdio_bb_data.mdio_dir.offset = - (u32)&cpm2_immr->im_ioport.iop_pdirc; - fs_enet_mdio_bb_data.mdc_dat.offset = - (u32)&cpm2_immr->im_ioport.iop_pdatc; - - ret = platform_device_add_data( - fs_enet_mdio_bb_dev, - &fs_enet_mdio_bb_data, - sizeof(struct fs_mii_bb_platform_info)); - if (ret) - goto unreg; - } - - of_node_put(phy); - of_node_put(mdio); + of_node_put(phy); + of_node_put(mdio); - ret = platform_device_add_data(fs_enet_dev, &fs_enet_data, - sizeof(struct - fs_platform_info)); + ret = platform_device_add_data(fs_enet_dev, &fs_enet_data, + sizeof(struct + fs_platform_info)); if (ret) goto unreg; } diff --git a/trunk/arch/powerpc/sysdev/mpic.c b/trunk/arch/powerpc/sysdev/mpic.c index 3ee03a9a98fa..723972bb5bd9 100644 --- a/trunk/arch/powerpc/sysdev/mpic.c +++ b/trunk/arch/powerpc/sysdev/mpic.c @@ -341,7 +341,7 @@ static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); if (id == PCI_CAP_ID_HT) { id = readb(devbase + pos + 3); - if (id == HT_CAPTYPE_IRQ) + if (id == 0x80) break; } } diff --git a/trunk/arch/powerpc/sysdev/qe_lib/Kconfig b/trunk/arch/powerpc/sysdev/qe_lib/Kconfig deleted file mode 100644 index a725e80befa8..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -# -# QE Communication options -# - -menu "QE Options" - depends on QUICC_ENGINE - -config UCC_SLOW - bool "UCC Slow Protocols Support" - default n - select UCC - help - This option provides qe_lib support to UCC slow - protocols: UART, BISYNC, QMC - -config UCC_FAST - bool "UCC Fast Protocols Support" - default n - select UCC - select UCC_SLOW - help - This option provides qe_lib support to UCC fast - protocols: HDLC, Ethernet, ATM, transparent - -config UCC - bool - default y if UCC_FAST || UCC_SLOW - -endmenu - diff --git a/trunk/arch/powerpc/sysdev/qe_lib/Makefile b/trunk/arch/powerpc/sysdev/qe_lib/Makefile deleted file mode 100644 index 874fe1a5b1cf..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the linux ppc-specific parts of QE -# -obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o - -obj-$(CONFIG_UCC) += ucc.o -obj-$(CONFIG_UCC_SLOW) += ucc_slow.o -obj-$(CONFIG_UCC_FAST) += ucc_fast.o diff --git a/trunk/arch/powerpc/sysdev/qe_lib/qe.c b/trunk/arch/powerpc/sysdev/qe_lib/qe.c deleted file mode 100644 index 2bae632d3ad7..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/qe.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * Based on cpm2_common.c from Dan Malek (dmalek@jlc.net) - * - * Description: - * General Purpose functions for the global management of the - * QUICC Engine (QE). - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void qe_snums_init(void); -static void qe_muram_init(void); -static int qe_sdma_init(void); - -static DEFINE_SPINLOCK(qe_lock); - -/* QE snum state */ -enum qe_snum_state { - QE_SNUM_STATE_USED, - QE_SNUM_STATE_FREE -}; - -/* QE snum */ -struct qe_snum { - u8 num; - enum qe_snum_state state; -}; - -/* We allocate this here because it is used almost exclusively for - * the communication processor devices. - */ -struct qe_immap *qe_immr = NULL; -EXPORT_SYMBOL(qe_immr); - -static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ - -static phys_addr_t qebase = -1; - -phys_addr_t get_qe_base(void) -{ - struct device_node *qe; - - if (qebase != -1) - return qebase; - - qe = of_find_node_by_type(NULL, "qe"); - if (qe) { - unsigned int size; - const void *prop = get_property(qe, "reg", &size); - qebase = of_translate_address(qe, prop); - of_node_put(qe); - }; - - return qebase; -} - -EXPORT_SYMBOL(get_qe_base); - -void qe_reset(void) -{ - if (qe_immr == NULL) - qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE); - - qe_snums_init(); - - qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, - QE_CR_PROTOCOL_UNSPECIFIED, 0); - - /* Reclaim the MURAM memory for our use. */ - qe_muram_init(); - - if (qe_sdma_init()) - panic("sdma init failed!"); -} - -int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) -{ - unsigned long flags; - u8 mcn_shift = 0, dev_shift = 0; - - spin_lock_irqsave(&qe_lock, flags); - if (cmd == QE_RESET) { - out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG)); - } else { - if (cmd == QE_ASSIGN_PAGE) { - /* Here device is the SNUM, not sub-block */ - dev_shift = QE_CR_SNUM_SHIFT; - } else if (cmd == QE_ASSIGN_RISC) { - /* Here device is the SNUM, and mcnProtocol is - * e_QeCmdRiscAssignment value */ - dev_shift = QE_CR_SNUM_SHIFT; - mcn_shift = QE_CR_MCN_RISC_ASSIGN_SHIFT; - } else { - if (device == QE_CR_SUBBLOCK_USB) - mcn_shift = QE_CR_MCN_USB_SHIFT; - else - mcn_shift = QE_CR_MCN_NORMAL_SHIFT; - } - - out_be32(&qe_immr->cp.cecdr, - immrbar_virt_to_phys((void *)cmd_input)); - out_be32(&qe_immr->cp.cecr, - (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32) - mcn_protocol << mcn_shift)); - } - - /* wait for the QE_CR_FLG to clear */ - while(in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) - cpu_relax(); - spin_unlock_irqrestore(&qe_lock, flags); - - return 0; -} -EXPORT_SYMBOL(qe_issue_cmd); - -/* Set a baud rate generator. This needs lots of work. There are - * 16 BRGs, which can be connected to the QE channels or output - * as clocks. The BRGs are in two different block of internal - * memory mapped space. - * The baud rate clock is the system clock divided by something. - * It was set up long ago during the initial boot phase and is - * is given to us. - * Baud rate clocks are zero-based in the driver code (as that maps - * to port numbers). Documentation uses 1-based numbering. - */ -static unsigned int brg_clk = 0; - -unsigned int get_brg_clk(void) -{ - struct device_node *qe; - if (brg_clk) - return brg_clk; - - qe = of_find_node_by_type(NULL, "qe"); - if (qe) { - unsigned int size; - const u32 *prop = get_property(qe, "brg-frequency", &size); - brg_clk = *prop; - of_node_put(qe); - }; - return brg_clk; -} - -/* This function is used by UARTS, or anything else that uses a 16x - * oversampled clock. - */ -void qe_setbrg(u32 brg, u32 rate) -{ - volatile u32 *bp; - u32 divisor, tempval; - int div16 = 0; - - bp = &qe_immr->brg.brgc1; - bp += brg; - - divisor = (get_brg_clk() / rate); - if (divisor > QE_BRGC_DIVISOR_MAX + 1) { - div16 = 1; - divisor /= 16; - } - - tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE; - if (div16) - tempval |= QE_BRGC_DIV16; - - out_be32(bp, tempval); -} - -/* Initialize SNUMs (thread serial numbers) according to - * QE Module Control chapter, SNUM table - */ -static void qe_snums_init(void) -{ - int i; - static const u8 snum_init[] = { - 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, - 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, - 0x98, 0x99, 0xA8, 0xA9, 0xB8, 0xB9, 0xC8, 0xC9, - 0xD8, 0xD9, 0xE8, 0xE9, - }; - - for (i = 0; i < QE_NUM_OF_SNUM; i++) { - snums[i].num = snum_init[i]; - snums[i].state = QE_SNUM_STATE_FREE; - } -} - -int qe_get_snum(void) -{ - unsigned long flags; - int snum = -EBUSY; - int i; - - spin_lock_irqsave(&qe_lock, flags); - for (i = 0; i < QE_NUM_OF_SNUM; i++) { - if (snums[i].state == QE_SNUM_STATE_FREE) { - snums[i].state = QE_SNUM_STATE_USED; - snum = snums[i].num; - break; - } - } - spin_unlock_irqrestore(&qe_lock, flags); - - return snum; -} -EXPORT_SYMBOL(qe_get_snum); - -void qe_put_snum(u8 snum) -{ - int i; - - for (i = 0; i < QE_NUM_OF_SNUM; i++) { - if (snums[i].num == snum) { - snums[i].state = QE_SNUM_STATE_FREE; - break; - } - } -} -EXPORT_SYMBOL(qe_put_snum); - -static int qe_sdma_init(void) -{ - struct sdma *sdma = &qe_immr->sdma; - u32 sdma_buf_offset; - - if (!sdma) - return -ENODEV; - - /* allocate 2 internal temporary buffers (512 bytes size each) for - * the SDMA */ - sdma_buf_offset = qe_muram_alloc(512 * 2, 64); - if (IS_MURAM_ERR(sdma_buf_offset)) - return -ENOMEM; - - out_be32(&sdma->sdebcr, sdma_buf_offset & QE_SDEBCR_BA_MASK); - out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK | (0x1 >> - QE_SDMR_CEN_SHIFT))); - - return 0; -} - -/* - * muram_alloc / muram_free bits. - */ -static DEFINE_SPINLOCK(qe_muram_lock); - -/* 16 blocks should be enough to satisfy all requests - * until the memory subsystem goes up... */ -static rh_block_t qe_boot_muram_rh_block[16]; -static rh_info_t qe_muram_info; - -static void qe_muram_init(void) -{ - struct device_node *np; - u32 address; - u64 size; - unsigned int flags; - - /* initialize the info header */ - rh_init(&qe_muram_info, 1, - sizeof(qe_boot_muram_rh_block) / - sizeof(qe_boot_muram_rh_block[0]), qe_boot_muram_rh_block); - - /* Attach the usable muram area */ - /* XXX: This is a subset of the available muram. It - * varies with the processor and the microcode patches activated. - */ - if ((np = of_find_node_by_name(NULL, "data-only")) != NULL) { - address = *of_get_address(np, 0, &size, &flags); - of_node_put(np); - rh_attach_region(&qe_muram_info, - (void *)address, (int)size); - } -} - -/* This function returns an index into the MURAM area. - */ -u32 qe_muram_alloc(u32 size, u32 align) -{ - void *start; - unsigned long flags; - - spin_lock_irqsave(&qe_muram_lock, flags); - start = rh_alloc_align(&qe_muram_info, size, align, "QE"); - spin_unlock_irqrestore(&qe_muram_lock, flags); - - return (u32) start; -} -EXPORT_SYMBOL(qe_muram_alloc); - -int qe_muram_free(u32 offset) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&qe_muram_lock, flags); - ret = rh_free(&qe_muram_info, (void *)offset); - spin_unlock_irqrestore(&qe_muram_lock, flags); - - return ret; -} -EXPORT_SYMBOL(qe_muram_free); - -/* not sure if this is ever needed */ -u32 qe_muram_alloc_fixed(u32 offset, u32 size) -{ - void *start; - unsigned long flags; - - spin_lock_irqsave(&qe_muram_lock, flags); - start = rh_alloc_fixed(&qe_muram_info, (void *)offset, size, "commproc"); - spin_unlock_irqrestore(&qe_muram_lock, flags); - - return (u32) start; -} -EXPORT_SYMBOL(qe_muram_alloc_fixed); - -void qe_muram_dump(void) -{ - rh_dump(&qe_muram_info); -} -EXPORT_SYMBOL(qe_muram_dump); - -void *qe_muram_addr(u32 offset) -{ - return (void *)&qe_immr->muram[offset]; -} -EXPORT_SYMBOL(qe_muram_addr); diff --git a/trunk/arch/powerpc/sysdev/qe_lib/qe_ic.c b/trunk/arch/powerpc/sysdev/qe_lib/qe_ic.c deleted file mode 100644 index c229d07d4957..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/qe_ic.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/qe_ic.c - * - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Author: Li Yang - * Based on code from Shlomi Gridish - * - * QUICC ENGINE Interrupt Controller - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qe_ic.h" - -static DEFINE_SPINLOCK(qe_ic_lock); - -static struct qe_ic_info qe_ic_info[] = { - [1] = { - .mask = 0x00008000, - .mask_reg = QEIC_CIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPWCC, - }, - [2] = { - .mask = 0x00004000, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPWCC, - }, - [3] = { - .mask = 0x00002000, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPWCC, - }, - [10] = { - .mask = 0x00000040, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPZCC, - }, - [11] = { - .mask = 0x00000020, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPZCC, - }, - [12] = { - .mask = 0x00000010, - .mask_reg = QEIC_CIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPZCC, - }, - [13] = { - .mask = 0x00000008, - .mask_reg = QEIC_CIMR, - .pri_code = 4, - .pri_reg = QEIC_CIPZCC, - }, - [14] = { - .mask = 0x00000004, - .mask_reg = QEIC_CIMR, - .pri_code = 5, - .pri_reg = QEIC_CIPZCC, - }, - [15] = { - .mask = 0x00000002, - .mask_reg = QEIC_CIMR, - .pri_code = 6, - .pri_reg = QEIC_CIPZCC, - }, - [20] = { - .mask = 0x10000000, - .mask_reg = QEIC_CRIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPRTA, - }, - [25] = { - .mask = 0x00800000, - .mask_reg = QEIC_CRIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPRTB, - }, - [26] = { - .mask = 0x00400000, - .mask_reg = QEIC_CRIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPRTB, - }, - [27] = { - .mask = 0x00200000, - .mask_reg = QEIC_CRIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPRTB, - }, - [28] = { - .mask = 0x00100000, - .mask_reg = QEIC_CRIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPRTB, - }, - [32] = { - .mask = 0x80000000, - .mask_reg = QEIC_CIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPXCC, - }, - [33] = { - .mask = 0x40000000, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPXCC, - }, - [34] = { - .mask = 0x20000000, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPXCC, - }, - [35] = { - .mask = 0x10000000, - .mask_reg = QEIC_CIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPXCC, - }, - [36] = { - .mask = 0x08000000, - .mask_reg = QEIC_CIMR, - .pri_code = 4, - .pri_reg = QEIC_CIPXCC, - }, - [40] = { - .mask = 0x00800000, - .mask_reg = QEIC_CIMR, - .pri_code = 0, - .pri_reg = QEIC_CIPYCC, - }, - [41] = { - .mask = 0x00400000, - .mask_reg = QEIC_CIMR, - .pri_code = 1, - .pri_reg = QEIC_CIPYCC, - }, - [42] = { - .mask = 0x00200000, - .mask_reg = QEIC_CIMR, - .pri_code = 2, - .pri_reg = QEIC_CIPYCC, - }, - [43] = { - .mask = 0x00100000, - .mask_reg = QEIC_CIMR, - .pri_code = 3, - .pri_reg = QEIC_CIPYCC, - }, -}; - -static inline u32 qe_ic_read(volatile __be32 __iomem * base, unsigned int reg) -{ - return in_be32(base + (reg >> 2)); -} - -static inline void qe_ic_write(volatile __be32 __iomem * base, unsigned int reg, - u32 value) -{ - out_be32(base + (reg >> 2), value); -} - -static inline struct qe_ic *qe_ic_from_irq(unsigned int virq) -{ - return irq_desc[virq].chip_data; -} - -#define virq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq) - -static void qe_ic_unmask_irq(unsigned int virq) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - unsigned long flags; - u32 temp; - - spin_lock_irqsave(&qe_ic_lock, flags); - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); - qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, - temp | qe_ic_info[src].mask); - - spin_unlock_irqrestore(&qe_ic_lock, flags); -} - -static void qe_ic_mask_irq(unsigned int virq) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - unsigned long flags; - u32 temp; - - spin_lock_irqsave(&qe_ic_lock, flags); - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); - qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, - temp & ~qe_ic_info[src].mask); - - spin_unlock_irqrestore(&qe_ic_lock, flags); -} - -static void qe_ic_mask_irq_and_ack(unsigned int virq) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - unsigned long flags; - u32 temp; - - spin_lock_irqsave(&qe_ic_lock, flags); - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); - qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, - temp & ~qe_ic_info[src].mask); - - /* There is nothing to do for ack here, ack is handled in ISR */ - - spin_unlock_irqrestore(&qe_ic_lock, flags); -} - -static struct irq_chip qe_ic_irq_chip = { - .typename = " QEIC ", - .unmask = qe_ic_unmask_irq, - .mask = qe_ic_mask_irq, - .mask_ack = qe_ic_mask_irq_and_ack, -}; - -static int qe_ic_host_match(struct irq_host *h, struct device_node *node) -{ - struct qe_ic *qe_ic = h->host_data; - - /* Exact match, unless qe_ic node is NULL */ - return qe_ic->of_node == NULL || qe_ic->of_node == node; -} - -static int qe_ic_host_map(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct qe_ic *qe_ic = h->host_data; - struct irq_chip *chip; - - if (qe_ic_info[hw].mask == 0) { - printk(KERN_ERR "Can't map reserved IRQ \n"); - return -EINVAL; - } - /* Default chip */ - chip = &qe_ic->hc_irq; - - set_irq_chip_data(virq, qe_ic); - get_irq_desc(virq)->status |= IRQ_LEVEL; - - set_irq_chip_and_handler(virq, chip, handle_level_irq); - - return 0; -} - -static int qe_ic_host_xlate(struct irq_host *h, struct device_node *ct, - u32 * intspec, unsigned int intsize, - irq_hw_number_t * out_hwirq, - unsigned int *out_flags) -{ - *out_hwirq = intspec[0]; - if (intsize > 1) - *out_flags = intspec[1]; - else - *out_flags = IRQ_TYPE_NONE; - return 0; -} - -static struct irq_host_ops qe_ic_host_ops = { - .match = qe_ic_host_match, - .map = qe_ic_host_map, - .xlate = qe_ic_host_xlate, -}; - -/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ -unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic, struct pt_regs *regs) -{ - int irq; - - BUG_ON(qe_ic == NULL); - - /* get the interrupt source vector. */ - irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26; - - if (irq == 0) - return NO_IRQ; - - return irq_linear_revmap(qe_ic->irqhost, irq); -} - -/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ -unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic, struct pt_regs *regs) -{ - int irq; - - BUG_ON(qe_ic == NULL); - - /* get the interrupt source vector. */ - irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26; - - if (irq == 0) - return NO_IRQ; - - return irq_linear_revmap(qe_ic->irqhost, irq); -} - -/* FIXME: We mask all the QE Low interrupts while handling. We should - * let other interrupt come in, but BAD interrupts are generated */ -void fastcall qe_ic_cascade_low(unsigned int irq, struct irq_desc *desc, - struct pt_regs *regs) -{ - struct qe_ic *qe_ic = desc->handler_data; - struct irq_chip *chip = irq_desc[irq].chip; - - unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic, regs); - - chip->mask_ack(irq); - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq, regs); - chip->unmask(irq); -} - -/* FIXME: We mask all the QE High interrupts while handling. We should - * let other interrupt come in, but BAD interrupts are generated */ -void fastcall qe_ic_cascade_high(unsigned int irq, struct irq_desc *desc, - struct pt_regs *regs) -{ - struct qe_ic *qe_ic = desc->handler_data; - struct irq_chip *chip = irq_desc[irq].chip; - - unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic, regs); - - chip->mask_ack(irq); - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq, regs); - chip->unmask(irq); -} - -void __init qe_ic_init(struct device_node *node, unsigned int flags) -{ - struct qe_ic *qe_ic; - struct resource res; - u32 temp = 0, ret, high_active = 0; - - qe_ic = alloc_bootmem(sizeof(struct qe_ic)); - if (qe_ic == NULL) - return; - - memset(qe_ic, 0, sizeof(struct qe_ic)); - qe_ic->of_node = node ? of_node_get(node) : NULL; - - qe_ic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, - NR_QE_IC_INTS, &qe_ic_host_ops, 0); - if (qe_ic->irqhost == NULL) { - of_node_put(node); - return; - } - - ret = of_address_to_resource(node, 0, &res); - if (ret) - return; - - qe_ic->regs = ioremap(res.start, res.end - res.start + 1); - - qe_ic->irqhost->host_data = qe_ic; - qe_ic->hc_irq = qe_ic_irq_chip; - - qe_ic->virq_high = irq_of_parse_and_map(node, 0); - qe_ic->virq_low = irq_of_parse_and_map(node, 1); - - if (qe_ic->virq_low == NO_IRQ) { - printk(KERN_ERR "Failed to map QE_IC low IRQ\n"); - return; - } - - /* default priority scheme is grouped. If spread mode is */ - /* required, configure cicr accordingly. */ - if (flags & QE_IC_SPREADMODE_GRP_W) - temp |= CICR_GWCC; - if (flags & QE_IC_SPREADMODE_GRP_X) - temp |= CICR_GXCC; - if (flags & QE_IC_SPREADMODE_GRP_Y) - temp |= CICR_GYCC; - if (flags & QE_IC_SPREADMODE_GRP_Z) - temp |= CICR_GZCC; - if (flags & QE_IC_SPREADMODE_GRP_RISCA) - temp |= CICR_GRTA; - if (flags & QE_IC_SPREADMODE_GRP_RISCB) - temp |= CICR_GRTB; - - /* choose destination signal for highest priority interrupt */ - if (flags & QE_IC_HIGH_SIGNAL) { - temp |= (SIGNAL_HIGH << CICR_HPIT_SHIFT); - high_active = 1; - } - - qe_ic_write(qe_ic->regs, QEIC_CICR, temp); - - set_irq_data(qe_ic->virq_low, qe_ic); - set_irq_chained_handler(qe_ic->virq_low, qe_ic_cascade_low); - - if (qe_ic->virq_high != NO_IRQ) { - set_irq_data(qe_ic->virq_high, qe_ic); - set_irq_chained_handler(qe_ic->virq_high, qe_ic_cascade_high); - } - - printk("QEIC (%d IRQ sources) at %p\n", NR_QE_IC_INTS, qe_ic->regs); -} - -void qe_ic_set_highest_priority(unsigned int virq, int high) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - u32 temp = 0; - - temp = qe_ic_read(qe_ic->regs, QEIC_CICR); - - temp &= ~CICR_HP_MASK; - temp |= src << CICR_HP_SHIFT; - - temp &= ~CICR_HPIT_MASK; - temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << CICR_HPIT_SHIFT; - - qe_ic_write(qe_ic->regs, QEIC_CICR, temp); -} - -/* Set Priority level within its group, from 1 to 8 */ -int qe_ic_set_priority(unsigned int virq, unsigned int priority) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - u32 temp; - - if (priority > 8 || priority == 0) - return -EINVAL; - if (src > 127) - return -EINVAL; - if (qe_ic_info[src].pri_reg == 0) - return -EINVAL; - - temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].pri_reg); - - if (priority < 4) { - temp &= ~(0x7 << (32 - priority * 3)); - temp |= qe_ic_info[src].pri_code << (32 - priority * 3); - } else { - temp &= ~(0x7 << (24 - priority * 3)); - temp |= qe_ic_info[src].pri_code << (24 - priority * 3); - } - - qe_ic_write(qe_ic->regs, qe_ic_info[src].pri_reg, temp); - - return 0; -} - -/* Set a QE priority to use high irq, only priority 1~2 can use high irq */ -int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high) -{ - struct qe_ic *qe_ic = qe_ic_from_irq(virq); - unsigned int src = virq_to_hw(virq); - u32 temp, control_reg = QEIC_CICNR, shift = 0; - - if (priority > 2 || priority == 0) - return -EINVAL; - - switch (qe_ic_info[src].pri_reg) { - case QEIC_CIPZCC: - shift = CICNR_ZCC1T_SHIFT; - break; - case QEIC_CIPWCC: - shift = CICNR_WCC1T_SHIFT; - break; - case QEIC_CIPYCC: - shift = CICNR_YCC1T_SHIFT; - break; - case QEIC_CIPXCC: - shift = CICNR_XCC1T_SHIFT; - break; - case QEIC_CIPRTA: - shift = CRICR_RTA1T_SHIFT; - control_reg = QEIC_CRICR; - break; - case QEIC_CIPRTB: - shift = CRICR_RTB1T_SHIFT; - control_reg = QEIC_CRICR; - break; - default: - return -EINVAL; - } - - shift += (2 - priority) * 2; - temp = qe_ic_read(qe_ic->regs, control_reg); - temp &= ~(SIGNAL_MASK << shift); - temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << shift; - qe_ic_write(qe_ic->regs, control_reg, temp); - - return 0; -} - -static struct sysdev_class qe_ic_sysclass = { - set_kset_name("qe_ic"), -}; - -static struct sys_device device_qe_ic = { - .id = 0, - .cls = &qe_ic_sysclass, -}; - -static int __init init_qe_ic_sysfs(void) -{ - int rc; - - printk(KERN_DEBUG "Registering qe_ic with sysfs...\n"); - - rc = sysdev_class_register(&qe_ic_sysclass); - if (rc) { - printk(KERN_ERR "Failed registering qe_ic sys class\n"); - return -ENODEV; - } - rc = sysdev_register(&device_qe_ic); - if (rc) { - printk(KERN_ERR "Failed registering qe_ic sys device\n"); - return -ENODEV; - } - return 0; -} - -subsys_initcall(init_qe_ic_sysfs); diff --git a/trunk/arch/powerpc/sysdev/qe_lib/qe_ic.h b/trunk/arch/powerpc/sysdev/qe_lib/qe_ic.h deleted file mode 100644 index 9a631adb189d..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/qe_ic.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/qe_ic.h - * - * QUICC ENGINE Interrupt Controller Header - * - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Author: Li Yang - * Based on code from Shlomi Gridish - * - * 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. - */ -#ifndef _POWERPC_SYSDEV_QE_IC_H -#define _POWERPC_SYSDEV_QE_IC_H - -#include - -#define NR_QE_IC_INTS 64 - -/* QE IC registers offset */ -#define QEIC_CICR 0x00 -#define QEIC_CIVEC 0x04 -#define QEIC_CRIPNR 0x08 -#define QEIC_CIPNR 0x0c -#define QEIC_CIPXCC 0x10 -#define QEIC_CIPYCC 0x14 -#define QEIC_CIPWCC 0x18 -#define QEIC_CIPZCC 0x1c -#define QEIC_CIMR 0x20 -#define QEIC_CRIMR 0x24 -#define QEIC_CICNR 0x28 -#define QEIC_CIPRTA 0x30 -#define QEIC_CIPRTB 0x34 -#define QEIC_CRICR 0x3c -#define QEIC_CHIVEC 0x60 - -/* Interrupt priority registers */ -#define CIPCC_SHIFT_PRI0 29 -#define CIPCC_SHIFT_PRI1 26 -#define CIPCC_SHIFT_PRI2 23 -#define CIPCC_SHIFT_PRI3 20 -#define CIPCC_SHIFT_PRI4 13 -#define CIPCC_SHIFT_PRI5 10 -#define CIPCC_SHIFT_PRI6 7 -#define CIPCC_SHIFT_PRI7 4 - -/* CICR priority modes */ -#define CICR_GWCC 0x00040000 -#define CICR_GXCC 0x00020000 -#define CICR_GYCC 0x00010000 -#define CICR_GZCC 0x00080000 -#define CICR_GRTA 0x00200000 -#define CICR_GRTB 0x00400000 -#define CICR_HPIT_SHIFT 8 -#define CICR_HPIT_MASK 0x00000300 -#define CICR_HP_SHIFT 24 -#define CICR_HP_MASK 0x3f000000 - -/* CICNR */ -#define CICNR_WCC1T_SHIFT 20 -#define CICNR_ZCC1T_SHIFT 28 -#define CICNR_YCC1T_SHIFT 12 -#define CICNR_XCC1T_SHIFT 4 - -/* CRICR */ -#define CRICR_RTA1T_SHIFT 20 -#define CRICR_RTB1T_SHIFT 28 - -/* Signal indicator */ -#define SIGNAL_MASK 3 -#define SIGNAL_HIGH 2 -#define SIGNAL_LOW 0 - -struct qe_ic { - /* Control registers offset */ - volatile u32 __iomem *regs; - - /* The remapper for this QEIC */ - struct irq_host *irqhost; - - /* The "linux" controller struct */ - struct irq_chip hc_irq; - - /* The device node of the interrupt controller */ - struct device_node *of_node; - - /* VIRQ numbers of QE high/low irqs */ - unsigned int virq_high; - unsigned int virq_low; -}; - -/* - * QE interrupt controller internal structure - */ -struct qe_ic_info { - u32 mask; /* location of this source at the QIMR register. */ - u32 mask_reg; /* Mask register offset */ - u8 pri_code; /* for grouped interrupts sources - the interrupt - code as appears at the group priority register */ - u32 pri_reg; /* Group priority register offset */ -}; - -#endif /* _POWERPC_SYSDEV_QE_IC_H */ diff --git a/trunk/arch/powerpc/sysdev/qe_lib/qe_io.c b/trunk/arch/powerpc/sysdev/qe_lib/qe_io.c deleted file mode 100644 index aea435970389..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/qe_io.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/qe_io.c - * - * QE Parallel I/O ports configuration routines - * - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Author: Li Yang - * Based on code from Shlomi Gridish - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#undef DEBUG - -#define NUM_OF_PINS 32 - -struct port_regs { - __be32 cpodr; /* Open drain register */ - __be32 cpdata; /* Data register */ - __be32 cpdir1; /* Direction register */ - __be32 cpdir2; /* Direction register */ - __be32 cppar1; /* Pin assignment register */ - __be32 cppar2; /* Pin assignment register */ -}; - -static struct port_regs *par_io = NULL; -static int num_par_io_ports = 0; - -int par_io_init(struct device_node *np) -{ - struct resource res; - int ret; - const u32 *num_ports; - - /* Map Parallel I/O ports registers */ - ret = of_address_to_resource(np, 0, &res); - if (ret) - return ret; - par_io = ioremap(res.start, res.end - res.start + 1); - - num_ports = get_property(np, "num-ports", NULL); - if (num_ports) - num_par_io_ports = *num_ports; - - return 0; -} - -int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, - int assignment, int has_irq) -{ - u32 pin_mask1bit, pin_mask2bits, new_mask2bits, tmp_val; - - if (!par_io) - return -1; - - /* calculate pin location for single and 2 bits information */ - pin_mask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1))); - - /* Set open drain, if required */ - tmp_val = in_be32(&par_io[port].cpodr); - if (open_drain) - out_be32(&par_io[port].cpodr, pin_mask1bit | tmp_val); - else - out_be32(&par_io[port].cpodr, ~pin_mask1bit & tmp_val); - - /* define direction */ - tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? - in_be32(&par_io[port].cpdir2) : - in_be32(&par_io[port].cpdir1); - - /* get all bits mask for 2 bit per port */ - pin_mask2bits = (u32) (0x3 << (NUM_OF_PINS - - (pin % (NUM_OF_PINS / 2) + 1) * 2)); - - /* Get the final mask we need for the right definition */ - new_mask2bits = (u32) (dir << (NUM_OF_PINS - - (pin % (NUM_OF_PINS / 2) + 1) * 2)); - - /* clear and set 2 bits mask */ - if (pin > (NUM_OF_PINS / 2) - 1) { - out_be32(&par_io[port].cpdir2, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io[port].cpdir2, new_mask2bits | tmp_val); - } else { - out_be32(&par_io[port].cpdir1, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io[port].cpdir1, new_mask2bits | tmp_val); - } - /* define pin assignment */ - tmp_val = (pin > (NUM_OF_PINS / 2) - 1) ? - in_be32(&par_io[port].cppar2) : - in_be32(&par_io[port].cppar1); - - new_mask2bits = (u32) (assignment << (NUM_OF_PINS - - (pin % (NUM_OF_PINS / 2) + 1) * 2)); - /* clear and set 2 bits mask */ - if (pin > (NUM_OF_PINS / 2) - 1) { - out_be32(&par_io[port].cppar2, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io[port].cppar2, new_mask2bits | tmp_val); - } else { - out_be32(&par_io[port].cppar1, - ~pin_mask2bits & tmp_val); - tmp_val &= ~pin_mask2bits; - out_be32(&par_io[port].cppar1, new_mask2bits | tmp_val); - } - - return 0; -} -EXPORT_SYMBOL(par_io_config_pin); - -int par_io_data_set(u8 port, u8 pin, u8 val) -{ - u32 pin_mask, tmp_val; - - if (port >= num_par_io_ports) - return -EINVAL; - if (pin >= NUM_OF_PINS) - return -EINVAL; - /* calculate pin location */ - pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin)); - - tmp_val = in_be32(&par_io[port].cpdata); - - if (val == 0) /* clear */ - out_be32(&par_io[port].cpdata, ~pin_mask & tmp_val); - else /* set */ - out_be32(&par_io[port].cpdata, pin_mask | tmp_val); - - return 0; -} -EXPORT_SYMBOL(par_io_data_set); - -int par_io_of_config(struct device_node *np) -{ - struct device_node *pio; - const phandle *ph; - int pio_map_len; - const unsigned int *pio_map; - - if (par_io == NULL) { - printk(KERN_ERR "par_io not initialized \n"); - return -1; - } - - ph = get_property(np, "pio-handle", NULL); - if (ph == 0) { - printk(KERN_ERR "pio-handle not available \n"); - return -1; - } - - pio = of_find_node_by_phandle(*ph); - - pio_map = get_property(pio, "pio-map", &pio_map_len); - if (pio_map == NULL) { - printk(KERN_ERR "pio-map is not set! \n"); - return -1; - } - pio_map_len /= sizeof(unsigned int); - if ((pio_map_len % 6) != 0) { - printk(KERN_ERR "pio-map format wrong! \n"); - return -1; - } - - while (pio_map_len > 0) { - par_io_config_pin((u8) pio_map[0], (u8) pio_map[1], - (int) pio_map[2], (int) pio_map[3], - (int) pio_map[4], (int) pio_map[5]); - pio_map += 6; - pio_map_len -= 6; - } - of_node_put(pio); - return 0; -} -EXPORT_SYMBOL(par_io_of_config); - -#ifdef DEBUG -static void dump_par_io(void) -{ - int i; - - printk(KERN_INFO "PAR IO registars:\n"); - printk(KERN_INFO "Base address: 0x%08x\n", (u32) par_io); - for (i = 0; i < num_par_io_ports; i++) { - printk(KERN_INFO "cpodr[%d] : addr - 0x%08x, val - 0x%08x\n", - i, (u32) & par_io[i].cpodr, - in_be32(&par_io[i].cpodr)); - printk(KERN_INFO "cpdata[%d]: addr - 0x%08x, val - 0x%08x\n", - i, (u32) & par_io[i].cpdata, - in_be32(&par_io[i].cpdata)); - printk(KERN_INFO "cpdir1[%d]: addr - 0x%08x, val - 0x%08x\n", - i, (u32) & par_io[i].cpdir1, - in_be32(&par_io[i].cpdir1)); - printk(KERN_INFO "cpdir2[%d]: addr - 0x%08x, val - 0x%08x\n", - i, (u32) & par_io[i].cpdir2, - in_be32(&par_io[i].cpdir2)); - printk(KERN_INFO "cppar1[%d]: addr - 0x%08x, val - 0x%08x\n", - i, (u32) & par_io[i].cppar1, - in_be32(&par_io[i].cppar1)); - printk(KERN_INFO "cppar2[%d]: addr - 0x%08x, val - 0x%08x\n", - i, (u32) & par_io[i].cppar2, - in_be32(&par_io[i].cppar2)); - } - -} -EXPORT_SYMBOL(dump_par_io); -#endif /* DEBUG */ diff --git a/trunk/arch/powerpc/sysdev/qe_lib/ucc.c b/trunk/arch/powerpc/sysdev/qe_lib/ucc.c deleted file mode 100644 index 916c9e5df57f..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/ucc.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/ucc.c - * - * QE UCC API Set - UCC specific routines implementations. - * - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static DEFINE_SPINLOCK(ucc_lock); - -int ucc_set_qe_mux_mii_mng(int ucc_num) -{ - unsigned long flags; - - spin_lock_irqsave(&ucc_lock, flags); - out_be32(&qe_immr->qmx.cmxgcr, - ((in_be32(&qe_immr->qmx.cmxgcr) & - ~QE_CMXGCR_MII_ENET_MNG) | - (ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT))); - spin_unlock_irqrestore(&ucc_lock, flags); - - return 0; -} - -int ucc_set_type(int ucc_num, struct ucc_common *regs, - enum ucc_speed_type speed) -{ - u8 guemr = 0; - - /* check if the UCC number is in range. */ - if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) - return -EINVAL; - - guemr = regs->guemr; - guemr &= ~(UCC_GUEMR_MODE_MASK_RX | UCC_GUEMR_MODE_MASK_TX); - switch (speed) { - case UCC_SPEED_TYPE_SLOW: - guemr |= (UCC_GUEMR_MODE_SLOW_RX | UCC_GUEMR_MODE_SLOW_TX); - break; - case UCC_SPEED_TYPE_FAST: - guemr |= (UCC_GUEMR_MODE_FAST_RX | UCC_GUEMR_MODE_FAST_TX); - break; - default: - return -EINVAL; - } - regs->guemr = guemr; - - return 0; -} - -int ucc_init_guemr(struct ucc_common *regs) -{ - u8 guemr = 0; - - if (!regs) - return -EINVAL; - - /* Set bit 3 (which is reserved in the GUEMR register) to 1 */ - guemr = UCC_GUEMR_SET_RESERVED3; - - regs->guemr = guemr; - - return 0; -} - -static void get_cmxucr_reg(int ucc_num, volatile u32 ** p_cmxucr, u8 * reg_num, - u8 * shift) -{ - switch (ucc_num) { - case 0: *p_cmxucr = &(qe_immr->qmx.cmxucr1); - *reg_num = 1; - *shift = 16; - break; - case 2: *p_cmxucr = &(qe_immr->qmx.cmxucr1); - *reg_num = 1; - *shift = 0; - break; - case 4: *p_cmxucr = &(qe_immr->qmx.cmxucr2); - *reg_num = 2; - *shift = 16; - break; - case 6: *p_cmxucr = &(qe_immr->qmx.cmxucr2); - *reg_num = 2; - *shift = 0; - break; - case 1: *p_cmxucr = &(qe_immr->qmx.cmxucr3); - *reg_num = 3; - *shift = 16; - break; - case 3: *p_cmxucr = &(qe_immr->qmx.cmxucr3); - *reg_num = 3; - *shift = 0; - break; - case 5: *p_cmxucr = &(qe_immr->qmx.cmxucr4); - *reg_num = 4; - *shift = 16; - break; - case 7: *p_cmxucr = &(qe_immr->qmx.cmxucr4); - *reg_num = 4; - *shift = 0; - break; - default: - break; - } -} - -int ucc_mux_set_grant_tsa_bkpt(int ucc_num, int set, u32 mask) -{ - volatile u32 *p_cmxucr; - u8 reg_num; - u8 shift; - - /* check if the UCC number is in range. */ - if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) - return -EINVAL; - - get_cmxucr_reg(ucc_num, &p_cmxucr, ®_num, &shift); - - if (set) - out_be32(p_cmxucr, in_be32(p_cmxucr) | (mask << shift)); - else - out_be32(p_cmxucr, in_be32(p_cmxucr) & ~(mask << shift)); - - return 0; -} - -int ucc_set_qe_mux_rxtx(int ucc_num, enum qe_clock clock, enum comm_dir mode) -{ - volatile u32 *p_cmxucr; - u8 reg_num; - u8 shift; - u32 clock_bits; - u32 clock_mask; - int source = -1; - - /* check if the UCC number is in range. */ - if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) - return -EINVAL; - - if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) { - printk(KERN_ERR - "ucc_set_qe_mux_rxtx: bad comm mode type passed."); - return -EINVAL; - } - - get_cmxucr_reg(ucc_num, &p_cmxucr, ®_num, &shift); - - switch (reg_num) { - case 1: - switch (clock) { - case QE_BRG1: source = 1; break; - case QE_BRG2: source = 2; break; - case QE_BRG7: source = 3; break; - case QE_BRG8: source = 4; break; - case QE_CLK9: source = 5; break; - case QE_CLK10: source = 6; break; - case QE_CLK11: source = 7; break; - case QE_CLK12: source = 8; break; - case QE_CLK15: source = 9; break; - case QE_CLK16: source = 10; break; - default: source = -1; break; - } - break; - case 2: - switch (clock) { - case QE_BRG5: source = 1; break; - case QE_BRG6: source = 2; break; - case QE_BRG7: source = 3; break; - case QE_BRG8: source = 4; break; - case QE_CLK13: source = 5; break; - case QE_CLK14: source = 6; break; - case QE_CLK19: source = 7; break; - case QE_CLK20: source = 8; break; - case QE_CLK15: source = 9; break; - case QE_CLK16: source = 10; break; - default: source = -1; break; - } - break; - case 3: - switch (clock) { - case QE_BRG9: source = 1; break; - case QE_BRG10: source = 2; break; - case QE_BRG15: source = 3; break; - case QE_BRG16: source = 4; break; - case QE_CLK3: source = 5; break; - case QE_CLK4: source = 6; break; - case QE_CLK17: source = 7; break; - case QE_CLK18: source = 8; break; - case QE_CLK7: source = 9; break; - case QE_CLK8: source = 10; break; - default: source = -1; break; - } - break; - case 4: - switch (clock) { - case QE_BRG13: source = 1; break; - case QE_BRG14: source = 2; break; - case QE_BRG15: source = 3; break; - case QE_BRG16: source = 4; break; - case QE_CLK5: source = 5; break; - case QE_CLK6: source = 6; break; - case QE_CLK21: source = 7; break; - case QE_CLK22: source = 8; break; - case QE_CLK7: source = 9; break; - case QE_CLK8: source = 10; break; - default: source = -1; break; - } - break; - default: - source = -1; - break; - } - - if (source == -1) { - printk(KERN_ERR - "ucc_set_qe_mux_rxtx: Bad combination of clock and UCC."); - return -ENOENT; - } - - clock_bits = (u32) source; - clock_mask = QE_CMXUCR_TX_CLK_SRC_MASK; - if (mode == COMM_DIR_RX) { - clock_bits <<= 4; /* Rx field is 4 bits to left of Tx field */ - clock_mask <<= 4; /* Rx field is 4 bits to left of Tx field */ - } - clock_bits <<= shift; - clock_mask <<= shift; - - out_be32(p_cmxucr, (in_be32(p_cmxucr) & ~clock_mask) | clock_bits); - - return 0; -} diff --git a/trunk/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/trunk/arch/powerpc/sysdev/qe_lib/ucc_fast.c deleted file mode 100644 index c2be7348fcbd..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/ucc_fast.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * arch/powerpc/sysdev/qe_lib/ucc_fast.c - * - * QE UCC Fast API Set - UCC Fast specific routines implementations. - * - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define uccf_printk(level, format, arg...) \ - printk(level format "\n", ## arg) - -#define uccf_dbg(format, arg...) \ - uccf_printk(KERN_DEBUG , format , ## arg) -#define uccf_err(format, arg...) \ - uccf_printk(KERN_ERR , format , ## arg) -#define uccf_info(format, arg...) \ - uccf_printk(KERN_INFO , format , ## arg) -#define uccf_warn(format, arg...) \ - uccf_printk(KERN_WARNING , format , ## arg) - -#ifdef UCCF_VERBOSE_DEBUG -#define uccf_vdbg uccf_dbg -#else -#define uccf_vdbg(fmt, args...) do { } while (0) -#endif /* UCCF_VERBOSE_DEBUG */ - -void ucc_fast_dump_regs(struct ucc_fast_private * uccf) -{ - uccf_info("UCC%d Fast registers:", uccf->uf_info->ucc_num); - uccf_info("Base address: 0x%08x", (u32) uccf->uf_regs); - - uccf_info("gumr : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->gumr, in_be32(&uccf->uf_regs->gumr)); - uccf_info("upsmr : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->upsmr, in_be32(&uccf->uf_regs->upsmr)); - uccf_info("utodr : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->utodr, in_be16(&uccf->uf_regs->utodr)); - uccf_info("udsr : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->udsr, in_be16(&uccf->uf_regs->udsr)); - uccf_info("ucce : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->ucce, in_be32(&uccf->uf_regs->ucce)); - uccf_info("uccm : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->uccm, in_be32(&uccf->uf_regs->uccm)); - uccf_info("uccs : addr - 0x%08x, val - 0x%02x", - (u32) & uccf->uf_regs->uccs, uccf->uf_regs->uccs); - uccf_info("urfb : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->urfb, in_be32(&uccf->uf_regs->urfb)); - uccf_info("urfs : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->urfs, in_be16(&uccf->uf_regs->urfs)); - uccf_info("urfet : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->urfet, in_be16(&uccf->uf_regs->urfet)); - uccf_info("urfset: addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->urfset, - in_be16(&uccf->uf_regs->urfset)); - uccf_info("utfb : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->utfb, in_be32(&uccf->uf_regs->utfb)); - uccf_info("utfs : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->utfs, in_be16(&uccf->uf_regs->utfs)); - uccf_info("utfet : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->utfet, in_be16(&uccf->uf_regs->utfet)); - uccf_info("utftt : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->utftt, in_be16(&uccf->uf_regs->utftt)); - uccf_info("utpt : addr - 0x%08x, val - 0x%04x", - (u32) & uccf->uf_regs->utpt, in_be16(&uccf->uf_regs->utpt)); - uccf_info("urtry : addr - 0x%08x, val - 0x%08x", - (u32) & uccf->uf_regs->urtry, in_be32(&uccf->uf_regs->urtry)); - uccf_info("guemr : addr - 0x%08x, val - 0x%02x", - (u32) & uccf->uf_regs->guemr, uccf->uf_regs->guemr); -} - -u32 ucc_fast_get_qe_cr_subblock(int uccf_num) -{ - switch (uccf_num) { - case 0: return QE_CR_SUBBLOCK_UCCFAST1; - case 1: return QE_CR_SUBBLOCK_UCCFAST2; - case 2: return QE_CR_SUBBLOCK_UCCFAST3; - case 3: return QE_CR_SUBBLOCK_UCCFAST4; - case 4: return QE_CR_SUBBLOCK_UCCFAST5; - case 5: return QE_CR_SUBBLOCK_UCCFAST6; - case 6: return QE_CR_SUBBLOCK_UCCFAST7; - case 7: return QE_CR_SUBBLOCK_UCCFAST8; - default: return QE_CR_SUBBLOCK_INVALID; - } -} - -void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf) -{ - out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); -} - -void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) -{ - struct ucc_fast *uf_regs; - u32 gumr; - - uf_regs = uccf->uf_regs; - - /* Enable reception and/or transmission on this UCC. */ - gumr = in_be32(&uf_regs->gumr); - if (mode & COMM_DIR_TX) { - gumr |= UCC_FAST_GUMR_ENT; - uccf->enabled_tx = 1; - } - if (mode & COMM_DIR_RX) { - gumr |= UCC_FAST_GUMR_ENR; - uccf->enabled_rx = 1; - } - out_be32(&uf_regs->gumr, gumr); -} - -void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) -{ - struct ucc_fast *uf_regs; - u32 gumr; - - uf_regs = uccf->uf_regs; - - /* Disable reception and/or transmission on this UCC. */ - gumr = in_be32(&uf_regs->gumr); - if (mode & COMM_DIR_TX) { - gumr &= ~UCC_FAST_GUMR_ENT; - uccf->enabled_tx = 0; - } - if (mode & COMM_DIR_RX) { - gumr &= ~UCC_FAST_GUMR_ENR; - uccf->enabled_rx = 0; - } - out_be32(&uf_regs->gumr, gumr); -} - -int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret) -{ - struct ucc_fast_private *uccf; - struct ucc_fast *uf_regs; - u32 gumr = 0; - int ret; - - uccf_vdbg("%s: IN", __FUNCTION__); - - if (!uf_info) - return -EINVAL; - - /* check if the UCC port number is in range. */ - if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) { - uccf_err("ucc_fast_init: Illagal UCC number!"); - return -EINVAL; - } - - /* Check that 'max_rx_buf_length' is properly aligned (4). */ - if (uf_info->max_rx_buf_length & (UCC_FAST_MRBLR_ALIGNMENT - 1)) { - uccf_err("ucc_fast_init: max_rx_buf_length not aligned."); - return -EINVAL; - } - - /* Validate Virtual Fifo register values */ - if (uf_info->urfs < UCC_FAST_URFS_MIN_VAL) { - uccf_err - ("ucc_fast_init: Virtual Fifo register urfs too small."); - return -EINVAL; - } - - if (uf_info->urfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - uccf_err - ("ucc_fast_init: Virtual Fifo register urfs not aligned."); - return -EINVAL; - } - - if (uf_info->urfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - uccf_err - ("ucc_fast_init: Virtual Fifo register urfet not aligned."); - return -EINVAL; - } - - if (uf_info->urfset & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - uccf_err - ("ucc_fast_init: Virtual Fifo register urfset not aligned."); - return -EINVAL; - } - - if (uf_info->utfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - uccf_err - ("ucc_fast_init: Virtual Fifo register utfs not aligned."); - return -EINVAL; - } - - if (uf_info->utfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - uccf_err - ("ucc_fast_init: Virtual Fifo register utfet not aligned."); - return -EINVAL; - } - - if (uf_info->utftt & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - uccf_err - ("ucc_fast_init: Virtual Fifo register utftt not aligned."); - return -EINVAL; - } - - uccf = (struct ucc_fast_private *) - kmalloc(sizeof(struct ucc_fast_private), GFP_KERNEL); - if (!uccf) { - uccf_err - ("ucc_fast_init: No memory for UCC slow data structure!"); - return -ENOMEM; - } - memset(uccf, 0, sizeof(struct ucc_fast_private)); - - /* Fill fast UCC structure */ - uccf->uf_info = uf_info; - /* Set the PHY base address */ - uccf->uf_regs = - (struct ucc_fast *) ioremap(uf_info->regs, sizeof(struct ucc_fast)); - if (uccf->uf_regs == NULL) { - uccf_err - ("ucc_fast_init: No memory map for UCC slow controller!"); - return -ENOMEM; - } - - uccf->enabled_tx = 0; - uccf->enabled_rx = 0; - uccf->stopped_tx = 0; - uccf->stopped_rx = 0; - uf_regs = uccf->uf_regs; - uccf->p_ucce = (u32 *) & (uf_regs->ucce); - uccf->p_uccm = (u32 *) & (uf_regs->uccm); -#ifdef STATISTICS - uccf->tx_frames = 0; - uccf->rx_frames = 0; - uccf->rx_discarded = 0; -#endif /* STATISTICS */ - - /* Init Guemr register */ - if ((ret = ucc_init_guemr((struct ucc_common *) (uf_regs)))) { - uccf_err("ucc_fast_init: Could not init the guemr register."); - ucc_fast_free(uccf); - return ret; - } - - /* Set UCC to fast type */ - if ((ret = ucc_set_type(uf_info->ucc_num, - (struct ucc_common *) (uf_regs), - UCC_SPEED_TYPE_FAST))) { - uccf_err("ucc_fast_init: Could not set type to fast."); - ucc_fast_free(uccf); - return ret; - } - - uccf->mrblr = uf_info->max_rx_buf_length; - - /* Set GUMR */ - /* For more details see the hardware spec. */ - /* gumr starts as zero. */ - if (uf_info->tci) - gumr |= UCC_FAST_GUMR_TCI; - gumr |= uf_info->ttx_trx; - if (uf_info->cdp) - gumr |= UCC_FAST_GUMR_CDP; - if (uf_info->ctsp) - gumr |= UCC_FAST_GUMR_CTSP; - if (uf_info->cds) - gumr |= UCC_FAST_GUMR_CDS; - if (uf_info->ctss) - gumr |= UCC_FAST_GUMR_CTSS; - if (uf_info->txsy) - gumr |= UCC_FAST_GUMR_TXSY; - if (uf_info->rsyn) - gumr |= UCC_FAST_GUMR_RSYN; - gumr |= uf_info->synl; - if (uf_info->rtsm) - gumr |= UCC_FAST_GUMR_RTSM; - gumr |= uf_info->renc; - if (uf_info->revd) - gumr |= UCC_FAST_GUMR_REVD; - gumr |= uf_info->tenc; - gumr |= uf_info->tcrc; - gumr |= uf_info->mode; - out_be32(&uf_regs->gumr, gumr); - - /* Allocate memory for Tx Virtual Fifo */ - uccf->ucc_fast_tx_virtual_fifo_base_offset = - qe_muram_alloc(uf_info->utfs, UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); - if (IS_MURAM_ERR(uccf->ucc_fast_tx_virtual_fifo_base_offset)) { - uccf_err - ("ucc_fast_init: Can not allocate MURAM memory for " - "struct ucc_fastx_virtual_fifo_base_offset."); - uccf->ucc_fast_tx_virtual_fifo_base_offset = 0; - ucc_fast_free(uccf); - return -ENOMEM; - } - - /* Allocate memory for Rx Virtual Fifo */ - uccf->ucc_fast_rx_virtual_fifo_base_offset = - qe_muram_alloc(uf_info->urfs + - (u32) - UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR, - UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); - if (IS_MURAM_ERR(uccf->ucc_fast_rx_virtual_fifo_base_offset)) { - uccf_err - ("ucc_fast_init: Can not allocate MURAM memory for " - "ucc_fast_rx_virtual_fifo_base_offset."); - uccf->ucc_fast_rx_virtual_fifo_base_offset = 0; - ucc_fast_free(uccf); - return -ENOMEM; - } - - /* Set Virtual Fifo registers */ - out_be16(&uf_regs->urfs, uf_info->urfs); - out_be16(&uf_regs->urfet, uf_info->urfet); - out_be16(&uf_regs->urfset, uf_info->urfset); - out_be16(&uf_regs->utfs, uf_info->utfs); - out_be16(&uf_regs->utfet, uf_info->utfet); - out_be16(&uf_regs->utftt, uf_info->utftt); - /* utfb, urfb are offsets from MURAM base */ - out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset); - out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset); - - /* Mux clocking */ - /* Grant Support */ - ucc_set_qe_mux_grant(uf_info->ucc_num, uf_info->grant_support); - /* Breakpoint Support */ - ucc_set_qe_mux_bkpt(uf_info->ucc_num, uf_info->brkpt_support); - /* Set Tsa or NMSI mode. */ - ucc_set_qe_mux_tsa(uf_info->ucc_num, uf_info->tsa); - /* If NMSI (not Tsa), set Tx and Rx clock. */ - if (!uf_info->tsa) { - /* Rx clock routing */ - if (uf_info->rx_clock != QE_CLK_NONE) { - if (ucc_set_qe_mux_rxtx - (uf_info->ucc_num, uf_info->rx_clock, - COMM_DIR_RX)) { - uccf_err - ("ucc_fast_init: Illegal value for parameter 'RxClock'."); - ucc_fast_free(uccf); - return -EINVAL; - } - } - /* Tx clock routing */ - if (uf_info->tx_clock != QE_CLK_NONE) { - if (ucc_set_qe_mux_rxtx - (uf_info->ucc_num, uf_info->tx_clock, - COMM_DIR_TX)) { - uccf_err - ("ucc_fast_init: Illegal value for parameter 'TxClock'."); - ucc_fast_free(uccf); - return -EINVAL; - } - } - } - - /* Set interrupt mask register at UCC level. */ - out_be32(&uf_regs->uccm, uf_info->uccm_mask); - - /* First, clear anything pending at UCC level, - * otherwise, old garbage may come through - * as soon as the dam is opened - * Writing '1' clears - */ - out_be32(&uf_regs->ucce, 0xffffffff); - - *uccf_ret = uccf; - return 0; -} - -void ucc_fast_free(struct ucc_fast_private * uccf) -{ - if (!uccf) - return; - - if (uccf->ucc_fast_tx_virtual_fifo_base_offset) - qe_muram_free(uccf->ucc_fast_tx_virtual_fifo_base_offset); - - if (uccf->ucc_fast_rx_virtual_fifo_base_offset) - qe_muram_free(uccf->ucc_fast_rx_virtual_fifo_base_offset); - - kfree(uccf); -} diff --git a/trunk/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/trunk/arch/powerpc/sysdev/qe_lib/ucc_slow.c deleted file mode 100644 index 1fb88ef7cf06..000000000000 --- a/trunk/arch/powerpc/sysdev/qe_lib/ucc_slow.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QE UCC Slow API Set - UCC Slow specific routines implementations. - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#define uccs_printk(level, format, arg...) \ - printk(level format "\n", ## arg) - -#define uccs_dbg(format, arg...) \ - uccs_printk(KERN_DEBUG , format , ## arg) -#define uccs_err(format, arg...) \ - uccs_printk(KERN_ERR , format , ## arg) -#define uccs_info(format, arg...) \ - uccs_printk(KERN_INFO , format , ## arg) -#define uccs_warn(format, arg...) \ - uccs_printk(KERN_WARNING , format , ## arg) - -#ifdef UCCS_VERBOSE_DEBUG -#define uccs_vdbg uccs_dbg -#else -#define uccs_vdbg(fmt, args...) do { } while (0) -#endif /* UCCS_VERBOSE_DEBUG */ - -u32 ucc_slow_get_qe_cr_subblock(int uccs_num) -{ - switch (uccs_num) { - case 0: return QE_CR_SUBBLOCK_UCCSLOW1; - case 1: return QE_CR_SUBBLOCK_UCCSLOW2; - case 2: return QE_CR_SUBBLOCK_UCCSLOW3; - case 3: return QE_CR_SUBBLOCK_UCCSLOW4; - case 4: return QE_CR_SUBBLOCK_UCCSLOW5; - case 5: return QE_CR_SUBBLOCK_UCCSLOW6; - case 6: return QE_CR_SUBBLOCK_UCCSLOW7; - case 7: return QE_CR_SUBBLOCK_UCCSLOW8; - default: return QE_CR_SUBBLOCK_INVALID; - } -} - -void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs) -{ - out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD); -} - -void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs) -{ - struct ucc_slow_info *us_info = uccs->us_info; - u32 id; - - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_GRACEFUL_STOP_TX, id, - QE_CR_PROTOCOL_UNSPECIFIED, 0); -} - -void ucc_slow_stop_tx(struct ucc_slow_private * uccs) -{ - struct ucc_slow_info *us_info = uccs->us_info; - u32 id; - - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_STOP_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); -} - -void ucc_slow_restart_tx(struct ucc_slow_private * uccs) -{ - struct ucc_slow_info *us_info = uccs->us_info; - u32 id; - - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_RESTART_TX, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); -} - -void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) -{ - struct ucc_slow *us_regs; - u32 gumr_l; - - us_regs = uccs->us_regs; - - /* Enable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); - if (mode & COMM_DIR_TX) { - gumr_l |= UCC_SLOW_GUMR_L_ENT; - uccs->enabled_tx = 1; - } - if (mode & COMM_DIR_RX) { - gumr_l |= UCC_SLOW_GUMR_L_ENR; - uccs->enabled_rx = 1; - } - out_be32(&us_regs->gumr_l, gumr_l); -} - -void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) -{ - struct ucc_slow *us_regs; - u32 gumr_l; - - us_regs = uccs->us_regs; - - /* Disable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); - if (mode & COMM_DIR_TX) { - gumr_l &= ~UCC_SLOW_GUMR_L_ENT; - uccs->enabled_tx = 0; - } - if (mode & COMM_DIR_RX) { - gumr_l &= ~UCC_SLOW_GUMR_L_ENR; - uccs->enabled_rx = 0; - } - out_be32(&us_regs->gumr_l, gumr_l); -} - -int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret) -{ - u32 i; - struct ucc_slow *us_regs; - u32 gumr; - u8 function_code = 0; - u8 *bd; - struct ucc_slow_private *uccs; - u32 id; - u32 command; - int ret; - - uccs_vdbg("%s: IN", __FUNCTION__); - - if (!us_info) - return -EINVAL; - - /* check if the UCC port number is in range. */ - if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM - 1)) { - uccs_err("ucc_slow_init: Illagal UCC number!"); - return -EINVAL; - } - - /* - * Set mrblr - * Check that 'max_rx_buf_length' is properly aligned (4), unless - * rfw is 1, meaning that QE accepts one byte at a time, unlike normal - * case when QE accepts 32 bits at a time. - */ - if ((!us_info->rfw) && - (us_info->max_rx_buf_length & (UCC_SLOW_MRBLR_ALIGNMENT - 1))) { - uccs_err("max_rx_buf_length not aligned."); - return -EINVAL; - } - - uccs = (struct ucc_slow_private *) - kmalloc(sizeof(struct ucc_slow_private), GFP_KERNEL); - if (!uccs) { - uccs_err - ("ucc_slow_init: No memory for UCC slow data structure!"); - return -ENOMEM; - } - memset(uccs, 0, sizeof(struct ucc_slow_private)); - - /* Fill slow UCC structure */ - uccs->us_info = us_info; - uccs->saved_uccm = 0; - uccs->p_rx_frame = 0; - uccs->us_regs = us_info->us_regs; - us_regs = uccs->us_regs; - uccs->p_ucce = (u16 *) & (us_regs->ucce); - uccs->p_uccm = (u16 *) & (us_regs->uccm); -#ifdef STATISTICS - uccs->rx_frames = 0; - uccs->tx_frames = 0; - uccs->rx_discarded = 0; -#endif /* STATISTICS */ - - /* Get PRAM base */ - uccs->us_pram_offset = qe_muram_alloc(UCC_SLOW_PRAM_SIZE, - ALIGNMENT_OF_UCC_SLOW_PRAM); - if (IS_MURAM_ERR(uccs->us_pram_offset)) { - uccs_err - ("ucc_slow_init: Can not allocate MURAM memory " - "for Slow UCC."); - ucc_slow_free(uccs); - return -ENOMEM; - } - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, id, QE_CR_PROTOCOL_UNSPECIFIED, - (u32) uccs->us_pram_offset); - - uccs->us_pram = qe_muram_addr(uccs->us_pram_offset); - - /* Init Guemr register */ - if ((ret = ucc_init_guemr((struct ucc_common *) (us_info->us_regs)))) { - uccs_err("ucc_slow_init: Could not init the guemr register."); - ucc_slow_free(uccs); - return ret; - } - - /* Set UCC to slow type */ - if ((ret = ucc_set_type(us_info->ucc_num, - (struct ucc_common *) (us_info->us_regs), - UCC_SPEED_TYPE_SLOW))) { - uccs_err("ucc_slow_init: Could not init the guemr register."); - ucc_slow_free(uccs); - return ret; - } - - out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length); - - INIT_LIST_HEAD(&uccs->confQ); - - /* Allocate BDs. */ - uccs->rx_base_offset = - qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd), - QE_ALIGNMENT_OF_BD); - if (IS_MURAM_ERR(uccs->rx_base_offset)) { - uccs_err("ucc_slow_init: No memory for Rx BD's."); - uccs->rx_base_offset = 0; - ucc_slow_free(uccs); - return -ENOMEM; - } - - uccs->tx_base_offset = - qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd), - QE_ALIGNMENT_OF_BD); - if (IS_MURAM_ERR(uccs->tx_base_offset)) { - uccs_err("ucc_slow_init: No memory for Tx BD's."); - uccs->tx_base_offset = 0; - ucc_slow_free(uccs); - return -ENOMEM; - } - - /* Init Tx bds */ - bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs->tx_base_offset); - for (i = 0; i < us_info->tx_bd_ring_len; i++) { - /* clear bd buffer */ - out_be32(&(((struct qe_bd *)bd)->buf), 0); - /* set bd status and length */ - out_be32((u32*)bd, 0); - bd += sizeof(struct qe_bd); - } - bd -= sizeof(struct qe_bd); - /* set bd status and length */ - out_be32((u32*)bd, T_W); /* for last BD set Wrap bit */ - - /* Init Rx bds */ - bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset); - for (i = 0; i < us_info->rx_bd_ring_len; i++) { - /* set bd status and length */ - out_be32((u32*)bd, 0); - /* clear bd buffer */ - out_be32(&(((struct qe_bd *)bd)->buf), 0); - bd += sizeof(struct qe_bd); - } - bd -= sizeof(struct qe_bd); - /* set bd status and length */ - out_be32((u32*)bd, R_W); /* for last BD set Wrap bit */ - - /* Set GUMR (For more details see the hardware spec.). */ - /* gumr_h */ - gumr = 0; - gumr |= us_info->tcrc; - if (us_info->cdp) - gumr |= UCC_SLOW_GUMR_H_CDP; - if (us_info->ctsp) - gumr |= UCC_SLOW_GUMR_H_CTSP; - if (us_info->cds) - gumr |= UCC_SLOW_GUMR_H_CDS; - if (us_info->ctss) - gumr |= UCC_SLOW_GUMR_H_CTSS; - if (us_info->tfl) - gumr |= UCC_SLOW_GUMR_H_TFL; - if (us_info->rfw) - gumr |= UCC_SLOW_GUMR_H_RFW; - if (us_info->txsy) - gumr |= UCC_SLOW_GUMR_H_TXSY; - if (us_info->rtsm) - gumr |= UCC_SLOW_GUMR_H_RTSM; - out_be32(&us_regs->gumr_h, gumr); - - /* gumr_l */ - gumr = 0; - if (us_info->tci) - gumr |= UCC_SLOW_GUMR_L_TCI; - if (us_info->rinv) - gumr |= UCC_SLOW_GUMR_L_RINV; - if (us_info->tinv) - gumr |= UCC_SLOW_GUMR_L_TINV; - if (us_info->tend) - gumr |= UCC_SLOW_GUMR_L_TEND; - gumr |= us_info->tdcr; - gumr |= us_info->rdcr; - gumr |= us_info->tenc; - gumr |= us_info->renc; - gumr |= us_info->diag; - gumr |= us_info->mode; - out_be32(&us_regs->gumr_l, gumr); - - /* Function code registers */ - /* function_code has initial value 0 */ - - /* if the data is in cachable memory, the 'global' */ - /* in the function code should be set. */ - function_code |= us_info->data_mem_part; - function_code |= QE_BMR_BYTE_ORDER_BO_MOT; /* Required for QE */ - uccs->us_pram->tfcr = function_code; - uccs->us_pram->rfcr = function_code; - - /* rbase, tbase are offsets from MURAM base */ - out_be16(&uccs->us_pram->rbase, uccs->us_pram_offset); - out_be16(&uccs->us_pram->tbase, uccs->us_pram_offset); - - /* Mux clocking */ - /* Grant Support */ - ucc_set_qe_mux_grant(us_info->ucc_num, us_info->grant_support); - /* Breakpoint Support */ - ucc_set_qe_mux_bkpt(us_info->ucc_num, us_info->brkpt_support); - /* Set Tsa or NMSI mode. */ - ucc_set_qe_mux_tsa(us_info->ucc_num, us_info->tsa); - /* If NMSI (not Tsa), set Tx and Rx clock. */ - if (!us_info->tsa) { - /* Rx clock routing */ - if (ucc_set_qe_mux_rxtx - (us_info->ucc_num, us_info->rx_clock, COMM_DIR_RX)) { - uccs_err - ("ucc_slow_init: Illegal value for parameter" - " 'RxClock'."); - ucc_slow_free(uccs); - return -EINVAL; - } - /* Tx clock routing */ - if (ucc_set_qe_mux_rxtx(us_info->ucc_num, - us_info->tx_clock, COMM_DIR_TX)) { - uccs_err - ("ucc_slow_init: Illegal value for parameter " - "'TxClock'."); - ucc_slow_free(uccs); - return -EINVAL; - } - } - - /* - * INTERRUPTS - */ - /* Set interrupt mask register at UCC level. */ - out_be16(&us_regs->uccm, us_info->uccm_mask); - - /* First, clear anything pending at UCC level, */ - /* otherwise, old garbage may come through */ - /* as soon as the dam is opened. */ - - /* Writing '1' clears */ - out_be16(&us_regs->ucce, 0xffff); - - /* Issue QE Init command */ - if (us_info->init_tx && us_info->init_rx) - command = QE_INIT_TX_RX; - else if (us_info->init_tx) - command = QE_INIT_TX; - else - command = QE_INIT_RX; /* We know at least one is TRUE */ - id = ucc_slow_get_qe_cr_subblock(us_info->ucc_num); - qe_issue_cmd(command, id, QE_CR_PROTOCOL_UNSPECIFIED, 0); - - *uccs_ret = uccs; - return 0; -} - -void ucc_slow_free(struct ucc_slow_private * uccs) -{ - if (!uccs) - return; - - if (uccs->rx_base_offset) - qe_muram_free(uccs->rx_base_offset); - - if (uccs->tx_base_offset) - qe_muram_free(uccs->tx_base_offset); - - if (uccs->us_pram) { - qe_muram_free(uccs->us_pram_offset); - uccs->us_pram = NULL; - } - - kfree(uccs); -} diff --git a/trunk/arch/powerpc/sysdev/tsi108_dev.c b/trunk/arch/powerpc/sysdev/tsi108_dev.c index f3038461d4c0..11de090eb901 100644 --- a/trunk/arch/powerpc/sysdev/tsi108_dev.c +++ b/trunk/arch/powerpc/sysdev/tsi108_dev.c @@ -9,7 +9,6 @@ * option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/powerpc/xmon/xmon.c b/trunk/arch/powerpc/xmon/xmon.c index 708236f34746..8adad1444a51 100644 --- a/trunk/arch/powerpc/xmon/xmon.c +++ b/trunk/arch/powerpc/xmon/xmon.c @@ -2,8 +2,6 @@ * Routines providing a simple monitor for use on the PowerMac. * * Copyright (C) 1996-2005 Paul Mackerras. - * Copyright (C) 2001 PPC64 Team, IBM Corp - * Copyrignt (C) 2006 Michael Ellerman, IBM Corp * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -505,7 +503,7 @@ static int xmon_core(struct pt_regs *regs, int fromipi) mtmsr(msr); /* restore interrupt enable */ - return cmd != 'X' && cmd != EOF; + return cmd != 'X'; } int xmon(struct pt_regs *excp) @@ -2599,34 +2597,3 @@ static int __init setup_xmon_sysrq(void) } __initcall(setup_xmon_sysrq); #endif /* CONFIG_MAGIC_SYSRQ */ - -int __initdata xmon_early, xmon_off; - -static int __init early_parse_xmon(char *p) -{ - if (!p || strncmp(p, "early", 5) == 0) { - /* just "xmon" is equivalent to "xmon=early" */ - xmon_init(1); - xmon_early = 1; - } else if (strncmp(p, "on", 2) == 0) - xmon_init(1); - else if (strncmp(p, "off", 3) == 0) - xmon_off = 1; - else if (strncmp(p, "nobt", 4) == 0) - xmon_no_auto_backtrace = 1; - else - return 1; - - return 0; -} -early_param("xmon", early_parse_xmon); - -void __init xmon_setup(void) -{ -#ifdef CONFIG_XMON_DEFAULT - if (!xmon_off) - xmon_init(1); -#endif - if (xmon_early) - debugger(NULL); -} diff --git a/trunk/arch/ppc/amiga/time.c b/trunk/arch/ppc/amiga/time.c index 0073527a7036..8c880c0ca380 100644 --- a/trunk/arch/ppc/amiga/time.c +++ b/trunk/arch/ppc/amiga/time.c @@ -1,4 +1,3 @@ -#include /* CONFIG_HEARTBEAT */ #include #include #include diff --git a/trunk/arch/ppc/platforms/4xx/cpci405.h b/trunk/arch/ppc/platforms/4xx/cpci405.h index f5a5c0cd062d..a6c0a138b0d7 100644 --- a/trunk/arch/ppc/platforms/4xx/cpci405.h +++ b/trunk/arch/ppc/platforms/4xx/cpci405.h @@ -13,7 +13,6 @@ #ifndef __CPCI405_H__ #define __CPCI405_H__ -#include #include #include diff --git a/trunk/arch/s390/kernel/kprobes.c b/trunk/arch/s390/kernel/kprobes.c index 4d9ff5ce4cbd..67914fe7f317 100644 --- a/trunk/arch/s390/kernel/kprobes.c +++ b/trunk/arch/s390/kernel/kprobes.c @@ -20,7 +20,6 @@ * s390 port, used ppc64 as template. Mike Grundy */ -#include #include #include #include diff --git a/trunk/arch/sparc/kernel/ioport.c b/trunk/arch/sparc/kernel/ioport.c index d33f8a07ccac..54d51b404603 100644 --- a/trunk/arch/sparc/kernel/ioport.c +++ b/trunk/arch/sparc/kernel/ioport.c @@ -25,7 +25,6 @@ * Sounds reasonable */ -#include #include #include #include diff --git a/trunk/arch/sparc/kernel/of_device.c b/trunk/arch/sparc/kernel/of_device.c index 97bf87e8cdde..74bef2a2d37f 100644 --- a/trunk/arch/sparc/kernel/of_device.c +++ b/trunk/arch/sparc/kernel/of_device.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/trunk/arch/sparc64/kernel/auxio.c b/trunk/arch/sparc64/kernel/auxio.c index 718350aba1ec..826118ee53d5 100644 --- a/trunk/arch/sparc64/kernel/auxio.c +++ b/trunk/arch/sparc64/kernel/auxio.c @@ -5,7 +5,6 @@ * Refactoring for unified NCR/PCIO support 2002 Eric Brower (ebrower@usa.net) */ -#include #include #include #include diff --git a/trunk/arch/sparc64/kernel/of_device.c b/trunk/arch/sparc64/kernel/of_device.c index 238bbf6de07d..7f9204535770 100644 --- a/trunk/arch/sparc64/kernel/of_device.c +++ b/trunk/arch/sparc64/kernel/of_device.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/trunk/arch/sparc64/kernel/prom.c b/trunk/arch/sparc64/kernel/prom.c index 5cc5ab63293f..e21cd6afa709 100644 --- a/trunk/arch/sparc64/kernel/prom.c +++ b/trunk/arch/sparc64/kernel/prom.c @@ -15,7 +15,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/trunk/arch/um/drivers/hostaudio_kern.c b/trunk/arch/um/drivers/hostaudio_kern.c index d247ef45c374..a0d148ea63d6 100644 --- a/trunk/arch/um/drivers/hostaudio_kern.c +++ b/trunk/arch/um/drivers/hostaudio_kern.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/module.h" #include "linux/init.h" #include "linux/slab.h" diff --git a/trunk/arch/um/drivers/net_kern.c b/trunk/arch/um/drivers/net_kern.c index 300a54a6523e..c1c5604752fb 100644 --- a/trunk/arch/um/drivers/net_kern.c +++ b/trunk/arch/um/drivers/net_kern.c @@ -5,7 +5,6 @@ * Licensed under the GPL. */ -#include "linux/config.h" #include "linux/kernel.h" #include "linux/netdevice.h" #include "linux/rtnetlink.h" diff --git a/trunk/arch/um/drivers/slip_kern.c b/trunk/arch/um/drivers/slip_kern.c index ccea2d7885e5..788da5439a2d 100644 --- a/trunk/arch/um/drivers/slip_kern.c +++ b/trunk/arch/um/drivers/slip_kern.c @@ -1,4 +1,3 @@ -#include "linux/config.h" #include "linux/kernel.h" #include "linux/stddef.h" #include "linux/init.h" diff --git a/trunk/arch/um/drivers/ssl.c b/trunk/arch/um/drivers/ssl.c index 6f13e7c71a82..ed9c59082d0d 100644 --- a/trunk/arch/um/drivers/ssl.c +++ b/trunk/arch/um/drivers/ssl.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/fs.h" #include "linux/tty.h" #include "linux/tty_driver.h" diff --git a/trunk/arch/um/drivers/stdio_console.c b/trunk/arch/um/drivers/stdio_console.c index e4bfcfe8550b..7a4897e27f42 100644 --- a/trunk/arch/um/drivers/stdio_console.c +++ b/trunk/arch/um/drivers/stdio_console.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/posix_types.h" #include "linux/tty.h" #include "linux/tty_flip.h" diff --git a/trunk/arch/um/drivers/ubd_kern.c b/trunk/arch/um/drivers/ubd_kern.c index fda4a3940698..f0b0668458b7 100644 --- a/trunk/arch/um/drivers/ubd_kern.c +++ b/trunk/arch/um/drivers/ubd_kern.c @@ -20,7 +20,6 @@ #define MAJOR_NR UBD_MAJOR #define UBD_SHIFT 4 -#include "linux/config.h" #include "linux/module.h" #include "linux/blkdev.h" #include "linux/hdreg.h" diff --git a/trunk/arch/um/include/mconsole_kern.h b/trunk/arch/um/include/mconsole_kern.h index d86ee14260ce..d0b690197fd7 100644 --- a/trunk/arch/um/include/mconsole_kern.h +++ b/trunk/arch/um/include/mconsole_kern.h @@ -6,7 +6,6 @@ #ifndef __MCONSOLE_KERN_H__ #define __MCONSOLE_KERN_H__ -#include "linux/config.h" #include "linux/list.h" #include "mconsole.h" diff --git a/trunk/arch/um/include/mode_kern.h b/trunk/arch/um/include/mode_kern.h index e7539a8451ef..88e5e77bf517 100644 --- a/trunk/arch/um/include/mode_kern.h +++ b/trunk/arch/um/include/mode_kern.h @@ -6,8 +6,6 @@ #ifndef __MODE_KERN_H__ #define __MODE_KERN_H__ -#include "linux/config.h" - #ifdef CONFIG_MODE_TT #include "mode_kern_tt.h" #endif diff --git a/trunk/arch/um/include/skas/mmu-skas.h b/trunk/arch/um/include/skas/mmu-skas.h index d8869a6ef1b4..b26986c0c3d2 100644 --- a/trunk/arch/um/include/skas/mmu-skas.h +++ b/trunk/arch/um/include/skas/mmu-skas.h @@ -6,7 +6,6 @@ #ifndef __SKAS_MMU_H #define __SKAS_MMU_H -#include "linux/config.h" #include "mm_id.h" #include "asm/ldt.h" diff --git a/trunk/arch/um/include/sysdep-ppc/ptrace.h b/trunk/arch/um/include/sysdep-ppc/ptrace.h index 8a27353733a9..df2397dba3e5 100644 --- a/trunk/arch/um/include/sysdep-ppc/ptrace.h +++ b/trunk/arch/um/include/sysdep-ppc/ptrace.h @@ -5,7 +5,6 @@ #ifndef __SYS_PTRACE_PPC_H #define __SYS_PTRACE_PPC_H -#include "linux/config.h" #include "linux/types.h" /* the following taken from */ diff --git a/trunk/arch/um/include/um_uaccess.h b/trunk/arch/um/include/um_uaccess.h index 4567f1eeb4a7..5126a99b5961 100644 --- a/trunk/arch/um/include/um_uaccess.h +++ b/trunk/arch/um/include/um_uaccess.h @@ -6,7 +6,6 @@ #ifndef __ARCH_UM_UACCESS_H #define __ARCH_UM_UACCESS_H -#include "linux/config.h" #include "choose-mode.h" #ifdef CONFIG_MODE_TT diff --git a/trunk/arch/um/kernel/init_task.c b/trunk/arch/um/kernel/init_task.c index 49ed5ddf0704..8cde431348cc 100644 --- a/trunk/arch/um/kernel/init_task.c +++ b/trunk/arch/um/kernel/init_task.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/mm.h" #include "linux/module.h" #include "linux/sched.h" diff --git a/trunk/arch/um/kernel/irq.c b/trunk/arch/um/kernel/irq.c index ce7f233fc490..eee97bb81ba5 100644 --- a/trunk/arch/um/kernel/irq.c +++ b/trunk/arch/um/kernel/irq.c @@ -5,7 +5,6 @@ * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar */ -#include "linux/config.h" #include "linux/kernel.h" #include "linux/module.h" #include "linux/smp.h" diff --git a/trunk/arch/um/kernel/ksyms.c b/trunk/arch/um/kernel/ksyms.c index f030e44262ba..0e00cf93f900 100644 --- a/trunk/arch/um/kernel/ksyms.c +++ b/trunk/arch/um/kernel/ksyms.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/module.h" #include "linux/string.h" #include "linux/smp_lock.h" diff --git a/trunk/arch/um/kernel/signal.c b/trunk/arch/um/kernel/signal.c index 4aa9808ba264..2a32e5e8e9c9 100644 --- a/trunk/arch/um/kernel/signal.c +++ b/trunk/arch/um/kernel/signal.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/stddef.h" #include "linux/sys.h" #include "linux/sched.h" diff --git a/trunk/arch/um/kernel/skas/mem.c b/trunk/arch/um/kernel/skas/mem.c index 27bbf54b1e52..0d2cce621134 100644 --- a/trunk/arch/um/kernel/skas/mem.c +++ b/trunk/arch/um/kernel/skas/mem.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/mm.h" #include "asm/pgtable.h" #include "mem_user.h" diff --git a/trunk/arch/um/kernel/skas/mmu.c b/trunk/arch/um/kernel/skas/mmu.c index 4cd2ff546ef6..c17eddcf89b3 100644 --- a/trunk/arch/um/kernel/skas/mmu.c +++ b/trunk/arch/um/kernel/skas/mmu.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/sched.h" #include "linux/list.h" #include "linux/spinlock.h" diff --git a/trunk/arch/um/kernel/skas/tlb.c b/trunk/arch/um/kernel/skas/tlb.c index 6e84963dfc29..27eb29ce666b 100644 --- a/trunk/arch/um/kernel/skas/tlb.c +++ b/trunk/arch/um/kernel/skas/tlb.c @@ -6,7 +6,6 @@ #include "linux/stddef.h" #include "linux/sched.h" -#include "linux/config.h" #include "linux/mm.h" #include "asm/page.h" #include "asm/pgtable.h" diff --git a/trunk/arch/um/kernel/smp.c b/trunk/arch/um/kernel/smp.c index 511116aebaf7..759b07053160 100644 --- a/trunk/arch/um/kernel/smp.c +++ b/trunk/arch/um/kernel/smp.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/percpu.h" #include "asm/pgalloc.h" #include "asm/tlb.h" diff --git a/trunk/arch/um/kernel/sysrq.c b/trunk/arch/um/kernel/sysrq.c index b331e970002f..239c98054dec 100644 --- a/trunk/arch/um/kernel/sysrq.c +++ b/trunk/arch/um/kernel/sysrq.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/sched.h" #include "linux/kernel.h" #include "linux/module.h" diff --git a/trunk/arch/um/kernel/trap.c b/trunk/arch/um/kernel/trap.c index c7b195c7e51f..b5f124a2f6ae 100644 --- a/trunk/arch/um/kernel/trap.c +++ b/trunk/arch/um/kernel/trap.c @@ -8,7 +8,6 @@ #include "linux/sched.h" #include "linux/mm.h" #include "linux/spinlock.h" -#include "linux/config.h" #include "linux/init.h" #include "linux/ptrace.h" #include "asm/semaphore.h" diff --git a/trunk/arch/um/kernel/tt/gdb_kern.c b/trunk/arch/um/kernel/tt/gdb_kern.c index 26506388a6aa..68e1bf63cd0a 100644 --- a/trunk/arch/um/kernel/tt/gdb_kern.c +++ b/trunk/arch/um/kernel/tt/gdb_kern.c @@ -4,7 +4,6 @@ */ #include "linux/init.h" -#include "linux/config.h" #include "mconsole_kern.h" #ifdef CONFIG_MCONSOLE diff --git a/trunk/arch/um/kernel/tt/mem.c b/trunk/arch/um/kernel/tt/mem.c index 84a23b14f770..4d1929dfa285 100644 --- a/trunk/arch/um/kernel/tt/mem.c +++ b/trunk/arch/um/kernel/tt/mem.c @@ -4,7 +4,6 @@ */ #include "linux/stddef.h" -#include "linux/config.h" #include "linux/mm.h" #include "asm/uaccess.h" #include "mem_user.h" diff --git a/trunk/arch/um/kernel/um_arch.c b/trunk/arch/um/kernel/um_arch.c index 97d88e7902f7..66f43c906821 100644 --- a/trunk/arch/um/kernel/um_arch.c +++ b/trunk/arch/um/kernel/um_arch.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/kernel.h" #include "linux/sched.h" #include "linux/notifier.h" diff --git a/trunk/arch/um/sys-i386/ldt.c b/trunk/arch/um/sys-i386/ldt.c index 69971b78beaf..e299ee5a753d 100644 --- a/trunk/arch/um/sys-i386/ldt.c +++ b/trunk/arch/um/sys-i386/ldt.c @@ -4,7 +4,6 @@ */ #include "linux/stddef.h" -#include "linux/config.h" #include "linux/sched.h" #include "linux/slab.h" #include "linux/types.h" diff --git a/trunk/arch/um/sys-i386/sysrq.c b/trunk/arch/um/sys-i386/sysrq.c index d5244f070539..171b3e9dc867 100644 --- a/trunk/arch/um/sys-i386/sysrq.c +++ b/trunk/arch/um/sys-i386/sysrq.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/kernel.h" #include "linux/smp.h" #include "linux/sched.h" diff --git a/trunk/arch/um/sys-i386/tls.c b/trunk/arch/um/sys-i386/tls.c index 71b9796258ef..643dab585727 100644 --- a/trunk/arch/um/sys-i386/tls.c +++ b/trunk/arch/um/sys-i386/tls.c @@ -3,7 +3,6 @@ * Licensed under the GPL */ -#include "linux/config.h" #include "linux/kernel.h" #include "linux/sched.h" #include "linux/slab.h" diff --git a/trunk/arch/v850/kernel/time.c b/trunk/arch/v850/kernel/time.c index f4d1a4d3cdc2..cd06f47c0ea7 100644 --- a/trunk/arch/v850/kernel/time.c +++ b/trunk/arch/v850/kernel/time.c @@ -10,7 +10,6 @@ * "A Kernel Model for Precision Timekeeping" by Dave Mills */ -#include /* CONFIG_HEARTBEAT */ #include #include #include diff --git a/trunk/arch/x86_64/boot/video.S b/trunk/arch/x86_64/boot/video.S index 2aa565c136e5..d6ff88f35135 100644 --- a/trunk/arch/x86_64/boot/video.S +++ b/trunk/arch/x86_64/boot/video.S @@ -11,8 +11,6 @@ * */ -#include /* for CONFIG_VIDEO_* */ - /* Enable autodetection of SVGA adapters and modes. */ #undef CONFIG_VIDEO_SVGA diff --git a/trunk/arch/x86_64/ia32/ia32_binfmt.c b/trunk/arch/x86_64/ia32/ia32_binfmt.c index 2fd5a67fd435..82ef182de6ae 100644 --- a/trunk/arch/x86_64/ia32/ia32_binfmt.c +++ b/trunk/arch/x86_64/ia32/ia32_binfmt.c @@ -6,7 +6,6 @@ * of ugly preprocessor tricks. Talk about very very poor man's inheritance. */ #include -#include #include #include #include diff --git a/trunk/arch/x86_64/kernel/i8259.c b/trunk/arch/x86_64/kernel/i8259.c index 0612a33bb896..2dd51f364ea2 100644 --- a/trunk/arch/x86_64/kernel/i8259.c +++ b/trunk/arch/x86_64/kernel/i8259.c @@ -43,10 +43,17 @@ BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ BI(x,c) BI(x,d) BI(x,e) BI(x,f) +#define BUILD_15_IRQS(x) \ + BI(x,0) BI(x,1) BI(x,2) BI(x,3) \ + BI(x,4) BI(x,5) BI(x,6) BI(x,7) \ + BI(x,8) BI(x,9) BI(x,a) BI(x,b) \ + BI(x,c) BI(x,d) BI(x,e) + /* * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts: * (these are usually mapped to vectors 0x20-0x2f) */ +BUILD_16_IRQS(0x0) /* * The IO-APIC gives us many more interrupt sources. Most of these @@ -58,12 +65,17 @@ * * (these are usually mapped into the 0x30-0xff vector range) */ - BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) + BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3) BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7) BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb) -BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf) +BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) + +#ifdef CONFIG_PCI_MSI + BUILD_15_IRQS(0xe) +#endif #undef BUILD_16_IRQS +#undef BUILD_15_IRQS #undef BI @@ -76,15 +88,29 @@ BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf) IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f) +#define IRQLIST_15(x) \ + IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \ + IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \ + IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \ + IRQ(x,c), IRQ(x,d), IRQ(x,e) + void (*interrupt[NR_IRQS])(void) = { - IRQLIST_16(0x2), IRQLIST_16(0x3), + IRQLIST_16(0x0), + + IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3), IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7), IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb), - IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf) + IRQLIST_16(0xc), IRQLIST_16(0xd) + +#ifdef CONFIG_PCI_MSI + , IRQLIST_15(0xe) +#endif + }; #undef IRQ #undef IRQLIST_16 +#undef IRQLIST_14 /* * This is the 'legacy' 8259A Programmable Interrupt Controller, @@ -95,15 +121,42 @@ void (*interrupt[NR_IRQS])(void) = { * moves to arch independent land */ -static int i8259A_auto_eoi; DEFINE_SPINLOCK(i8259A_lock); + +static int i8259A_auto_eoi; + +static void end_8259A_irq (unsigned int irq) +{ + if (irq > 256) { + char var; + printk("return %p stack %p ti %p\n", __builtin_return_address(0), &var, task_thread_info(current)); + + BUG(); + } + + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && + irq_desc[irq].action) + enable_8259A_irq(irq); +} + +#define shutdown_8259A_irq disable_8259A_irq + static void mask_and_ack_8259A(unsigned int); -static struct irq_chip i8259A_chip = { - .name = "XT-PIC", - .mask = disable_8259A_irq, - .unmask = enable_8259A_irq, - .mask_ack = mask_and_ack_8259A, +static unsigned int startup_8259A_irq(unsigned int irq) +{ + enable_8259A_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type i8259A_irq_type = { + .typename = "XT-PIC", + .startup = startup_8259A_irq, + .shutdown = shutdown_8259A_irq, + .enable = enable_8259A_irq, + .disable = disable_8259A_irq, + .ack = mask_and_ack_8259A, + .end = end_8259A_irq, }; /* @@ -178,7 +231,7 @@ void make_8259A_irq(unsigned int irq) { disable_irq_nosync(irq); io_apic_irqs &= ~(1<= NR_IRQS) + break; if (vector != IA32_SYSCALL_VECTOR) set_intr_gate(vector, interrupt[i]); } @@ -520,7 +554,7 @@ void __init init_IRQ(void) * IRQ0 must be given a fixed assignment and initialized, * because it's used before the IO-APIC is set up. */ - __get_cpu_var(vector_irq)[FIRST_DEVICE_VECTOR] = 0; + set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); /* * The reschedule interrupt is a CPU-to-CPU reschedule-helper diff --git a/trunk/arch/x86_64/kernel/io_apic.c b/trunk/arch/x86_64/kernel/io_apic.c index 91728d9d3472..0491019d4c8d 100644 --- a/trunk/arch/x86_64/kernel/io_apic.c +++ b/trunk/arch/x86_64/kernel/io_apic.c @@ -26,12 +26,9 @@ #include #include #include -#include #include #include #include -#include -#include #ifdef CONFIG_ACPI #include #endif @@ -44,10 +41,6 @@ #include #include #include -#include -#include - -static int assign_irq_vector(int irq, cpumask_t mask); #define __apicdebuginit __init @@ -88,6 +81,14 @@ static struct irq_pin_list { short apic, pin, next; } irq_2_pin[PIN_MAP_SIZE]; +int vector_irq[NR_VECTORS] __read_mostly = { [0 ... NR_VECTORS - 1] = -1}; +#ifdef CONFIG_PCI_MSI +#define vector_to_irq(vector) \ + (platform_legacy_irq(vector) ? vector : vector_irq[vector]) +#else +#define vector_to_irq(vector) (vector) +#endif + #define __DO_ACTION(R, ACTION, FINAL) \ \ { \ @@ -138,35 +139,11 @@ static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) } #ifdef CONFIG_SMP -static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) -{ - int apic, pin; - struct irq_pin_list *entry = irq_2_pin + irq; - - BUG_ON(irq >= NR_IRQS); - for (;;) { - unsigned int reg; - apic = entry->apic; - pin = entry->pin; - if (pin == -1) - break; - io_apic_write(apic, 0x11 + pin*2, dest); - reg = io_apic_read(apic, 0x10 + pin*2); - reg &= ~0x000000ff; - reg |= vector; - io_apic_modify(apic, reg); - if (!entry->next) - break; - entry = irq_2_pin + entry->next; - } -} - static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) { unsigned long flags; unsigned int dest; cpumask_t tmp; - int vector; cpus_and(tmp, mask, cpu_online_map); if (cpus_empty(tmp)) @@ -174,13 +151,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) cpus_and(mask, tmp, CPU_MASK_ALL); - vector = assign_irq_vector(irq, mask); - if (vector < 0) - return; - - cpus_clear(tmp); - cpu_set(vector >> 8, tmp); - dest = cpu_mask_to_apicid(tmp); + dest = cpu_mask_to_apicid(mask); /* * Only the high 8 bits are valid. @@ -188,12 +159,14 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) dest = SET_APIC_LOGICAL_ID(dest); spin_lock_irqsave(&ioapic_lock, flags); - __target_IO_APIC_irq(irq, dest, vector & 0xff); - set_native_irq_info(irq, mask); + __DO_ACTION(1, = dest, ) + set_irq_info(irq, mask); spin_unlock_irqrestore(&ioapic_lock, flags); } #endif +static u8 gsi_2_irq[NR_IRQ_VECTORS] = { [0 ... NR_IRQ_VECTORS-1] = 0xFF }; + /* * The common case is 1:1 IRQ<->pin mappings. Sometimes there are * shared ISA-space IRQs, so we have to support them. We are super @@ -519,6 +492,64 @@ static inline int irq_trigger(int idx) return MPBIOS_trigger(idx); } +static int next_irq = 16; + +/* + * gsi_irq_sharing -- Name overload! "irq" can be either a legacy IRQ + * in the range 0-15, a linux IRQ in the range 0-223, or a GSI number + * from ACPI, which can reach 800 in large boxen. + * + * Compact the sparse GSI space into a sequential IRQ series and reuse + * vectors if possible. + */ +int gsi_irq_sharing(int gsi) +{ + int i, tries, vector; + + BUG_ON(gsi >= NR_IRQ_VECTORS); + + if (platform_legacy_irq(gsi)) + return gsi; + + if (gsi_2_irq[gsi] != 0xFF) + return (int)gsi_2_irq[gsi]; + + tries = NR_IRQS; + try_again: + vector = assign_irq_vector(gsi); + + /* + * Sharing vectors means sharing IRQs, so scan irq_vectors for previous + * use of vector and if found, return that IRQ. However, we never want + * to share legacy IRQs, which usually have a different trigger mode + * than PCI. + */ + for (i = 0; i < NR_IRQS; i++) + if (IO_APIC_VECTOR(i) == vector) + break; + if (platform_legacy_irq(i)) { + if (--tries >= 0) { + IO_APIC_VECTOR(i) = 0; + goto try_again; + } + panic("gsi_irq_sharing: didn't find an IRQ using vector 0x%02X for GSI %d", vector, gsi); + } + if (i < NR_IRQS) { + gsi_2_irq[gsi] = i; + printk(KERN_INFO "GSI %d sharing vector 0x%02X and IRQ %d\n", + gsi, vector, i); + return i; + } + + i = next_irq++; + BUG_ON(i >= NR_IRQS); + gsi_2_irq[gsi] = i; + IO_APIC_VECTOR(i) = vector; + printk(KERN_INFO "GSI %d assigned vector 0x%02X and IRQ %d\n", + gsi, vector, i); + return i; +} + static int pin_2_irq(int idx, int apic, int pin) { int irq, i; @@ -540,6 +571,7 @@ static int pin_2_irq(int idx, int apic, int pin) while (i < apic) irq += nr_ioapic_registers[i++]; irq += pin; + irq = gsi_irq_sharing(irq); } BUG_ON(irq >= NR_IRQS); return irq; @@ -563,83 +595,46 @@ static inline int IO_APIC_irq_trigger(int irq) } /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ -unsigned int irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_EXTERNAL_VECTOR, 0 }; +u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 }; -static int __assign_irq_vector(int irq, cpumask_t mask) +int assign_irq_vector(int irq) { - /* - * NOTE! The local APIC isn't very good at handling - * multiple interrupts at the same interrupt level. - * As the interrupt level is determined by taking the - * vector number and shifting that right by 4, we - * want to spread these out a bit so that they don't - * all fall in the same interrupt level. - * - * Also, we've got to be careful not to trash gate - * 0x80, because int 0x80 is hm, kind of importantish. ;) - */ - static struct { - int vector; - int offset; - } pos[NR_CPUS] = { [ 0 ... NR_CPUS - 1] = {FIRST_DEVICE_VECTOR, 0} }; - int old_vector = -1; - int cpu; - - BUG_ON((unsigned)irq >= NR_IRQ_VECTORS); - - if (IO_APIC_VECTOR(irq) > 0) - old_vector = IO_APIC_VECTOR(irq); - if ((old_vector > 0) && cpu_isset(old_vector >> 8, mask)) { - return old_vector; - } + static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; + unsigned long flags; + int vector; + + BUG_ON(irq != AUTO_ASSIGN && (unsigned)irq >= NR_IRQ_VECTORS); + + spin_lock_irqsave(&vector_lock, flags); - for_each_cpu_mask(cpu, mask) { - int vector, offset; - vector = pos[cpu].vector; - offset = pos[cpu].offset; + if (irq != AUTO_ASSIGN && IO_APIC_VECTOR(irq) > 0) { + spin_unlock_irqrestore(&vector_lock, flags); + return IO_APIC_VECTOR(irq); + } next: - vector += 8; - if (vector >= FIRST_SYSTEM_VECTOR) { - /* If we run out of vectors on large boxen, must share them. */ - offset = (offset + 1) % 8; - vector = FIRST_DEVICE_VECTOR + offset; - } - if (unlikely(pos[cpu].vector == vector)) - continue; - if (vector == IA32_SYSCALL_VECTOR) - goto next; - if (per_cpu(vector_irq, cpu)[vector] != -1) - goto next; - /* Found one! */ - pos[cpu].vector = vector; - pos[cpu].offset = offset; - if (old_vector >= 0) { - int old_cpu = old_vector >> 8; - old_vector &= 0xff; - per_cpu(vector_irq, old_cpu)[old_vector] = -1; - } - per_cpu(vector_irq, cpu)[vector] = irq; - vector |= cpu << 8; - IO_APIC_VECTOR(irq) = vector; - return vector; + current_vector += 8; + if (current_vector == IA32_SYSCALL_VECTOR) + goto next; + + if (current_vector >= FIRST_SYSTEM_VECTOR) { + /* If we run out of vectors on large boxen, must share them. */ + offset = (offset + 1) % 8; + current_vector = FIRST_DEVICE_VECTOR + offset; } - return -ENOSPC; -} -static int assign_irq_vector(int irq, cpumask_t mask) -{ - int vector; - unsigned long flags; + vector = current_vector; + vector_irq[vector] = irq; + if (irq != AUTO_ASSIGN) + IO_APIC_VECTOR(irq) = vector; - spin_lock_irqsave(&vector_lock, flags); - vector = __assign_irq_vector(irq, mask); spin_unlock_irqrestore(&vector_lock, flags); + return vector; } extern void (*interrupt[NR_IRQS])(void); - -static struct irq_chip ioapic_chip; +static struct hw_interrupt_type ioapic_level_type; +static struct hw_interrupt_type ioapic_edge_type; #define IOAPIC_AUTO -1 #define IOAPIC_EDGE 0 @@ -647,13 +642,16 @@ static struct irq_chip ioapic_chip; static void ioapic_register_intr(int irq, int vector, unsigned long trigger) { + unsigned idx; + + idx = use_pci_vector() && !platform_legacy_irq(irq) ? vector : irq; + if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || trigger == IOAPIC_LEVEL) - set_irq_chip_and_handler(irq, &ioapic_chip, - handle_fasteoi_irq); + irq_desc[idx].chip = &ioapic_level_type; else - set_irq_chip_and_handler(irq, &ioapic_chip, - handle_edge_irq); + irq_desc[idx].chip = &ioapic_edge_type; + set_intr_gate(vector, interrupt[idx]); } static void __init setup_IO_APIC_irqs(void) @@ -703,15 +701,8 @@ static void __init setup_IO_APIC_irqs(void) continue; if (IO_APIC_IRQ(irq)) { - cpumask_t mask; - vector = assign_irq_vector(irq, TARGET_CPUS); - if (vector < 0) - continue; - - cpus_clear(mask); - cpu_set(vector >> 8, mask); - entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); - entry.vector = vector & 0xff; + vector = assign_irq_vector(irq); + entry.vector = vector; ioapic_register_intr(irq, vector, IOAPIC_AUTO); if (!apic && (irq < 16)) @@ -761,7 +752,7 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in * The timer IRQ doesn't have to know that behind the * scene we have a 8259A-master in AEOI mode ... */ - set_irq_chip_and_handler(0, &ioapic_chip, handle_edge_irq); + irq_desc[0].chip = &ioapic_edge_type; /* * Add it to the IO-APIC irq-routing table: @@ -877,12 +868,17 @@ void __apicdebuginit print_IO_APIC(void) ); } } + if (use_pci_vector()) + printk(KERN_INFO "Using vector-based indexing\n"); printk(KERN_DEBUG "IRQ to pin mappings:\n"); for (i = 0; i < NR_IRQS; i++) { struct irq_pin_list *entry = irq_2_pin + i; if (entry->pin < 0) continue; - printk(KERN_DEBUG "IRQ%d ", i); + if (use_pci_vector() && !platform_legacy_irq(i)) + printk(KERN_DEBUG "IRQ%d ", IO_APIC_VECTOR(i)); + else + printk(KERN_DEBUG "IRQ%d ", i); for (;;) { printk("-> %d:%d", entry->apic, entry->pin); if (!entry->next) @@ -1189,7 +1185,7 @@ static int __init timer_irq_works(void) * an edge even if it isn't on the 8259A... */ -static unsigned int startup_ioapic_irq(unsigned int irq) +static unsigned int startup_edge_ioapic_irq(unsigned int irq) { int was_pending = 0; unsigned long flags; @@ -1206,16 +1202,107 @@ static unsigned int startup_ioapic_irq(unsigned int irq) return was_pending; } -static int ioapic_retrigger_irq(unsigned int irq) +/* + * Once we have recorded IRQ_PENDING already, we can mask the + * interrupt for real. This prevents IRQ storms from unhandled + * devices. + */ +static void ack_edge_ioapic_irq(unsigned int irq) +{ + move_irq(irq); + if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED)) + == (IRQ_PENDING | IRQ_DISABLED)) + mask_IO_APIC_irq(irq); + ack_APIC_irq(); +} + +/* + * Level triggered interrupts can just be masked, + * and shutting down and starting up the interrupt + * is the same as enabling and disabling them -- except + * with a startup need to return a "was pending" value. + * + * Level triggered interrupts are special because we + * do not touch any IO-APIC register while handling + * them. We ack the APIC in the end-IRQ handler, not + * in the start-IRQ-handler. Protection against reentrance + * from the same interrupt is still provided, both by the + * generic IRQ layer and by the fact that an unacked local + * APIC does not accept IRQs. + */ +static unsigned int startup_level_ioapic_irq (unsigned int irq) +{ + unmask_IO_APIC_irq(irq); + + return 0; /* don't check for pending */ +} + +static void end_level_ioapic_irq (unsigned int irq) +{ + move_irq(irq); + ack_APIC_irq(); +} + +#ifdef CONFIG_PCI_MSI +static unsigned int startup_edge_ioapic_vector(unsigned int vector) +{ + int irq = vector_to_irq(vector); + + return startup_edge_ioapic_irq(irq); +} + +static void ack_edge_ioapic_vector(unsigned int vector) +{ + int irq = vector_to_irq(vector); + + move_native_irq(vector); + ack_edge_ioapic_irq(irq); +} + +static unsigned int startup_level_ioapic_vector (unsigned int vector) { - cpumask_t mask; - unsigned vector; + int irq = vector_to_irq(vector); - vector = irq_vector[irq]; - cpus_clear(mask); - cpu_set(vector >> 8, mask); + return startup_level_ioapic_irq (irq); +} + +static void end_level_ioapic_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + move_native_irq(vector); + end_level_ioapic_irq(irq); +} + +static void mask_IO_APIC_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + mask_IO_APIC_irq(irq); +} - send_IPI_mask(mask, vector & 0xff); +static void unmask_IO_APIC_vector (unsigned int vector) +{ + int irq = vector_to_irq(vector); + + unmask_IO_APIC_irq(irq); +} + +#ifdef CONFIG_SMP +static void set_ioapic_affinity_vector (unsigned int vector, + cpumask_t cpu_mask) +{ + int irq = vector_to_irq(vector); + + set_native_irq_info(vector, cpu_mask); + set_ioapic_affinity_irq(irq, cpu_mask); +} +#endif // CONFIG_SMP +#endif // CONFIG_PCI_MSI + +static int ioapic_retrigger(unsigned int irq) +{ + send_IPI_self(IO_APIC_VECTOR(irq)); return 1; } @@ -1229,47 +1316,32 @@ static int ioapic_retrigger_irq(unsigned int irq) * races. */ -static void ack_apic_edge(unsigned int irq) -{ - move_native_irq(irq); - ack_APIC_irq(); -} - -static void ack_apic_level(unsigned int irq) -{ - int do_unmask_irq = 0; - -#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) - /* If we are moving the irq we need to mask it */ - if (unlikely(irq_desc[irq].status & IRQ_MOVE_PENDING)) { - do_unmask_irq = 1; - mask_IO_APIC_irq(irq); - } +static struct hw_interrupt_type ioapic_edge_type __read_mostly = { + .typename = "IO-APIC-edge", + .startup = startup_edge_ioapic, + .shutdown = shutdown_edge_ioapic, + .enable = enable_edge_ioapic, + .disable = disable_edge_ioapic, + .ack = ack_edge_ioapic, + .end = end_edge_ioapic, +#ifdef CONFIG_SMP + .set_affinity = set_ioapic_affinity, #endif + .retrigger = ioapic_retrigger, +}; - /* - * We must acknowledge the irq before we move it or the acknowledge will - * not propogate properly. - */ - ack_APIC_irq(); - - /* Now we can move and renable the irq */ - move_masked_irq(irq); - if (unlikely(do_unmask_irq)) - unmask_IO_APIC_irq(irq); -} - -static struct irq_chip ioapic_chip __read_mostly = { - .name = "IO-APIC", - .startup = startup_ioapic_irq, - .mask = mask_IO_APIC_irq, - .unmask = unmask_IO_APIC_irq, - .ack = ack_apic_edge, - .eoi = ack_apic_level, +static struct hw_interrupt_type ioapic_level_type __read_mostly = { + .typename = "IO-APIC-level", + .startup = startup_level_ioapic, + .shutdown = shutdown_level_ioapic, + .enable = enable_level_ioapic, + .disable = disable_level_ioapic, + .ack = mask_and_ack_level_ioapic, + .end = end_level_ioapic, #ifdef CONFIG_SMP - .set_affinity = set_ioapic_affinity_irq, + .set_affinity = set_ioapic_affinity, #endif - .retrigger = ioapic_retrigger_irq, + .retrigger = ioapic_retrigger, }; static inline void init_IO_APIC_traps(void) @@ -1289,6 +1361,11 @@ static inline void init_IO_APIC_traps(void) */ for (irq = 0; irq < NR_IRQS ; irq++) { int tmp = irq; + if (use_pci_vector()) { + if (!platform_legacy_irq(tmp)) + if ((tmp = vector_to_irq(tmp)) == -1) + continue; + } if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) { /* * Hmm.. We don't have an entry for this, @@ -1299,7 +1376,7 @@ static inline void init_IO_APIC_traps(void) make_8259A_irq(irq); else /* Strange. Oh, well.. */ - irq_desc[irq].chip = &no_irq_chip; + irq_desc[irq].chip = &no_irq_type; } } } @@ -1418,6 +1495,8 @@ static inline void unlock_ExtINT_logic(void) spin_unlock_irqrestore(&ioapic_lock, flags); } +int timer_uses_ioapic_pin_0; + /* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ @@ -1435,7 +1514,8 @@ static inline void check_timer(void) * get/set the timer IRQ vector: */ disable_8259A_irq(0); - vector = assign_irq_vector(0, TARGET_CPUS); + vector = assign_irq_vector(0); + set_intr_gate(vector, interrupt[0]); /* * Subtle, code in do_timer_interrupt() expects an AEOI @@ -1454,6 +1534,9 @@ static inline void check_timer(void) pin2 = ioapic_i8259.pin; apic2 = ioapic_i8259.apic; + if (pin1 == 0) + timer_uses_ioapic_pin_0 = 1; + apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", vector, apic1, pin1, apic2, pin2); @@ -1657,253 +1740,6 @@ static int __init ioapic_init_sysfs(void) device_initcall(ioapic_init_sysfs); -/* - * Dynamic irq allocate and deallocation - */ -int create_irq(void) -{ - /* Allocate an unused irq */ - int irq; - int new; - int vector = 0; - unsigned long flags; - - irq = -ENOSPC; - spin_lock_irqsave(&vector_lock, flags); - for (new = (NR_IRQS - 1); new >= 0; new--) { - if (platform_legacy_irq(new)) - continue; - if (irq_vector[new] != 0) - continue; - vector = __assign_irq_vector(new, TARGET_CPUS); - if (likely(vector > 0)) - irq = new; - break; - } - spin_unlock_irqrestore(&vector_lock, flags); - - if (irq >= 0) { - dynamic_irq_init(irq); - } - return irq; -} - -void destroy_irq(unsigned int irq) -{ - unsigned long flags; - - dynamic_irq_cleanup(irq); - - spin_lock_irqsave(&vector_lock, flags); - irq_vector[irq] = 0; - spin_unlock_irqrestore(&vector_lock, flags); -} - -/* - * MSI mesage composition - */ -#ifdef CONFIG_PCI_MSI -static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg) -{ - int vector; - unsigned dest; - - vector = assign_irq_vector(irq, TARGET_CPUS); - if (vector >= 0) { - cpumask_t tmp; - - cpus_clear(tmp); - cpu_set(vector >> 8, tmp); - dest = cpu_mask_to_apicid(tmp); - - msg->address_hi = MSI_ADDR_BASE_HI; - msg->address_lo = - MSI_ADDR_BASE_LO | - ((INT_DEST_MODE == 0) ? - MSI_ADDR_DEST_MODE_PHYSICAL: - MSI_ADDR_DEST_MODE_LOGICAL) | - ((INT_DELIVERY_MODE != dest_LowestPrio) ? - MSI_ADDR_REDIRECTION_CPU: - MSI_ADDR_REDIRECTION_LOWPRI) | - MSI_ADDR_DEST_ID(dest); - - msg->data = - MSI_DATA_TRIGGER_EDGE | - MSI_DATA_LEVEL_ASSERT | - ((INT_DELIVERY_MODE != dest_LowestPrio) ? - MSI_DATA_DELIVERY_FIXED: - MSI_DATA_DELIVERY_LOWPRI) | - MSI_DATA_VECTOR(vector); - } - return vector; -} - -#ifdef CONFIG_SMP -static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) -{ - struct msi_msg msg; - unsigned int dest; - cpumask_t tmp; - int vector; - - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) - tmp = TARGET_CPUS; - - cpus_and(mask, tmp, CPU_MASK_ALL); - - vector = assign_irq_vector(irq, mask); - if (vector < 0) - return; - - cpus_clear(tmp); - cpu_set(vector >> 8, tmp); - dest = cpu_mask_to_apicid(tmp); - - read_msi_msg(irq, &msg); - - msg.data &= ~MSI_DATA_VECTOR_MASK; - msg.data |= MSI_DATA_VECTOR(vector); - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; - msg.address_lo |= MSI_ADDR_DEST_ID(dest); - - write_msi_msg(irq, &msg); - set_native_irq_info(irq, mask); -} -#endif /* CONFIG_SMP */ - -/* - * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, - * which implement the MSI or MSI-X Capability Structure. - */ -static struct irq_chip msi_chip = { - .name = "PCI-MSI", - .unmask = unmask_msi_irq, - .mask = mask_msi_irq, - .ack = ack_apic_edge, -#ifdef CONFIG_SMP - .set_affinity = set_msi_irq_affinity, -#endif - .retrigger = ioapic_retrigger_irq, -}; - -int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev) -{ - struct msi_msg msg; - int ret; - ret = msi_compose_msg(dev, irq, &msg); - if (ret < 0) - return ret; - - write_msi_msg(irq, &msg); - - set_irq_chip_and_handler(irq, &msi_chip, handle_edge_irq); - - return 0; -} - -void arch_teardown_msi_irq(unsigned int irq) -{ - return; -} - -#endif /* CONFIG_PCI_MSI */ - -/* - * Hypertransport interrupt support - */ -#ifdef CONFIG_HT_IRQ - -#ifdef CONFIG_SMP - -static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector) -{ - u32 low, high; - low = read_ht_irq_low(irq); - high = read_ht_irq_high(irq); - - low &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK); - high &= ~(HT_IRQ_HIGH_DEST_ID_MASK); - - low |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest); - high |= HT_IRQ_HIGH_DEST_ID(dest); - - write_ht_irq_low(irq, low); - write_ht_irq_high(irq, high); -} - -static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) -{ - unsigned int dest; - cpumask_t tmp; - int vector; - - cpus_and(tmp, mask, cpu_online_map); - if (cpus_empty(tmp)) - tmp = TARGET_CPUS; - - cpus_and(mask, tmp, CPU_MASK_ALL); - - vector = assign_irq_vector(irq, mask); - if (vector < 0) - return; - - cpus_clear(tmp); - cpu_set(vector >> 8, tmp); - dest = cpu_mask_to_apicid(tmp); - - target_ht_irq(irq, dest, vector & 0xff); - set_native_irq_info(irq, mask); -} -#endif - -static struct hw_interrupt_type ht_irq_chip = { - .name = "PCI-HT", - .mask = mask_ht_irq, - .unmask = unmask_ht_irq, - .ack = ack_apic_edge, -#ifdef CONFIG_SMP - .set_affinity = set_ht_irq_affinity, -#endif - .retrigger = ioapic_retrigger_irq, -}; - -int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) -{ - int vector; - - vector = assign_irq_vector(irq, TARGET_CPUS); - if (vector >= 0) { - u32 low, high; - unsigned dest; - cpumask_t tmp; - - cpus_clear(tmp); - cpu_set(vector >> 8, tmp); - dest = cpu_mask_to_apicid(tmp); - - high = HT_IRQ_HIGH_DEST_ID(dest); - - low = HT_IRQ_LOW_BASE | - HT_IRQ_LOW_DEST_ID(dest) | - HT_IRQ_LOW_VECTOR(vector) | - ((INT_DEST_MODE == 0) ? - HT_IRQ_LOW_DM_PHYSICAL : - HT_IRQ_LOW_DM_LOGICAL) | - HT_IRQ_LOW_RQEOI_EDGE | - ((INT_DELIVERY_MODE != dest_LowestPrio) ? - HT_IRQ_LOW_MT_FIXED : - HT_IRQ_LOW_MT_ARBITRATED); - - write_ht_irq_low(irq, low); - write_ht_irq_high(irq, high); - - set_irq_chip_and_handler(irq, &ht_irq_chip, handle_edge_irq); - } - return vector; -} -#endif /* CONFIG_HT_IRQ */ - /* -------------------------------------------------------------------------- ACPI-based IOAPIC Configuration -------------------------------------------------------------------------- */ @@ -1929,8 +1765,6 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p { struct IO_APIC_route_entry entry; unsigned long flags; - int vector; - cpumask_t mask; if (!IO_APIC_IRQ(irq)) { apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n", @@ -1938,20 +1772,6 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p return -EINVAL; } - /* - * IRQs < 16 are already in the irq_2_pin[] map - */ - if (irq >= 16) - add_pin_to_irq(irq, ioapic, pin); - - - vector = assign_irq_vector(irq, TARGET_CPUS); - if (vector < 0) - return vector; - - cpus_clear(mask); - cpu_set(vector >> 8, mask); - /* * Generate a PCI IRQ routing entry and program the IOAPIC accordingly. * Note that we mask (disable) IRQs now -- these get enabled when the @@ -1962,11 +1782,19 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p entry.delivery_mode = INT_DELIVERY_MODE; entry.dest_mode = INT_DEST_MODE; - entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); + entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); entry.trigger = triggering; entry.polarity = polarity; entry.mask = 1; /* Disabled (masked) */ - entry.vector = vector & 0xff; + + irq = gsi_irq_sharing(irq); + /* + * IRQs < 16 are already in the irq_2_pin[] map + */ + if (irq >= 16) + add_pin_to_irq(irq, ioapic, pin); + + entry.vector = assign_irq_vector(irq); apic_printk(APIC_VERBOSE,KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " "IRQ %d Mode:%i Active:%i)\n", ioapic, @@ -1981,7 +1809,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p ioapic_write_entry(ioapic, pin, entry); spin_lock_irqsave(&ioapic_lock, flags); - set_native_irq_info(irq, TARGET_CPUS); + set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); return 0; diff --git a/trunk/arch/x86_64/kernel/irq.c b/trunk/arch/x86_64/kernel/irq.c index 506f27c85ca5..b3677e6ccc6e 100644 --- a/trunk/arch/x86_64/kernel/irq.c +++ b/trunk/arch/x86_64/kernel/irq.c @@ -74,8 +74,7 @@ int show_interrupts(struct seq_file *p, void *v) for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif - seq_printf(p, " %8s", irq_desc[i].chip->name); - seq_printf(p, "-%s", handle_irq_name(irq_desc[i].handle_irq)); + seq_printf(p, " %14s", irq_desc[i].chip->typename); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) @@ -105,12 +104,7 @@ int show_interrupts(struct seq_file *p, void *v) asmlinkage unsigned int do_IRQ(struct pt_regs *regs) { /* high bit used in ret_from_ code */ - unsigned vector = ~regs->orig_rax; - unsigned irq; - - exit_idle(); - irq_enter(); - irq = __get_cpu_var(vector_irq)[vector]; + unsigned irq = ~regs->orig_rax; if (unlikely(irq >= NR_IRQS)) { printk(KERN_EMERG "%s: cannot handle IRQ %d\n", @@ -118,10 +112,12 @@ asmlinkage unsigned int do_IRQ(struct pt_regs *regs) BUG(); } + exit_idle(); + irq_enter(); #ifdef CONFIG_DEBUG_STACKOVERFLOW stack_overflow_check(regs); #endif - generic_handle_irq(irq, regs); + __do_IRQ(irq, regs); irq_exit(); return 1; diff --git a/trunk/arch/x86_64/kernel/mpparse.c b/trunk/arch/x86_64/kernel/mpparse.c index b147ab19fbd4..b8d53dfa9931 100644 --- a/trunk/arch/x86_64/kernel/mpparse.c +++ b/trunk/arch/x86_64/kernel/mpparse.c @@ -790,11 +790,20 @@ void __init mp_config_acpi_legacy_irqs(void) } } +#define MAX_GSI_NUM 4096 + int mp_register_gsi(u32 gsi, int triggering, int polarity) { int ioapic = -1; int ioapic_pin = 0; int idx, bit = 0; + static int pci_irq = 16; + /* + * Mapping between Global System Interrupts, which + * represent all possible interrupts, to the IRQs + * assigned to actual devices. + */ + static int gsi_to_irq[MAX_GSI_NUM]; if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC) return gsi; @@ -827,11 +836,42 @@ int mp_register_gsi(u32 gsi, int triggering, int polarity) if ((1< 15), but + * avoid a problem where the 8254 timer (IRQ0) is setup + * via an override (so it's not on pin 0 of the ioapic), + * and at the same time, the pin 0 interrupt is a PCI + * type. The gsi > 15 test could cause these two pins + * to be shared as IRQ0, and they are not shareable. + * So test for this condition, and if necessary, avoid + * the pin collision. + */ + if (gsi > 15 || (gsi == 0 && !timer_uses_ioapic_pin_0)) + gsi = pci_irq++; + /* + * Don't assign IRQ used by ACPI SCI + */ + if (gsi == acpi_fadt.sci_int) + gsi = pci_irq++; + gsi_to_irq[irq] = gsi; + } else { + printk(KERN_ERR "GSI %u is too high\n", gsi); + return gsi; + } + } + io_apic_set_pci_routing(ioapic, ioapic_pin, gsi, triggering == ACPI_EDGE_SENSITIVE ? 0 : 1, polarity == ACPI_ACTIVE_HIGH ? 0 : 1); diff --git a/trunk/arch/x86_64/kernel/pci-calgary.c b/trunk/arch/x86_64/kernel/pci-calgary.c index cfb09b07ae99..f760045d6d35 100644 --- a/trunk/arch/x86_64/kernel/pci-calgary.c +++ b/trunk/arch/x86_64/kernel/pci-calgary.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/x86_64/kernel/tce.c b/trunk/arch/x86_64/kernel/tce.c index cbabfdf78e06..f61fb8e4f129 100644 --- a/trunk/arch/x86_64/kernel/tce.c +++ b/trunk/arch/x86_64/kernel/tce.c @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/trunk/arch/x86_64/kernel/x8664_ksyms.c b/trunk/arch/x86_64/kernel/x8664_ksyms.c index c3454af5e3a2..6d77e4797a47 100644 --- a/trunk/arch/x86_64/kernel/x8664_ksyms.c +++ b/trunk/arch/x86_64/kernel/x8664_ksyms.c @@ -1,7 +1,6 @@ /* Exports for assembly files. All C exports should go in the respective C files. */ -#include #include #include diff --git a/trunk/arch/x86_64/lib/copy_page.S b/trunk/arch/x86_64/lib/copy_page.S index 0ebb03b60e79..727a5d46d2fc 100644 --- a/trunk/arch/x86_64/lib/copy_page.S +++ b/trunk/arch/x86_64/lib/copy_page.S @@ -1,6 +1,5 @@ /* Written 2003 by Andi Kleen, based on a kernel by Evandro Menezes */ - -#include + #include #include diff --git a/trunk/arch/x86_64/lib/delay.c b/trunk/arch/x86_64/lib/delay.c index b6cd3cca2f45..50be90975d04 100644 --- a/trunk/arch/x86_64/lib/delay.c +++ b/trunk/arch/x86_64/lib/delay.c @@ -8,7 +8,6 @@ * depends wildly on alignment on many x86 processors. */ -#include #include #include #include diff --git a/trunk/arch/x86_64/lib/memcpy.S b/trunk/arch/x86_64/lib/memcpy.S index 967b22fa7d07..0ea0ddc875a7 100644 --- a/trunk/arch/x86_64/lib/memcpy.S +++ b/trunk/arch/x86_64/lib/memcpy.S @@ -1,6 +1,5 @@ /* Copyright 2002 Andi Kleen */ - -#include + #include #include #include diff --git a/trunk/arch/x86_64/lib/memset.S b/trunk/arch/x86_64/lib/memset.S index 09ed1f6b0eaa..2c5948116bd2 100644 --- a/trunk/arch/x86_64/lib/memset.S +++ b/trunk/arch/x86_64/lib/memset.S @@ -1,6 +1,5 @@ /* Copyright 2002 Andi Kleen, SuSE Labs */ -#include #include #include diff --git a/trunk/arch/x86_64/lib/thunk.S b/trunk/arch/x86_64/lib/thunk.S index 0025535cac8d..55e586d352d3 100644 --- a/trunk/arch/x86_64/lib/thunk.S +++ b/trunk/arch/x86_64/lib/thunk.S @@ -5,7 +5,6 @@ * Subject to the GNU public license, v.2. No warranty of any kind. */ - #include #include #include #include diff --git a/trunk/drivers/ata/ahci.c b/trunk/drivers/ata/ahci.c index 54e1f38ce301..1aabc81d82f1 100644 --- a/trunk/drivers/ata/ahci.c +++ b/trunk/drivers/ata/ahci.c @@ -299,46 +299,76 @@ static const struct ata_port_info ahci_port_info[] = { static const struct pci_device_id ahci_pci_tbl[] = { /* Intel */ - { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */ - { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */ - { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */ - { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */ - { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */ - { PCI_VDEVICE(AL, 0x5288), board_ahci }, /* ULi M5288 */ - { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */ - { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */ - { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */ - { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */ - { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */ - { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */ - { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */ - { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */ - { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */ + { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH6 */ + { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH6M */ + { PCI_VENDOR_ID_INTEL, 0x27c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH7 */ + { PCI_VENDOR_ID_INTEL, 0x27c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH7M */ + { PCI_VENDOR_ID_INTEL, 0x27c3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH7R */ + { PCI_VENDOR_ID_AL, 0x5288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ULi M5288 */ + { PCI_VENDOR_ID_INTEL, 0x2681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ESB2 */ + { PCI_VENDOR_ID_INTEL, 0x2682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ESB2 */ + { PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ESB2 */ + { PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH7-M DH */ + { PCI_VENDOR_ID_INTEL, 0x2821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH8 */ + { PCI_VENDOR_ID_INTEL, 0x2822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH8 */ + { PCI_VENDOR_ID_INTEL, 0x2824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH8 */ + { PCI_VENDOR_ID_INTEL, 0x2829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH8M */ + { PCI_VENDOR_ID_INTEL, 0x282a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH8M */ /* JMicron */ - { PCI_VDEVICE(JMICRON, 0x2360), board_ahci }, /* JMicron JMB360 */ - { PCI_VDEVICE(JMICRON, 0x2361), board_ahci }, /* JMicron JMB361 */ - { PCI_VDEVICE(JMICRON, 0x2363), board_ahci }, /* JMicron JMB363 */ - { PCI_VDEVICE(JMICRON, 0x2365), board_ahci }, /* JMicron JMB365 */ - { PCI_VDEVICE(JMICRON, 0x2366), board_ahci }, /* JMicron JMB366 */ + { 0x197b, 0x2360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* JMicron JMB360 */ + { 0x197b, 0x2361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* JMicron JMB361 */ + { 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* JMicron JMB363 */ + { 0x197b, 0x2365, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* JMicron JMB365 */ + { 0x197b, 0x2366, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* JMicron JMB366 */ /* ATI */ - { PCI_VDEVICE(ATI, 0x4380), board_ahci }, /* ATI SB600 non-raid */ - { PCI_VDEVICE(ATI, 0x4381), board_ahci }, /* ATI SB600 raid */ + { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB600 non-raid */ + { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB600 raid */ /* VIA */ - { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ + { PCI_VENDOR_ID_VIA, 0x3349, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci_vt8251 }, /* VIA VT8251 */ /* NVIDIA */ - { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */ - { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci }, /* MCP65 */ - { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci }, /* MCP65 */ - { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ + { PCI_VENDOR_ID_NVIDIA, 0x044f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* MCP65 */ /* SiS */ - { PCI_VDEVICE(SI, 0x1184), board_ahci }, /* SiS 966 */ - { PCI_VDEVICE(SI, 0x1185), board_ahci }, /* SiS 966 */ - { PCI_VDEVICE(SI, 0x0186), board_ahci }, /* SiS 968 */ + { PCI_VENDOR_ID_SI, 0x1184, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 966 */ + { PCI_VENDOR_ID_SI, 0x1185, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 966 */ + { PCI_VENDOR_ID_SI, 0x0186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* SiS 968 */ { } /* terminate list */ }; diff --git a/trunk/drivers/ata/libata-core.c b/trunk/drivers/ata/libata-core.c index dce65651d858..b4abd6850367 100644 --- a/trunk/drivers/ata/libata-core.c +++ b/trunk/drivers/ata/libata-core.c @@ -2340,8 +2340,7 @@ unsigned int ata_busy_sleep (struct ata_port *ap, if (status & ATA_BUSY) ata_port_printk(ap, KERN_WARNING, - "port is slow to respond, please be patient " - "(Status 0x%x)\n", status); + "port is slow to respond, please be patient\n"); timeout = timer_start + tmout; while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) { @@ -2351,8 +2350,7 @@ unsigned int ata_busy_sleep (struct ata_port *ap, if (status & ATA_BUSY) { ata_port_printk(ap, KERN_ERR, "port failed to respond " - "(%lu secs, Status 0x%x)\n", - tmout / HZ, status); + "(%lu secs)\n", tmout / HZ); return 1; } @@ -5480,10 +5478,11 @@ int ata_device_add(const struct ata_probe_ent *ent) int irq_line = ent->irq; ap = ata_port_add(ent, host, i); - host->ports[i] = ap; if (!ap) goto err_out; + host->ports[i] = ap; + /* dummy? */ if (ent->dummy_port_mask & (1 << i)) { ata_port_printk(ap, KERN_INFO, "DUMMY\n"); @@ -5741,7 +5740,7 @@ void ata_host_remove(struct ata_host *host) /** * ata_scsi_release - SCSI layer callback hook for host unload - * @shost: libata host to be unloaded + * @host: libata host to be unloaded * * Performs all duties necessary to shut down a libata port... * Kill port kthread, disable port, and release resources. @@ -5787,7 +5786,6 @@ ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) probe_ent->mwdma_mask = port->mwdma_mask; probe_ent->udma_mask = port->udma_mask; probe_ent->port_ops = port->port_ops; - probe_ent->private_data = port->private_data; return probe_ent; } diff --git a/trunk/drivers/ata/libata-scsi.c b/trunk/drivers/ata/libata-scsi.c index b0d0cc41f3e8..3986ec8741b4 100644 --- a/trunk/drivers/ata/libata-scsi.c +++ b/trunk/drivers/ata/libata-scsi.c @@ -889,7 +889,6 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) { struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev; - unsigned long flags; int max_depth; if (queue_depth < 1) @@ -905,14 +904,6 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) queue_depth = max_depth; scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth); - - spin_lock_irqsave(ap->lock, flags); - if (queue_depth > 1) - dev->flags &= ~ATA_DFLAG_NCQ_OFF; - else - dev->flags |= ATA_DFLAG_NCQ_OFF; - spin_unlock_irqrestore(ap->lock, flags); - return queue_depth; } @@ -1302,8 +1293,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm */ goto nothing_to_do; - if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF | - ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) { + if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ) { /* yay, NCQ */ if (!lba_48_ok(block, n_block)) goto out_of_range; @@ -3184,7 +3174,7 @@ void ata_scsi_dev_rescan(void *data) /** * ata_sas_port_alloc - Allocate port for a SAS attached SATA device - * @host: ATA host container for all SAS ports + * @pdev: PCI device that the scsi device is attached to * @port_info: Information from low-level host driver * @shost: SCSI host that the scsi device is attached to * diff --git a/trunk/drivers/ata/libata-sff.c b/trunk/drivers/ata/libata-sff.c index 06daaa3736a2..08b3a407473e 100644 --- a/trunk/drivers/ata/libata-sff.c +++ b/trunk/drivers/ata/libata-sff.c @@ -828,6 +828,7 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int probe_ent->irq = pdev->irq; probe_ent->irq_flags = IRQF_SHARED; + probe_ent->private_data = port[0]->private_data; if (ports & ATA_PORT_PRIMARY) { probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 0); @@ -877,6 +878,7 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, return NULL; probe_ent->n_ports = 2; + probe_ent->private_data = port[0]->private_data; if (port_mask & ATA_PORT_PRIMARY) { probe_ent->irq = ATA_PRIMARY_IRQ; @@ -906,8 +908,6 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, probe_ent->_host_flags |= ATA_HOST_SIMPLEX; } ata_std_ports(&probe_ent->port[1]); - - /* FIXME: could be pointing to stack area; must copy */ probe_ent->pinfo2 = port[1]; } else probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY; @@ -946,21 +946,35 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, { struct ata_probe_ent *probe_ent = NULL; struct ata_port_info *port[2]; - u8 mask; + u8 tmp8, mask; unsigned int legacy_mode = 0; int disable_dev_on_err = 1; int rc; DPRINTK("ENTER\n"); - BUG_ON(n_ports < 1 || n_ports > 2); - port[0] = port_info[0]; if (n_ports > 1) port[1] = port_info[1]; else port[1] = port[0]; + if ((port[0]->flags & ATA_FLAG_NO_LEGACY) == 0 + && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { + /* TODO: What if one channel is in native mode ... */ + pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); + mask = (1 << 2) | (1 << 0); + if ((tmp8 & mask) != mask) + legacy_mode = (1 << 3); + } + + /* FIXME... */ + if ((!legacy_mode) && (n_ports > 2)) { + printk(KERN_ERR "ata: BUG: native mode, n_ports > 2\n"); + n_ports = 2; + /* For now */ + } + /* FIXME: Really for ATA it isn't safe because the device may be multi-purpose and we want to leave it alone if it was already enabled. Secondly for shared use as Arjan says we want refcounting @@ -973,16 +987,6 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, if (rc) return rc; - if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { - u8 tmp8; - - /* TODO: What if one channel is in native mode ... */ - pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); - mask = (1 << 2) | (1 << 0); - if ((tmp8 & mask) != mask) - legacy_mode = (1 << 3); - } - rc = pci_request_regions(pdev, DRV_NAME); if (rc) { disable_dev_on_err = 0; @@ -1035,7 +1039,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, goto err_out_regions; } - /* TODO: If we get no DMA mask we should fall back to PIO */ + /* FIXME: If we get no DMA mask we should fall back to PIO */ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) goto err_out_regions; @@ -1058,17 +1062,13 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, pci_set_master(pdev); - if (!ata_device_add(probe_ent)) { - rc = -ENODEV; - goto err_out_ent; - } + /* FIXME: check ata_device_add return */ + ata_device_add(probe_ent); kfree(probe_ent); return 0; -err_out_ent: - kfree(probe_ent); err_out_regions: if (legacy_mode & ATA_PORT_PRIMARY) release_region(ATA_PRIMARY_CMD, 8); diff --git a/trunk/drivers/ata/pata_ali.c b/trunk/drivers/ata/pata_ali.c index 1d695df5860a..87af3b5861ab 100644 --- a/trunk/drivers/ata/pata_ali.c +++ b/trunk/drivers/ata/pata_ali.c @@ -34,7 +34,7 @@ #include #define DRV_NAME "pata_ali" -#define DRV_VERSION "0.6.6" +#define DRV_VERSION "0.6.5" /* * Cable special cases @@ -630,7 +630,7 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_read_config_byte(pdev, 0x53, &tmp); if (rev <= 0x20) tmp &= ~0x02; - if (rev >= 0xc7) + if (rev == 0xc7) tmp |= 0x03; else tmp |= 0x01; /* CD_ROM enable for DMA */ @@ -644,11 +644,10 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } -static const struct pci_device_id ali[] = { - { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5228), }, - { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5229), }, - - { }, +static struct pci_device_id ali[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5228), }, + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229), }, + { 0, }, }; static struct pci_driver ali_pci_driver = { diff --git a/trunk/drivers/ata/pata_amd.c b/trunk/drivers/ata/pata_amd.c index 29234c897118..599ee266722c 100644 --- a/trunk/drivers/ata/pata_amd.c +++ b/trunk/drivers/ata/pata_amd.c @@ -662,28 +662,27 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct pci_device_id amd[] = { - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_COBRA_7401), 0 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7409), 1 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7411), 3 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 4 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 5 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 7 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 8 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 8 }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 9 }, - - { }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, + { 0, }, }; static struct pci_driver amd_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = amd, .probe = amd_init_one, .remove = ata_pci_remove_one @@ -699,6 +698,7 @@ static void __exit amd_exit(void) pci_unregister_driver(&amd_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for AMD PATA IDE"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_artop.c b/trunk/drivers/ata/pata_artop.c index 690828eb5226..c4ccb75a4f1d 100644 --- a/trunk/drivers/ata/pata_artop.c +++ b/trunk/drivers/ata/pata_artop.c @@ -426,7 +426,7 @@ static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id) .port_ops = &artop6260_ops, }; struct ata_port_info *port_info[2]; - struct ata_port_info *info = NULL; + struct ata_port_info *info; int ports = 2; if (!printed_version++) @@ -470,20 +470,16 @@ static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id) pci_write_config_byte(pdev, 0x4a, (reg & ~0x01) | 0x80); } - - BUG_ON(info == NULL); - port_info[0] = port_info[1] = info; return ata_pci_init_one(pdev, port_info, ports); } static const struct pci_device_id artop_pci_tbl[] = { - { PCI_VDEVICE(ARTOP, 0x0005), 0 }, - { PCI_VDEVICE(ARTOP, 0x0006), 1 }, - { PCI_VDEVICE(ARTOP, 0x0007), 1 }, - { PCI_VDEVICE(ARTOP, 0x0008), 2 }, - { PCI_VDEVICE(ARTOP, 0x0009), 2 }, - + { 0x1191, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0x1191, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0x1191, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0x1191, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { 0x1191, 0x0009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, { } /* terminate list */ }; @@ -504,6 +500,7 @@ static void __exit artop_exit(void) pci_unregister_driver(&artop_pci_driver); } + module_init(artop_init); module_exit(artop_exit); diff --git a/trunk/drivers/ata/pata_atiixp.c b/trunk/drivers/ata/pata_atiixp.c index 1ce28d2125f4..6c2269b6bd3c 100644 --- a/trunk/drivers/ata/pata_atiixp.c +++ b/trunk/drivers/ata/pata_atiixp.c @@ -267,13 +267,12 @@ static int atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static const struct pci_device_id atiixp[] = { - { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP200_IDE), }, - { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP300_IDE), }, - { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), }, - { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), }, - - { }, +static struct pci_device_id atiixp[] = { + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_IDE), }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE), }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), }, + { 0, }, }; static struct pci_driver atiixp_pci_driver = { @@ -294,6 +293,7 @@ static void __exit atiixp_exit(void) pci_unregister_driver(&atiixp_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for ATI IXP200/300/400"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_cmd64x.c b/trunk/drivers/ata/pata_cmd64x.c index b9bbd1d454bf..e92b0ef43ec5 100644 --- a/trunk/drivers/ata/pata_cmd64x.c +++ b/trunk/drivers/ata/pata_cmd64x.c @@ -468,17 +468,16 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } -static const struct pci_device_id cmd64x[] = { - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 4 }, - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 5 }, - - { }, +static struct pci_device_id cmd64x[] = { + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { 0, }, }; static struct pci_driver cmd64x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = cmd64x, .probe = cmd64x_init_one, .remove = ata_pci_remove_one @@ -489,11 +488,13 @@ static int __init cmd64x_init(void) return pci_register_driver(&cmd64x_pci_driver); } + static void __exit cmd64x_exit(void) { pci_unregister_driver(&cmd64x_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for CMD64x series PATA controllers"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_cs5520.c b/trunk/drivers/ata/pata_cs5520.c index 2cd3c0ff76df..a6c6cebd0dae 100644 --- a/trunk/drivers/ata/pata_cs5520.c +++ b/trunk/drivers/ata/pata_cs5520.c @@ -299,11 +299,10 @@ static void __devexit cs5520_remove_one(struct pci_dev *pdev) /* For now keep DMA off. We can set it for all but A rev CS5510 once the core ATA code can handle it */ -static const struct pci_device_id pata_cs5520[] = { - { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, - { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5520), }, - - { }, +static struct pci_device_id pata_cs5520[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, + { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520), }, + { 0, }, }; static struct pci_driver cs5520_pci_driver = { @@ -313,6 +312,7 @@ static struct pci_driver cs5520_pci_driver = { .remove = cs5520_remove_one }; + static int __init cs5520_init(void) { return pci_register_driver(&cs5520_pci_driver); diff --git a/trunk/drivers/ata/pata_cs5530.c b/trunk/drivers/ata/pata_cs5530.c index a07cc81ef791..7bba4d954e9c 100644 --- a/trunk/drivers/ata/pata_cs5530.c +++ b/trunk/drivers/ata/pata_cs5530.c @@ -353,14 +353,13 @@ static int cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; } -static const struct pci_device_id cs5530[] = { - { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, - - { }, +static struct pci_device_id cs5530[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, + { 0, }, }; static struct pci_driver cs5530_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = cs5530, .probe = cs5530_init_one, .remove = ata_pci_remove_one @@ -371,11 +370,13 @@ static int __init cs5530_init(void) return pci_register_driver(&cs5530_pci_driver); } + static void __exit cs5530_exit(void) { pci_unregister_driver(&cs5530_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Cyrix/NS/AMD 5530"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_cs5535.c b/trunk/drivers/ata/pata_cs5535.c index f8def3f9c618..d64fcdceaf01 100644 --- a/trunk/drivers/ata/pata_cs5535.c +++ b/trunk/drivers/ata/pata_cs5535.c @@ -257,10 +257,9 @@ static int cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, ports, 1); } -static const struct pci_device_id cs5535[] = { - { PCI_VDEVICE(NS, 0x002D), }, - - { }, +static struct pci_device_id cs5535[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, 0x002D), }, + { 0, }, }; static struct pci_driver cs5535_pci_driver = { @@ -275,11 +274,13 @@ static int __init cs5535_init(void) return pci_register_driver(&cs5535_pci_driver); } + static void __exit cs5535_exit(void) { pci_unregister_driver(&cs5535_pci_driver); } + MODULE_AUTHOR("Alan Cox, Jens Altmann, Wolfgan Zuleger, Alexander Kiausch"); MODULE_DESCRIPTION("low-level driver for the NS/AMD 5530"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_cypress.c b/trunk/drivers/ata/pata_cypress.c index 247b43608b14..dfa5ac539048 100644 --- a/trunk/drivers/ata/pata_cypress.c +++ b/trunk/drivers/ata/pata_cypress.c @@ -184,8 +184,8 @@ static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *i }; static struct ata_port_info *port_info[1] = { &info }; - /* Devfn 1 is the ATA primary. The secondary is magic and on devfn2. - For the moment we don't handle the secondary. FIXME */ + /* Devfn 1 is the ATA primary. The secondary is magic and on devfn2. For the + moment we don't handle the secondary. FIXME */ if (PCI_FUNC(pdev->devfn) != 1) return -ENODEV; @@ -193,14 +193,13 @@ static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *i return ata_pci_init_one(pdev, port_info, 1); } -static const struct pci_device_id cy82c693[] = { - { PCI_VDEVICE(CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693), }, - - { }, +static struct pci_device_id cy82c693[] = { + { PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, }; static struct pci_driver cy82c693_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = cy82c693, .probe = cy82c693_init_one, .remove = ata_pci_remove_one diff --git a/trunk/drivers/ata/pata_efar.c b/trunk/drivers/ata/pata_efar.c index ef18c60fe140..95cd1ca181f5 100644 --- a/trunk/drivers/ata/pata_efar.c +++ b/trunk/drivers/ata/pata_efar.c @@ -305,8 +305,7 @@ static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } static const struct pci_device_id efar_pci_tbl[] = { - { PCI_VDEVICE(EFAR, 0x9130), }, - + { 0x1055, 0x9130, PCI_ANY_ID, PCI_ANY_ID, }, { } /* terminate list */ }; @@ -327,6 +326,7 @@ static void __exit efar_exit(void) pci_unregister_driver(&efar_pci_driver); } + module_init(efar_init); module_exit(efar_exit); diff --git a/trunk/drivers/ata/pata_hpt366.c b/trunk/drivers/ata/pata_hpt366.c index 6d3e4c0f15fe..8c757438f350 100644 --- a/trunk/drivers/ata/pata_hpt366.c +++ b/trunk/drivers/ata/pata_hpt366.c @@ -444,14 +444,13 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static const struct pci_device_id hpt36x[] = { - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, - - { }, +static struct pci_device_id hpt36x[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { 0, }, }; static struct pci_driver hpt36x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt36x, .probe = hpt36x_init_one, .remove = ata_pci_remove_one diff --git a/trunk/drivers/ata/pata_hpt37x.c b/trunk/drivers/ata/pata_hpt37x.c index 7350443948c1..10318c0012ef 100644 --- a/trunk/drivers/ata/pata_hpt37x.c +++ b/trunk/drivers/ata/pata_hpt37x.c @@ -1219,18 +1219,17 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static const struct pci_device_id hpt37x[] = { - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT374), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), }, - - { }, +static struct pci_device_id hpt37x[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302), }, + { 0, }, }; static struct pci_driver hpt37x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt37x, .probe = hpt37x_init_one, .remove = ata_pci_remove_one @@ -1241,11 +1240,13 @@ static int __init hpt37x_init(void) return pci_register_driver(&hpt37x_pci_driver); } + static void __exit hpt37x_exit(void) { pci_unregister_driver(&hpt37x_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT37x/30x"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_hpt3x2n.c b/trunk/drivers/ata/pata_hpt3x2n.c index 58cfb2bc8098..5c5d4f6ab901 100644 --- a/trunk/drivers/ata/pata_hpt3x2n.c +++ b/trunk/drivers/ata/pata_hpt3x2n.c @@ -560,17 +560,16 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static const struct pci_device_id hpt3x2n[] = { - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), }, - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), }, - - { }, +static struct pci_device_id hpt3x2n[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372N), }, + { 0, }, }; static struct pci_driver hpt3x2n_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt3x2n, .probe = hpt3x2n_init_one, .remove = ata_pci_remove_one @@ -581,11 +580,13 @@ static int __init hpt3x2n_init(void) return pci_register_driver(&hpt3x2n_pci_driver); } + static void __exit hpt3x2n_exit(void) { pci_unregister_driver(&hpt3x2n_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the Highpoint HPT3x2n/30x"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_hpt3x3.c b/trunk/drivers/ata/pata_hpt3x3.c index 3334d72e251b..1f084ab1ccc6 100644 --- a/trunk/drivers/ata/pata_hpt3x3.c +++ b/trunk/drivers/ata/pata_hpt3x3.c @@ -192,14 +192,13 @@ static int hpt3x3_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static const struct pci_device_id hpt3x3[] = { - { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT343), }, - - { }, +static struct pci_device_id hpt3x3[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343), }, + { 0, }, }; static struct pci_driver hpt3x3_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = hpt3x3, .probe = hpt3x3_init_one, .remove = ata_pci_remove_one diff --git a/trunk/drivers/ata/pata_it821x.c b/trunk/drivers/ata/pata_it821x.c index 18ff3e59a89b..82a46ff40000 100644 --- a/trunk/drivers/ata/pata_it821x.c +++ b/trunk/drivers/ata/pata_it821x.c @@ -808,15 +808,14 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } -static const struct pci_device_id it821x[] = { - { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), }, - { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), }, - - { }, +static struct pci_device_id it821x[] = { + { PCI_DEVICE(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8211), }, + { PCI_DEVICE(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212), }, + { 0, }, }; static struct pci_driver it821x_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = it821x, .probe = it821x_init_one, .remove = ata_pci_remove_one @@ -827,11 +826,13 @@ static int __init it821x_init(void) return pci_register_driver(&it821x_pci_driver); } + static void __exit it821x_exit(void) { pci_unregister_driver(&it821x_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for the IT8211/IT8212 IDE RAID controller"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_jmicron.c b/trunk/drivers/ata/pata_jmicron.c index 52a2bdf3c38d..be3a866b111f 100644 --- a/trunk/drivers/ata/pata_jmicron.c +++ b/trunk/drivers/ata/pata_jmicron.c @@ -229,12 +229,11 @@ static int jmicron_init_one (struct pci_dev *pdev, const struct pci_device_id *i } static const struct pci_device_id jmicron_pci_tbl[] = { - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB361), 361}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB363), 363}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB365), 365}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB366), 366}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB368), 368}, - + { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361), 361}, + { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363), 363}, + { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365), 365}, + { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366), 366}, + { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368), 368}, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/pata_mpiix.c b/trunk/drivers/ata/pata_mpiix.c index 9dfe3e9abea3..3c65393c1f01 100644 --- a/trunk/drivers/ata/pata_mpiix.c +++ b/trunk/drivers/ata/pata_mpiix.c @@ -274,10 +274,11 @@ static void __devexit mpiix_remove_one(struct pci_dev *pdev) dev_set_drvdata(dev, NULL); } -static const struct pci_device_id mpiix[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), }, - { }, + +static const struct pci_device_id mpiix[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX), }, + { 0, }, }; static struct pci_driver mpiix_pci_driver = { @@ -292,11 +293,13 @@ static int __init mpiix_init(void) return pci_register_driver(&mpiix_pci_driver); } + static void __exit mpiix_exit(void) { pci_unregister_driver(&mpiix_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Intel MPIIX"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_netcell.c b/trunk/drivers/ata/pata_netcell.c index f5672de99c22..76eb9c90bee1 100644 --- a/trunk/drivers/ata/pata_netcell.c +++ b/trunk/drivers/ata/pata_netcell.c @@ -142,8 +142,7 @@ static int netcell_init_one (struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id netcell_pci_tbl[] = { - { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), }, - + { PCI_DEVICE(PCI_VENDOR_ID_NETCELL, PCI_DEVICE_ID_REVOLUTION), }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/pata_ns87410.c b/trunk/drivers/ata/pata_ns87410.c index 2a3dbeed89b4..2005a95f48f6 100644 --- a/trunk/drivers/ata/pata_ns87410.c +++ b/trunk/drivers/ata/pata_ns87410.c @@ -200,13 +200,12 @@ static int ns87410_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id ns87410[] = { - { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87410), }, - - { }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410), }, + { 0, }, }; static struct pci_driver ns87410_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = ns87410, .probe = ns87410_init_one, .remove = ata_pci_remove_one @@ -217,11 +216,13 @@ static int __init ns87410_init(void) return pci_register_driver(&ns87410_pci_driver); } + static void __exit ns87410_exit(void) { pci_unregister_driver(&ns87410_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Nat Semi 87410"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_oldpiix.c b/trunk/drivers/ata/pata_oldpiix.c index fc947dfecd73..31a285ca88dc 100644 --- a/trunk/drivers/ata/pata_oldpiix.c +++ b/trunk/drivers/ata/pata_oldpiix.c @@ -303,8 +303,7 @@ static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id oldpiix_pci_tbl[] = { - { PCI_VDEVICE(INTEL, 0x1230), }, - + { PCI_DEVICE(0x8086, 0x1230), }, { } /* terminate list */ }; @@ -325,6 +324,7 @@ static void __exit oldpiix_exit(void) pci_unregister_driver(&oldpiix_pci_driver); } + module_init(oldpiix_init); module_exit(oldpiix_exit); diff --git a/trunk/drivers/ata/pata_opti.c b/trunk/drivers/ata/pata_opti.c index a7320ba15575..57fe21f3a975 100644 --- a/trunk/drivers/ata/pata_opti.c +++ b/trunk/drivers/ata/pata_opti.c @@ -256,14 +256,13 @@ static int opti_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id opti[] = { - { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C621), 0 }, - { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C825), 1 }, - - { }, + { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { 0, }, }; static struct pci_driver opti_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = opti, .probe = opti_init_one, .remove = ata_pci_remove_one @@ -274,6 +273,7 @@ static int __init opti_init(void) return pci_register_driver(&opti_pci_driver); } + static void __exit opti_exit(void) { pci_unregister_driver(&opti_pci_driver); diff --git a/trunk/drivers/ata/pata_optidma.c b/trunk/drivers/ata/pata_optidma.c index c6906b4215de..7296a20cd107 100644 --- a/trunk/drivers/ata/pata_optidma.c +++ b/trunk/drivers/ata/pata_optidma.c @@ -512,13 +512,12 @@ static int optidma_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id optidma[] = { - { PCI_VDEVICE(OPTI, 0xD568), }, /* Opti 82C700 */ - - { }, + { PCI_DEVICE(0x1045, 0xD568), }, /* Opti 82C700 */ + { 0, }, }; static struct pci_driver optidma_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = optidma, .probe = optidma_init_one, .remove = ata_pci_remove_one @@ -529,11 +528,13 @@ static int __init optidma_init(void) return pci_register_driver(&optidma_pci_driver); } + static void __exit optidma_exit(void) { pci_unregister_driver(&optidma_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Opti Firestar/Firestar Plus"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_pcmcia.c b/trunk/drivers/ata/pata_pcmcia.c index e93ea2702c73..cb501e145a42 100644 --- a/trunk/drivers/ata/pata_pcmcia.c +++ b/trunk/drivers/ata/pata_pcmcia.c @@ -42,7 +42,7 @@ #define DRV_NAME "pata_pcmcia" -#define DRV_VERSION "0.2.11" +#define DRV_VERSION "0.2.9" /* * Private data structure to glue stuff together @@ -355,8 +355,6 @@ static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d), PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6), PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003), - PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443), - PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852), PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), diff --git a/trunk/drivers/ata/pata_pdc2027x.c b/trunk/drivers/ata/pata_pdc2027x.c index d894d9918b1d..bd4ed6734edc 100644 --- a/trunk/drivers/ata/pata_pdc2027x.c +++ b/trunk/drivers/ata/pata_pdc2027x.c @@ -108,14 +108,13 @@ static struct pdc2027x_udma_timing { }; static const struct pci_device_id pdc2027x_pci_tbl[] = { - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20268), PDC_UDMA_100 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20269), PDC_UDMA_133 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), PDC_UDMA_100 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), PDC_UDMA_133 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), PDC_UDMA_133 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), PDC_UDMA_133 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), PDC_UDMA_133 }, - + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_100 }, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20270, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_100 }, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, + { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PDC_UDMA_133 }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/pata_pdc202xx_old.c b/trunk/drivers/ata/pata_pdc202xx_old.c index 5ba9eb20a6c2..48f43432764e 100644 --- a/trunk/drivers/ata/pata_pdc202xx_old.c +++ b/trunk/drivers/ata/pata_pdc202xx_old.c @@ -385,18 +385,17 @@ static int pdc_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } -static const struct pci_device_id pdc[] = { - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2 }, - { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2 }, - - { }, +static struct pci_device_id pdc[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0}, + { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1}, + { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1}, + { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2}, + { PCI_DEVICE(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2}, + { 0, }, }; static struct pci_driver pdc_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = pdc, .probe = pdc_init_one, .remove = ata_pci_remove_one @@ -407,11 +406,13 @@ static int __init pdc_init(void) return pci_register_driver(&pdc_pci_driver); } + static void __exit pdc_exit(void) { pci_unregister_driver(&pdc_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Promise 2024x and 20262-20267"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_radisys.c b/trunk/drivers/ata/pata_radisys.c index 1af83d7694d5..c20bcf43ed6d 100644 --- a/trunk/drivers/ata/pata_radisys.c +++ b/trunk/drivers/ata/pata_radisys.c @@ -300,8 +300,7 @@ static int radisys_init_one (struct pci_dev *pdev, const struct pci_device_id *e } static const struct pci_device_id radisys_pci_tbl[] = { - { PCI_VDEVICE(RADISYS, 0x8201), }, - + { 0x1331, 0x8201, PCI_ANY_ID, PCI_ANY_ID, }, { } /* terminate list */ }; @@ -322,6 +321,7 @@ static void __exit radisys_exit(void) pci_unregister_driver(&radisys_pci_driver); } + module_init(radisys_init); module_exit(radisys_exit); diff --git a/trunk/drivers/ata/pata_rz1000.c b/trunk/drivers/ata/pata_rz1000.c index 4533b6357d99..eccc6fd45032 100644 --- a/trunk/drivers/ata/pata_rz1000.c +++ b/trunk/drivers/ata/pata_rz1000.c @@ -170,20 +170,20 @@ static int rz1000_init_one (struct pci_dev *pdev, const struct pci_device_id *en return -ENODEV; } -static const struct pci_device_id pata_rz1000[] = { - { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), }, - { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001), }, - - { }, +static struct pci_device_id pata_rz1000[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), }, + { PCI_DEVICE(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001), }, + { 0, }, }; static struct pci_driver rz1000_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = pata_rz1000, .probe = rz1000_init_one, .remove = ata_pci_remove_one }; + static int __init rz1000_init(void) { return pci_register_driver(&rz1000_pci_driver); diff --git a/trunk/drivers/ata/pata_sc1200.c b/trunk/drivers/ata/pata_sc1200.c index 067d9d223e35..107e6cd3dc0d 100644 --- a/trunk/drivers/ata/pata_sc1200.c +++ b/trunk/drivers/ata/pata_sc1200.c @@ -253,14 +253,13 @@ static int sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 1); } -static const struct pci_device_id sc1200[] = { - { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SCx200_IDE), }, - - { }, +static struct pci_device_id sc1200[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_IDE), }, + { 0, }, }; static struct pci_driver sc1200_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = sc1200, .probe = sc1200_init_one, .remove = ata_pci_remove_one @@ -271,11 +270,13 @@ static int __init sc1200_init(void) return pci_register_driver(&sc1200_pci_driver); } + static void __exit sc1200_exit(void) { pci_unregister_driver(&sc1200_pci_driver); } + MODULE_AUTHOR("Alan Cox, Mark Lord"); MODULE_DESCRIPTION("low-level driver for the NS/AMD SC1200"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_serverworks.c b/trunk/drivers/ata/pata_serverworks.c index 5bbf76ec14a4..a5c8d7e121d1 100644 --- a/trunk/drivers/ata/pata_serverworks.c +++ b/trunk/drivers/ata/pata_serverworks.c @@ -553,14 +553,13 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id return ata_pci_init_one(pdev, port_info, ports); } -static const struct pci_device_id serverworks[] = { - { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, - { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, - { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, - { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, - { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, - - { }, +static struct pci_device_id serverworks[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 2}, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2}, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 2}, + { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 2}, + { 0, }, }; static struct pci_driver serverworks_pci_driver = { @@ -575,11 +574,13 @@ static int __init serverworks_init(void) return pci_register_driver(&serverworks_pci_driver); } + static void __exit serverworks_exit(void) { pci_unregister_driver(&serverworks_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Serverworks OSB4/CSB5/CSB6"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_sil680.c b/trunk/drivers/ata/pata_sil680.c index 4a2b72b4be8a..c8b2e26db70d 100644 --- a/trunk/drivers/ata/pata_sil680.c +++ b/trunk/drivers/ata/pata_sil680.c @@ -348,13 +348,12 @@ static int sil680_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct pci_device_id sil680[] = { - { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), }, - - { }, + { PCI_DEVICE(PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_680), }, + { 0, }, }; static struct pci_driver sil680_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = sil680, .probe = sil680_init_one, .remove = ata_pci_remove_one @@ -365,11 +364,13 @@ static int __init sil680_init(void) return pci_register_driver(&sil680_pci_driver); } + static void __exit sil680_exit(void) { pci_unregister_driver(&sil680_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for SI680 PATA"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_sis.c b/trunk/drivers/ata/pata_sis.c index b9ffafb4198c..17791e2785f9 100644 --- a/trunk/drivers/ata/pata_sis.c +++ b/trunk/drivers/ata/pata_sis.c @@ -988,9 +988,8 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } static const struct pci_device_id sis_pci_tbl[] = { - { PCI_VDEVICE(SI, 0x5513), }, /* SiS 5513 */ - { PCI_VDEVICE(SI, 0x5518), }, /* SiS 5518 */ - + { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x5513), }, /* SiS 5513 */ + { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x5518), }, /* SiS 5518 */ { } }; @@ -1011,6 +1010,7 @@ static void __exit sis_exit(void) pci_unregister_driver(&sis_pci_driver); } + module_init(sis_init); module_exit(sis_exit); diff --git a/trunk/drivers/ata/pata_sl82c105.c b/trunk/drivers/ata/pata_sl82c105.c index 08a6dc88676f..5b762acc5687 100644 --- a/trunk/drivers/ata/pata_sl82c105.c +++ b/trunk/drivers/ata/pata_sl82c105.c @@ -351,10 +351,9 @@ static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id return ata_pci_init_one(dev, port_info, 1); /* For now */ } -static const struct pci_device_id sl82c105[] = { - { PCI_VDEVICE(WINBOND, PCI_DEVICE_ID_WINBOND_82C105), }, - - { }, +static struct pci_device_id sl82c105[] = { + { PCI_DEVICE(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105), }, + { 0, }, }; static struct pci_driver sl82c105_pci_driver = { @@ -369,11 +368,13 @@ static int __init sl82c105_init(void) return pci_register_driver(&sl82c105_pci_driver); } + static void __exit sl82c105_exit(void) { pci_unregister_driver(&sl82c105_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Sl82c105"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_triflex.c b/trunk/drivers/ata/pata_triflex.c index 9640f80e8b0d..a954ed93a40c 100644 --- a/trunk/drivers/ata/pata_triflex.c +++ b/trunk/drivers/ata/pata_triflex.c @@ -248,13 +248,13 @@ static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) } static const struct pci_device_id triflex[] = { - { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE), }, - - { }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, }, }; static struct pci_driver triflex_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = triflex, .probe = triflex_init_one, .remove = ata_pci_remove_one @@ -265,11 +265,13 @@ static int __init triflex_init(void) return pci_register_driver(&triflex_pci_driver); } + static void __exit triflex_exit(void) { pci_unregister_driver(&triflex_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for Compaq Triflex"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pata_via.c b/trunk/drivers/ata/pata_via.c index 1e7be9eee9c3..7b5dd2343b9a 100644 --- a/trunk/drivers/ata/pata_via.c +++ b/trunk/drivers/ata/pata_via.c @@ -529,16 +529,15 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } static const struct pci_device_id via[] = { - { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1), }, - { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C586_1), }, - { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410), }, - { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), }, - - { }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1), }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1), }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_6410), }, + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), }, + { 0, }, }; static struct pci_driver via_pci_driver = { - .name = DRV_NAME, + .name = DRV_NAME, .id_table = via, .probe = via_init_one, .remove = ata_pci_remove_one @@ -549,11 +548,13 @@ static int __init via_init(void) return pci_register_driver(&via_pci_driver); } + static void __exit via_exit(void) { pci_unregister_driver(&via_pci_driver); } + MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for VIA PATA"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/pdc_adma.c b/trunk/drivers/ata/pdc_adma.c index 81f3d219e70e..0e23ecb77bc2 100644 --- a/trunk/drivers/ata/pdc_adma.c +++ b/trunk/drivers/ata/pdc_adma.c @@ -192,7 +192,8 @@ static struct ata_port_info adma_port_info[] = { }; static const struct pci_device_id adma_ata_pci_tbl[] = { - { PCI_VDEVICE(PDC, 0x1841), board_1841_idx }, + { PCI_VENDOR_ID_PDC, 0x1841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_1841_idx }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/sata_mv.c b/trunk/drivers/ata/sata_mv.c index e6aa1a86d5cf..c01496df4a99 100644 --- a/trunk/drivers/ata/sata_mv.c +++ b/trunk/drivers/ata/sata_mv.c @@ -533,20 +533,19 @@ static const struct ata_port_info mv_port_info[] = { }; static const struct pci_device_id mv_pci_tbl[] = { - { PCI_VDEVICE(MARVELL, 0x5040), chip_504x }, - { PCI_VDEVICE(MARVELL, 0x5041), chip_504x }, - { PCI_VDEVICE(MARVELL, 0x5080), chip_5080 }, - { PCI_VDEVICE(MARVELL, 0x5081), chip_508x }, - - { PCI_VDEVICE(MARVELL, 0x6040), chip_604x }, - { PCI_VDEVICE(MARVELL, 0x6041), chip_604x }, - { PCI_VDEVICE(MARVELL, 0x6042), chip_6042 }, - { PCI_VDEVICE(MARVELL, 0x6080), chip_608x }, - { PCI_VDEVICE(MARVELL, 0x6081), chip_608x }, - - { PCI_VDEVICE(ADAPTEC2, 0x0241), chip_604x }, - - { } /* terminate list */ + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_5080}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x}, + + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6042), 0, 0, chip_6042}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x}, + + {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x0241), 0, 0, chip_604x}, + {} /* terminate list */ }; static struct pci_driver mv_pci_driver = { diff --git a/trunk/drivers/ata/sata_nv.c b/trunk/drivers/ata/sata_nv.c index d09d20a17790..8cd730fe5dd3 100644 --- a/trunk/drivers/ata/sata_nv.c +++ b/trunk/drivers/ata/sata_nv.c @@ -106,32 +106,45 @@ enum nv_host_type }; static const struct pci_device_id nv_pci_tbl[] = { - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), NFORCE2 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), NFORCE3 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), NFORCE3 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA), CK804 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2), CK804 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA), CK804 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2), CK804 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), GENERIC }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), GENERIC }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), GENERIC }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), GENERIC }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), GENERIC }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), GENERIC }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), GENERIC }, - { PCI_VDEVICE(NVIDIA, 0x045c), GENERIC }, - { PCI_VDEVICE(NVIDIA, 0x045d), GENERIC }, - { PCI_VDEVICE(NVIDIA, 0x045e), GENERIC }, - { PCI_VDEVICE(NVIDIA, 0x045f), GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, 0x045c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, 0x045d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, 0x045e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, 0x045f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC }, - - { } /* terminate list */ + { 0, } /* terminate list */ }; static struct pci_driver nv_pci_driver = { diff --git a/trunk/drivers/ata/sata_promise.c b/trunk/drivers/ata/sata_promise.c index 15c9437710fc..d627812ea73d 100644 --- a/trunk/drivers/ata/sata_promise.c +++ b/trunk/drivers/ata/sata_promise.c @@ -234,31 +234,48 @@ static const struct ata_port_info pdc_port_info[] = { }; static const struct pci_device_id pdc_ata_pci_tbl[] = { - { PCI_VDEVICE(PROMISE, 0x3371), board_2037x }, - { PCI_VDEVICE(PROMISE, 0x3570), board_2037x }, - { PCI_VDEVICE(PROMISE, 0x3571), board_2037x }, - { PCI_VDEVICE(PROMISE, 0x3373), board_2037x }, - { PCI_VDEVICE(PROMISE, 0x3375), board_2037x }, - { PCI_VDEVICE(PROMISE, 0x3376), board_2037x }, - { PCI_VDEVICE(PROMISE, 0x3574), board_2057x }, - { PCI_VDEVICE(PROMISE, 0x3d75), board_2057x }, - { PCI_VDEVICE(PROMISE, 0x3d73), board_2037x }, - - { PCI_VDEVICE(PROMISE, 0x3318), board_20319 }, - { PCI_VDEVICE(PROMISE, 0x3319), board_20319 }, - { PCI_VDEVICE(PROMISE, 0x3515), board_20319 }, - { PCI_VDEVICE(PROMISE, 0x3519), board_20319 }, - { PCI_VDEVICE(PROMISE, 0x3d17), board_20319 }, - { PCI_VDEVICE(PROMISE, 0x3d18), board_40518 }, - - { PCI_VDEVICE(PROMISE, 0x6629), board_20619 }, + { PCI_VENDOR_ID_PROMISE, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3571, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3373, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3375, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3376, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3574, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2057x }, + { PCI_VENDOR_ID_PROMISE, 0x3d75, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2057x }, + { PCI_VENDOR_ID_PROMISE, 0x3d73, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + + { PCI_VENDOR_ID_PROMISE, 0x3318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3d17, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3d18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_40518 }, + + { PCI_VENDOR_ID_PROMISE, 0x6629, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20619 }, /* TODO: remove all associated board_20771 code, as it completely * duplicates board_2037x code, unless reason for separation can be * divined. */ #if 0 - { PCI_VDEVICE(PROMISE, 0x3570), board_20771 }, + { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20771 }, #endif { } /* terminate list */ diff --git a/trunk/drivers/ata/sata_qstor.c b/trunk/drivers/ata/sata_qstor.c index 7f6cc3c07de5..fa29dfe2a7b5 100644 --- a/trunk/drivers/ata/sata_qstor.c +++ b/trunk/drivers/ata/sata_qstor.c @@ -185,7 +185,8 @@ static const struct ata_port_info qs_port_info[] = { }; static const struct pci_device_id qs_ata_pci_tbl[] = { - { PCI_VDEVICE(PDC, 0x2068), board_2068_idx }, + { PCI_VENDOR_ID_PDC, 0x2068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2068_idx }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/sata_sil.c b/trunk/drivers/ata/sata_sil.c index 3d9fa1cc834d..c63dbabc0cd9 100644 --- a/trunk/drivers/ata/sata_sil.c +++ b/trunk/drivers/ata/sata_sil.c @@ -123,14 +123,13 @@ static void sil_thaw(struct ata_port *ap); static const struct pci_device_id sil_pci_tbl[] = { - { PCI_VDEVICE(CMD, 0x3112), sil_3112 }, - { PCI_VDEVICE(CMD, 0x0240), sil_3112 }, - { PCI_VDEVICE(CMD, 0x3512), sil_3512 }, - { PCI_VDEVICE(CMD, 0x3114), sil_3114 }, - { PCI_VDEVICE(ATI, 0x436e), sil_3112 }, - { PCI_VDEVICE(ATI, 0x4379), sil_3112_no_sata_irq }, - { PCI_VDEVICE(ATI, 0x437a), sil_3112_no_sata_irq }, - + { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3512 }, + { 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 }, + { 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq }, + { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/sata_sil24.c b/trunk/drivers/ata/sata_sil24.c index a951f40c2f21..39cb07baebae 100644 --- a/trunk/drivers/ata/sata_sil24.c +++ b/trunk/drivers/ata/sata_sil24.c @@ -344,12 +344,11 @@ static int sil24_pci_device_resume(struct pci_dev *pdev); #endif static const struct pci_device_id sil24_pci_tbl[] = { - { PCI_VDEVICE(CMD, 0x3124), BID_SIL3124 }, - { PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 }, - { PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 }, - { PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 }, - { PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 }, - + { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, + { 0x8086, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, + { 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 }, + { 0x1095, 0x3131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, + { 0x1095, 0x3531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/sata_sis.c b/trunk/drivers/ata/sata_sis.c index 0738f52463a9..18d49fff8dc4 100644 --- a/trunk/drivers/ata/sata_sis.c +++ b/trunk/drivers/ata/sata_sis.c @@ -67,13 +67,13 @@ static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg); static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static const struct pci_device_id sis_pci_tbl[] = { - { PCI_VDEVICE(SI, 0x180), sis_180 }, - { PCI_VDEVICE(SI, 0x181), sis_180 }, - { PCI_VDEVICE(SI, 0x182), sis_180 }, - + { PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, + { PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, + { PCI_VENDOR_ID_SI, 0x182, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, { } /* terminate list */ }; + static struct pci_driver sis_pci_driver = { .name = DRV_NAME, .id_table = sis_pci_tbl, diff --git a/trunk/drivers/ata/sata_svw.c b/trunk/drivers/ata/sata_svw.c index 84025a2fd5be..d6d6658d8328 100644 --- a/trunk/drivers/ata/sata_svw.c +++ b/trunk/drivers/ata/sata_svw.c @@ -469,15 +469,15 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e * controller * */ static const struct pci_device_id k2_sata_pci_tbl[] = { - { PCI_VDEVICE(SERVERWORKS, 0x0240), 4 }, - { PCI_VDEVICE(SERVERWORKS, 0x0241), 4 }, - { PCI_VDEVICE(SERVERWORKS, 0x0242), 8 }, - { PCI_VDEVICE(SERVERWORKS, 0x024a), 4 }, - { PCI_VDEVICE(SERVERWORKS, 0x024b), 4 }, - + { 0x1166, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { 0x1166, 0x0241, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { 0x1166, 0x0242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { 0x1166, 0x024a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { 0x1166, 0x024b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, { } }; + static struct pci_driver k2_sata_pci_driver = { .name = DRV_NAME, .id_table = k2_sata_pci_tbl, @@ -485,16 +485,19 @@ static struct pci_driver k2_sata_pci_driver = { .remove = ata_pci_remove_one, }; + static int __init k2_sata_init(void) { return pci_register_driver(&k2_sata_pci_driver); } + static void __exit k2_sata_exit(void) { pci_unregister_driver(&k2_sata_pci_driver); } + MODULE_AUTHOR("Benjamin Herrenschmidt"); MODULE_DESCRIPTION("low-level driver for K2 SATA controller"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/ata/sata_sx4.c b/trunk/drivers/ata/sata_sx4.c index 8c74f2ff4344..091867e10ea3 100644 --- a/trunk/drivers/ata/sata_sx4.c +++ b/trunk/drivers/ata/sata_sx4.c @@ -230,11 +230,12 @@ static const struct ata_port_info pdc_port_info[] = { }; static const struct pci_device_id pdc_sata_pci_tbl[] = { - { PCI_VDEVICE(PROMISE, 0x6622), board_20621 }, - + { PCI_VENDOR_ID_PROMISE, 0x6622, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20621 }, { } /* terminate list */ }; + static struct pci_driver pdc_sata_pci_driver = { .name = DRV_NAME, .id_table = pdc_sata_pci_tbl, diff --git a/trunk/drivers/ata/sata_uli.c b/trunk/drivers/ata/sata_uli.c index 5c603ca3a50a..dd76f37be182 100644 --- a/trunk/drivers/ata/sata_uli.c +++ b/trunk/drivers/ata/sata_uli.c @@ -61,13 +61,13 @@ static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static const struct pci_device_id uli_pci_tbl[] = { - { PCI_VDEVICE(AL, 0x5289), uli_5289 }, - { PCI_VDEVICE(AL, 0x5287), uli_5287 }, - { PCI_VDEVICE(AL, 0x5281), uli_5281 }, - + { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 }, + { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 }, + { PCI_VENDOR_ID_AL, 0x5281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5281 }, { } /* terminate list */ }; + static struct pci_driver uli_pci_driver = { .name = DRV_NAME, .id_table = uli_pci_tbl, diff --git a/trunk/drivers/ata/sata_via.c b/trunk/drivers/ata/sata_via.c index f4455a1efe2d..a72a2389a11c 100644 --- a/trunk/drivers/ata/sata_via.c +++ b/trunk/drivers/ata/sata_via.c @@ -77,9 +77,9 @@ static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void vt6420_error_handler(struct ata_port *ap); static const struct pci_device_id svia_pci_tbl[] = { - { PCI_VDEVICE(VIA, 0x0591), vt6420 }, - { PCI_VDEVICE(VIA, 0x3149), vt6420 }, - { PCI_VDEVICE(VIA, 0x3249), vt6421 }, + { 0x1106, 0x0591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, + { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, + { 0x1106, 0x3249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6421 }, { } /* terminate list */ }; diff --git a/trunk/drivers/ata/sata_vsc.c b/trunk/drivers/ata/sata_vsc.c index 273d88fcf980..d0d92f33de54 100644 --- a/trunk/drivers/ata/sata_vsc.c +++ b/trunk/drivers/ata/sata_vsc.c @@ -442,15 +442,16 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d return rc; } + static const struct pci_device_id vsc_sata_pci_tbl[] = { { PCI_VENDOR_ID_VITESSE, 0x7174, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, { PCI_VENDOR_ID_INTEL, 0x3200, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, - { } /* terminate list */ }; + static struct pci_driver vsc_sata_pci_driver = { .name = DRV_NAME, .id_table = vsc_sata_pci_tbl, @@ -458,16 +459,19 @@ static struct pci_driver vsc_sata_pci_driver = { .remove = ata_pci_remove_one, }; + static int __init vsc_sata_init(void) { return pci_register_driver(&vsc_sata_pci_driver); } + static void __exit vsc_sata_exit(void) { pci_unregister_driver(&vsc_sata_pci_driver); } + MODULE_AUTHOR("Jeremy Higdon"); MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/atm/adummy.c b/trunk/drivers/atm/adummy.c index ac2c10822be0..6cc93de0b71d 100644 --- a/trunk/drivers/atm/adummy.c +++ b/trunk/drivers/atm/adummy.c @@ -113,13 +113,15 @@ static int __init adummy_init(void) printk(KERN_ERR "adummy: version %s\n", DRV_VERSION); - adummy_dev = kzalloc(sizeof(struct adummy_dev), + adummy_dev = (struct adummy_dev *) kmalloc(sizeof(struct adummy_dev), GFP_KERNEL); if (!adummy_dev) { - printk(KERN_ERR DEV_LABEL ": kzalloc() failed\n"); + printk(KERN_ERR DEV_LABEL ": kmalloc() failed\n"); err = -ENOMEM; goto out; } + memset(adummy_dev, 0, sizeof(struct adummy_dev)); + atm_dev = atm_dev_register(DEV_LABEL, &adummy_ops, -1, NULL); if (!atm_dev) { printk(KERN_ERR DEV_LABEL ": atm_dev_register() failed\n"); diff --git a/trunk/drivers/atm/ambassador.c b/trunk/drivers/atm/ambassador.c index da599e6e9d34..4521a249dd56 100644 --- a/trunk/drivers/atm/ambassador.c +++ b/trunk/drivers/atm/ambassador.c @@ -915,8 +915,8 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id, /********** make rate (not quite as much fun as Horizon) **********/ -static int make_rate (unsigned int rate, rounding r, - u16 * bits, unsigned int * actual) { +static unsigned int make_rate (unsigned int rate, rounding r, + u16 * bits, unsigned int * actual) { unsigned char exp = -1; // hush gcc unsigned int man = -1; // hush gcc diff --git a/trunk/drivers/atm/firestream.c b/trunk/drivers/atm/firestream.c index 5f25e5efefcd..38fc054bd671 100644 --- a/trunk/drivers/atm/firestream.c +++ b/trunk/drivers/atm/firestream.c @@ -1784,7 +1784,7 @@ static int __devinit fs_init (struct fs_dev *dev) write_fs (dev, RAM, (1 << (28 - FS155_VPI_BITS - FS155_VCI_BITS)) - 1); dev->nchannels = FS155_NR_CHANNELS; } - dev->atm_vccs = kcalloc (dev->nchannels, sizeof (struct atm_vcc *), + dev->atm_vccs = kmalloc (dev->nchannels * sizeof (struct atm_vcc *), GFP_KERNEL); fs_dprintk (FS_DEBUG_ALLOC, "Alloc atmvccs: %p(%Zd)\n", dev->atm_vccs, dev->nchannels * sizeof (struct atm_vcc *)); @@ -1794,8 +1794,9 @@ static int __devinit fs_init (struct fs_dev *dev) /* XXX Clean up..... */ return 1; } + memset (dev->atm_vccs, 0, dev->nchannels * sizeof (struct atm_vcc *)); - dev->tx_inuse = kzalloc (dev->nchannels / 8 /* bits/byte */ , GFP_KERNEL); + dev->tx_inuse = kmalloc (dev->nchannels / 8 /* bits/byte */ , GFP_KERNEL); fs_dprintk (FS_DEBUG_ALLOC, "Alloc tx_inuse: %p(%d)\n", dev->atm_vccs, dev->nchannels / 8); @@ -1804,6 +1805,8 @@ static int __devinit fs_init (struct fs_dev *dev) /* XXX Clean up..... */ return 1; } + memset (dev->tx_inuse, 0, dev->nchannels / 8); + /* -- RAS1 : FS155 and 50 differ. Default (0) should be OK for both */ /* -- RAS2 : FS50 only: Default is OK. */ @@ -1890,11 +1893,14 @@ static int __devinit firestream_init_one (struct pci_dev *pci_dev, if (pci_enable_device(pci_dev)) goto err_out; - fs_dev = kzalloc (sizeof (struct fs_dev), GFP_KERNEL); + fs_dev = kmalloc (sizeof (struct fs_dev), GFP_KERNEL); fs_dprintk (FS_DEBUG_ALLOC, "Alloc fs-dev: %p(%Zd)\n", fs_dev, sizeof (struct fs_dev)); if (!fs_dev) goto err_out; + + memset (fs_dev, 0, sizeof (struct fs_dev)); + atm_dev = atm_dev_register("fs", &ops, -1, NULL); if (!atm_dev) goto err_out_free_fs_dev; diff --git a/trunk/drivers/atm/he.c b/trunk/drivers/atm/he.c index b22a9142b240..f2511b42dba2 100644 --- a/trunk/drivers/atm/he.c +++ b/trunk/drivers/atm/he.c @@ -383,12 +383,14 @@ he_init_one(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent) } pci_set_drvdata(pci_dev, atm_dev); - he_dev = kzalloc(sizeof(struct he_dev), + he_dev = (struct he_dev *) kmalloc(sizeof(struct he_dev), GFP_KERNEL); if (!he_dev) { err = -ENOMEM; goto init_one_failure; } + memset(he_dev, 0, sizeof(struct he_dev)); + he_dev->pci_dev = pci_dev; he_dev->atm_dev = atm_dev; he_dev->atm_dev->dev_data = he_dev; diff --git a/trunk/drivers/atm/horizon.c b/trunk/drivers/atm/horizon.c index 209dba1c70da..d1113e845f95 100644 --- a/trunk/drivers/atm/horizon.c +++ b/trunk/drivers/atm/horizon.c @@ -2719,7 +2719,7 @@ static int __devinit hrz_probe(struct pci_dev *pci_dev, const struct pci_device_ goto out_disable; } - dev = kzalloc(sizeof(hrz_dev), GFP_KERNEL); + dev = kmalloc(sizeof(hrz_dev), GFP_KERNEL); if (!dev) { // perhaps we should be nice: deregister all adapters and abort? PRINTD(DBG_ERR, "out of memory"); @@ -2727,6 +2727,8 @@ static int __devinit hrz_probe(struct pci_dev *pci_dev, const struct pci_device_ goto out_release; } + memset(dev, 0, sizeof(hrz_dev)); + pci_set_drvdata(pci_dev, dev); // grab IRQ and install handler - move this someplace more sensible diff --git a/trunk/drivers/atm/idt77252.c b/trunk/drivers/atm/idt77252.c index 7487f0ad68e9..b0369bb20f08 100644 --- a/trunk/drivers/atm/idt77252.c +++ b/trunk/drivers/atm/idt77252.c @@ -642,9 +642,11 @@ alloc_scq(struct idt77252_dev *card, int class) { struct scq_info *scq; - scq = kzalloc(sizeof(struct scq_info), GFP_KERNEL); + scq = (struct scq_info *) kmalloc(sizeof(struct scq_info), GFP_KERNEL); if (!scq) return NULL; + memset(scq, 0, sizeof(struct scq_info)); + scq->base = pci_alloc_consistent(card->pcidev, SCQ_SIZE, &scq->paddr); if (scq->base == NULL) { @@ -2140,9 +2142,11 @@ idt77252_init_est(struct vc_map *vc, int pcr) { struct rate_estimator *est; - est = kzalloc(sizeof(struct rate_estimator), GFP_KERNEL); + est = kmalloc(sizeof(struct rate_estimator), GFP_KERNEL); if (!est) return NULL; + memset(est, 0, sizeof(*est)); + est->maxcps = pcr < 0 ? -pcr : pcr; est->cps = est->maxcps; est->avcps = est->cps << 5; @@ -2447,12 +2451,14 @@ idt77252_open(struct atm_vcc *vcc) index = VPCI2VC(card, vpi, vci); if (!card->vcs[index]) { - card->vcs[index] = kzalloc(sizeof(struct vc_map), GFP_KERNEL); + card->vcs[index] = kmalloc(sizeof(struct vc_map), GFP_KERNEL); if (!card->vcs[index]) { printk("%s: can't alloc vc in open()\n", card->name); up(&card->mutex); return -ENOMEM; } + memset(card->vcs[index], 0, sizeof(struct vc_map)); + card->vcs[index]->card = card; card->vcs[index]->index = index; @@ -2920,11 +2926,13 @@ open_card_oam(struct idt77252_dev *card) for (vci = 3; vci < 5; vci++) { index = VPCI2VC(card, vpi, vci); - vc = kzalloc(sizeof(struct vc_map), GFP_KERNEL); + vc = kmalloc(sizeof(struct vc_map), GFP_KERNEL); if (!vc) { printk("%s: can't alloc vc\n", card->name); return -ENOMEM; } + memset(vc, 0, sizeof(struct vc_map)); + vc->index = index; card->vcs[index] = vc; @@ -2987,11 +2995,12 @@ open_card_ubr0(struct idt77252_dev *card) { struct vc_map *vc; - vc = kzalloc(sizeof(struct vc_map), GFP_KERNEL); + vc = kmalloc(sizeof(struct vc_map), GFP_KERNEL); if (!vc) { printk("%s: can't alloc vc\n", card->name); return -ENOMEM; } + memset(vc, 0, sizeof(struct vc_map)); card->vcs[0] = vc; vc->class = SCHED_UBR0; @@ -3686,12 +3695,14 @@ idt77252_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) goto err_out_disable_pdev; } - card = kzalloc(sizeof(struct idt77252_dev), GFP_KERNEL); + card = kmalloc(sizeof(struct idt77252_dev), GFP_KERNEL); if (!card) { printk("idt77252-%d: can't allocate private data\n", index); err = -ENOMEM; goto err_out_disable_pdev; } + memset(card, 0, sizeof(struct idt77252_dev)); + card->revision = revision; card->index = index; card->pcidev = pcidev; diff --git a/trunk/drivers/atm/lanai.c b/trunk/drivers/atm/lanai.c index b9568e10965a..fe60a59b7fc0 100644 --- a/trunk/drivers/atm/lanai.c +++ b/trunk/drivers/atm/lanai.c @@ -1482,10 +1482,16 @@ static inline void vcc_table_deallocate(const struct lanai_dev *lanai) static inline struct lanai_vcc *new_lanai_vcc(void) { struct lanai_vcc *lvcc; - lvcc = kzalloc(sizeof(*lvcc), GFP_KERNEL); + lvcc = (struct lanai_vcc *) kmalloc(sizeof(*lvcc), GFP_KERNEL); if (likely(lvcc != NULL)) { + lvcc->vbase = NULL; + lvcc->rx.atmvcc = lvcc->tx.atmvcc = NULL; + lvcc->nref = 0; + memset(&lvcc->stats, 0, sizeof lvcc->stats); + lvcc->rx.buf.start = lvcc->tx.buf.start = NULL; skb_queue_head_init(&lvcc->tx.backlog); #ifdef DEBUG + lvcc->tx.unqueue = NULL; lvcc->vci = -1; #endif } diff --git a/trunk/drivers/atm/zatm.c b/trunk/drivers/atm/zatm.c index 083c5d3f2e18..2c65e82f0d6b 100644 --- a/trunk/drivers/atm/zatm.c +++ b/trunk/drivers/atm/zatm.c @@ -603,8 +603,9 @@ static int start_rx(struct atm_dev *dev) DPRINTK("start_rx\n"); zatm_dev = ZATM_DEV(dev); size = sizeof(struct atm_vcc *)*zatm_dev->chans; - zatm_dev->rx_map = kzalloc(size,GFP_KERNEL); + zatm_dev->rx_map = (struct atm_vcc **) kmalloc(size,GFP_KERNEL); if (!zatm_dev->rx_map) return -ENOMEM; + memset(zatm_dev->rx_map,0,size); /* set VPI/VCI split (use all VCIs and give what's left to VPIs) */ zpokel(zatm_dev,(1 << dev->ci_range.vci_bits)-1,uPD98401_VRR); /* prepare free buffer pools */ @@ -800,7 +801,6 @@ static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr) i = m = 1; zatm_dev->ubr_ref_cnt++; zatm_dev->ubr = shaper; - *pcr = 0; } else { if (min) { @@ -951,8 +951,9 @@ static int open_tx_first(struct atm_vcc *vcc) skb_queue_head_init(&zatm_vcc->tx_queue); init_waitqueue_head(&zatm_vcc->tx_wait); /* initialize ring */ - zatm_vcc->ring = kzalloc(RING_SIZE,GFP_KERNEL); + zatm_vcc->ring = kmalloc(RING_SIZE,GFP_KERNEL); if (!zatm_vcc->ring) return -ENOMEM; + memset(zatm_vcc->ring,0,RING_SIZE); loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS; loop[0] = uPD98401_TXPD_V; loop[1] = loop[2] = 0; diff --git a/trunk/drivers/block/cciss.c b/trunk/drivers/block/cciss.c index 99f87efe0f58..36b88f6c5f82 100644 --- a/trunk/drivers/block/cciss.c +++ b/trunk/drivers/block/cciss.c @@ -20,7 +20,6 @@ * */ -#include /* CONFIG_PROC_FS */ #include #include #include diff --git a/trunk/drivers/block/cpqarray.c b/trunk/drivers/block/cpqarray.c index 4abc193314ee..ada68e65b5ff 100644 --- a/trunk/drivers/block/cpqarray.c +++ b/trunk/drivers/block/cpqarray.c @@ -19,7 +19,6 @@ * Questions/Comments/Bugfixes to iss_storagedev@hp.com * */ -#include /* CONFIG_PROC_FS */ #include #include #include diff --git a/trunk/drivers/block/pktcdvd.c b/trunk/drivers/block/pktcdvd.c index f2904f67af47..a6b2aa67c9b2 100644 --- a/trunk/drivers/block/pktcdvd.c +++ b/trunk/drivers/block/pktcdvd.c @@ -62,8 +62,6 @@ #include -#define DRIVER_NAME "pktcdvd" - #if PACKET_DEBUG #define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) #else @@ -82,7 +80,7 @@ static struct pktcdvd_device *pkt_devs[MAX_WRITERS]; static struct proc_dir_entry *pkt_proc; -static int pktdev_major; +static int pkt_major; static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ static mempool_t *psd_pool; @@ -91,7 +89,7 @@ static void pkt_bio_finished(struct pktcdvd_device *pd) { BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0); if (atomic_dec_and_test(&pd->cdrw.pending_bios)) { - VPRINTK(DRIVER_NAME": queue empty\n"); + VPRINTK("pktcdvd: queue empty\n"); atomic_set(&pd->iosched.attention, 1); wake_up(&pd->wqueue); } @@ -402,7 +400,7 @@ static void pkt_dump_sense(struct packet_command *cgc) int i; struct request_sense *sense = cgc->sense; - printk(DRIVER_NAME":"); + printk("pktcdvd:"); for (i = 0; i < CDROM_PACKET_SIZE; i++) printk(" %02x", cgc->cmd[i]); printk(" - "); @@ -530,7 +528,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) need_write_seek = 0; if (need_write_seek && reads_queued) { if (atomic_read(&pd->cdrw.pending_bios) > 0) { - VPRINTK(DRIVER_NAME": write, waiting\n"); + VPRINTK("pktcdvd: write, waiting\n"); break; } pkt_flush_cache(pd); @@ -539,7 +537,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd) } else { if (!reads_queued && writes_queued) { if (atomic_read(&pd->cdrw.pending_bios) > 0) { - VPRINTK(DRIVER_NAME": read, waiting\n"); + VPRINTK("pktcdvd: read, waiting\n"); break; } pd->iosched.writing = 1; @@ -602,7 +600,7 @@ static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q set_bit(PACKET_MERGE_SEGS, &pd->flags); return 0; } else { - printk(DRIVER_NAME": cdrom max_phys_segments too small\n"); + printk("pktcdvd: cdrom max_phys_segments too small\n"); return -EIO; } } @@ -1051,7 +1049,7 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt) for (f = 0; f < pkt->frames; f++) if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset)) BUG(); - VPRINTK(DRIVER_NAME": vcnt=%d\n", pkt->w_bio->bi_vcnt); + VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt); atomic_set(&pkt->io_wait, 1); pkt->w_bio->bi_rw = WRITE; @@ -1288,7 +1286,7 @@ static int kcdrwd(void *foobar) static void pkt_print_settings(struct pktcdvd_device *pd) { - printk(DRIVER_NAME": %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); + printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); printk("%u blocks, ", pd->settings.size >> 2); printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2'); } @@ -1473,7 +1471,7 @@ static int pkt_set_write_settings(struct pktcdvd_device *pd) /* * paranoia */ - printk(DRIVER_NAME": write mode wrong %d\n", wp->data_block_type); + printk("pktcdvd: write mode wrong %d\n", wp->data_block_type); return 1; } wp->packet_size = cpu_to_be32(pd->settings.size >> 2); @@ -1517,7 +1515,7 @@ static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti) if (ti->rt == 1 && ti->blank == 0) return 1; - printk(DRIVER_NAME": bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); + printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); return 0; } @@ -1535,7 +1533,7 @@ static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di) case 0x12: /* DVD-RAM */ return 1; default: - VPRINTK(DRIVER_NAME": Wrong disc profile (%x)\n", pd->mmc3_profile); + VPRINTK("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile); return 0; } @@ -1544,22 +1542,22 @@ static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di) * but i'm not sure, should we leave this to user apps? probably. */ if (di->disc_type == 0xff) { - printk(DRIVER_NAME": Unknown disc. No track?\n"); + printk("pktcdvd: Unknown disc. No track?\n"); return 0; } if (di->disc_type != 0x20 && di->disc_type != 0) { - printk(DRIVER_NAME": Wrong disc type (%x)\n", di->disc_type); + printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type); return 0; } if (di->erasable == 0) { - printk(DRIVER_NAME": Disc not erasable\n"); + printk("pktcdvd: Disc not erasable\n"); return 0; } if (di->border_status == PACKET_SESSION_RESERVED) { - printk(DRIVER_NAME": Can't write to last track (reserved)\n"); + printk("pktcdvd: Can't write to last track (reserved)\n"); return 0; } @@ -1595,12 +1593,12 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ if ((ret = pkt_get_track_info(pd, track, 1, &ti))) { - printk(DRIVER_NAME": failed get_track\n"); + printk("pktcdvd: failed get_track\n"); return ret; } if (!pkt_writable_track(pd, &ti)) { - printk(DRIVER_NAME": can't write to this track\n"); + printk("pktcdvd: can't write to this track\n"); return -EROFS; } @@ -1610,11 +1608,11 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) */ pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; if (pd->settings.size == 0) { - printk(DRIVER_NAME": detected zero packet size!\n"); + printk("pktcdvd: detected zero packet size!\n"); return -ENXIO; } if (pd->settings.size > PACKET_MAX_SECTORS) { - printk(DRIVER_NAME": packet size is too big\n"); + printk("pktcdvd: packet size is too big\n"); return -EROFS; } pd->settings.fp = ti.fp; @@ -1656,7 +1654,7 @@ static int pkt_probe_settings(struct pktcdvd_device *pd) pd->settings.block_mode = PACKET_BLOCK_MODE2; break; default: - printk(DRIVER_NAME": unknown data mode\n"); + printk("pktcdvd: unknown data mode\n"); return -EROFS; } return 0; @@ -1690,10 +1688,10 @@ static int pkt_write_caching(struct pktcdvd_device *pd, int set) cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff)); ret = pkt_mode_select(pd, &cgc); if (ret) { - printk(DRIVER_NAME": write caching control failed\n"); + printk("pktcdvd: write caching control failed\n"); pkt_dump_sense(&cgc); } else if (!ret && set) - printk(DRIVER_NAME": enabled write caching on %s\n", pd->name); + printk("pktcdvd: enabled write caching on %s\n", pd->name); return ret; } @@ -1807,11 +1805,11 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) } if (!buf[6] & 0x40) { - printk(DRIVER_NAME": Disc type is not CD-RW\n"); + printk("pktcdvd: Disc type is not CD-RW\n"); return 1; } if (!buf[6] & 0x4) { - printk(DRIVER_NAME": A1 values on media are not valid, maybe not CDRW?\n"); + printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n"); return 1; } @@ -1831,14 +1829,14 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed) *speed = us_clv_to_speed[sp]; break; default: - printk(DRIVER_NAME": Unknown disc sub-type %d\n",st); + printk("pktcdvd: Unknown disc sub-type %d\n",st); return 1; } if (*speed) { - printk(DRIVER_NAME": Max. media speed: %d\n",*speed); + printk("pktcdvd: Max. media speed: %d\n",*speed); return 0; } else { - printk(DRIVER_NAME": Unknown speed %d for sub-type %d\n",sp,st); + printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st); return 1; } } @@ -1849,7 +1847,7 @@ static int pkt_perform_opc(struct pktcdvd_device *pd) struct request_sense sense; int ret; - VPRINTK(DRIVER_NAME": Performing OPC\n"); + VPRINTK("pktcdvd: Performing OPC\n"); init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); cgc.sense = &sense; @@ -1867,12 +1865,12 @@ static int pkt_open_write(struct pktcdvd_device *pd) unsigned int write_speed, media_write_speed, read_speed; if ((ret = pkt_probe_settings(pd))) { - VPRINTK(DRIVER_NAME": %s failed probe\n", pd->name); + VPRINTK("pktcdvd: %s failed probe\n", pd->name); return ret; } if ((ret = pkt_set_write_settings(pd))) { - DPRINTK(DRIVER_NAME": %s failed saving write settings\n", pd->name); + DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name); return -EIO; } @@ -1884,26 +1882,26 @@ static int pkt_open_write(struct pktcdvd_device *pd) case 0x13: /* DVD-RW */ case 0x1a: /* DVD+RW */ case 0x12: /* DVD-RAM */ - DPRINTK(DRIVER_NAME": write speed %ukB/s\n", write_speed); + DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed); break; default: if ((ret = pkt_media_speed(pd, &media_write_speed))) media_write_speed = 16; write_speed = min(write_speed, media_write_speed * 177); - DPRINTK(DRIVER_NAME": write speed %ux\n", write_speed / 176); + DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176); break; } read_speed = write_speed; if ((ret = pkt_set_speed(pd, write_speed, read_speed))) { - DPRINTK(DRIVER_NAME": %s couldn't set write speed\n", pd->name); + DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name); return -EIO; } pd->write_speed = write_speed; pd->read_speed = read_speed; if ((ret = pkt_perform_opc(pd))) { - DPRINTK(DRIVER_NAME": %s Optimum Power Calibration failed\n", pd->name); + DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name); } return 0; @@ -1931,7 +1929,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write) goto out_putdev; if ((ret = pkt_get_last_written(pd, &lba))) { - printk(DRIVER_NAME": pkt_get_last_written failed\n"); + printk("pktcdvd: pkt_get_last_written failed\n"); goto out_unclaim; } @@ -1961,11 +1959,11 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write) if (write) { if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) { - printk(DRIVER_NAME": not enough memory for buffers\n"); + printk("pktcdvd: not enough memory for buffers\n"); ret = -ENOMEM; goto out_unclaim; } - printk(DRIVER_NAME": %lukB available on disc\n", lba << 1); + printk("pktcdvd: %lukB available on disc\n", lba << 1); } return 0; @@ -1985,7 +1983,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write) static void pkt_release_dev(struct pktcdvd_device *pd, int flush) { if (flush && pkt_flush_cache(pd)) - DPRINTK(DRIVER_NAME": %s not flushing cache\n", pd->name); + DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); pkt_lock_door(pd, 0); @@ -2008,7 +2006,7 @@ static int pkt_open(struct inode *inode, struct file *file) struct pktcdvd_device *pd = NULL; int ret; - VPRINTK(DRIVER_NAME": entering open\n"); + VPRINTK("pktcdvd: entering open\n"); mutex_lock(&ctl_mutex); pd = pkt_find_dev_from_minor(iminor(inode)); @@ -2042,7 +2040,7 @@ static int pkt_open(struct inode *inode, struct file *file) out_dec: pd->refcnt--; out: - VPRINTK(DRIVER_NAME": failed open (%d)\n", ret); + VPRINTK("pktcdvd: failed open (%d)\n", ret); mutex_unlock(&ctl_mutex); return ret; } @@ -2090,7 +2088,7 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) pd = q->queuedata; if (!pd) { - printk(DRIVER_NAME": %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); + printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b)); goto end_io; } @@ -2112,13 +2110,13 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio) } if (!test_bit(PACKET_WRITABLE, &pd->flags)) { - printk(DRIVER_NAME": WRITE for ro device %s (%llu)\n", + printk("pktcdvd: WRITE for ro device %s (%llu)\n", pd->name, (unsigned long long)bio->bi_sector); goto end_io; } if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) { - printk(DRIVER_NAME": wrong bio size\n"); + printk("pktcdvd: wrong bio size\n"); goto end_io; } @@ -2321,7 +2319,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) struct block_device *bdev; if (pd->pkt_dev == dev) { - printk(DRIVER_NAME": Recursive setup not allowed\n"); + printk("pktcdvd: Recursive setup not allowed\n"); return -EBUSY; } for (i = 0; i < MAX_WRITERS; i++) { @@ -2329,11 +2327,11 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) if (!pd2) continue; if (pd2->bdev->bd_dev == dev) { - printk(DRIVER_NAME": %s already setup\n", bdevname(pd2->bdev, b)); + printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b)); return -EBUSY; } if (pd2->pkt_dev == dev) { - printk(DRIVER_NAME": Can't chain pktcdvd devices\n"); + printk("pktcdvd: Can't chain pktcdvd devices\n"); return -EBUSY; } } @@ -2356,7 +2354,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) atomic_set(&pd->cdrw.pending_bios, 0); pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name); if (IS_ERR(pd->cdrw.thread)) { - printk(DRIVER_NAME": can't start kernel thread\n"); + printk("pktcdvd: can't start kernel thread\n"); ret = -ENOMEM; goto out_mem; } @@ -2366,7 +2364,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev) proc->data = pd; proc->proc_fops = &pkt_proc_fops; } - DPRINTK(DRIVER_NAME": writer %s mapped to %s\n", pd->name, bdevname(bdev, b)); + DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b)); return 0; out_mem: @@ -2403,7 +2401,7 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); default: - VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd); + VPRINTK("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); return -ENOTTY; } @@ -2448,7 +2446,7 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) if (!pkt_devs[idx]) break; if (idx == MAX_WRITERS) { - printk(DRIVER_NAME": max %d writers supported\n", MAX_WRITERS); + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); return -EBUSY; } @@ -2472,15 +2470,15 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd) spin_lock_init(&pd->lock); spin_lock_init(&pd->iosched.lock); - sprintf(pd->name, DRIVER_NAME"%d", idx); + sprintf(pd->name, "pktcdvd%d", idx); init_waitqueue_head(&pd->wqueue); pd->bio_queue = RB_ROOT; - disk->major = pktdev_major; + disk->major = pkt_major; disk->first_minor = idx; disk->fops = &pktcdvd_ops; disk->flags = GENHD_FL_REMOVABLE; - sprintf(disk->disk_name, DRIVER_NAME"%d", idx); + sprintf(disk->disk_name, "pktcdvd%d", idx); disk->private_data = pd; disk->queue = blk_alloc_queue(GFP_KERNEL); if (!disk->queue) @@ -2522,7 +2520,7 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) break; } if (idx == MAX_WRITERS) { - DPRINTK(DRIVER_NAME": dev not setup\n"); + DPRINTK("pktcdvd: dev not setup\n"); return -ENXIO; } @@ -2535,7 +2533,7 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd) blkdev_put(pd->bdev); remove_proc_entry(pd->name, pkt_proc); - DPRINTK(DRIVER_NAME": writer %s unmapped\n", pd->name); + DPRINTK("pktcdvd: writer %s unmapped\n", pd->name); del_gendisk(pd->disk); blk_cleanup_queue(pd->disk->queue); @@ -2612,7 +2610,7 @@ static struct file_operations pkt_ctl_fops = { static struct miscdevice pkt_misc = { .minor = MISC_DYNAMIC_MINOR, - .name = DRIVER_NAME, + .name = "pktcdvd", .fops = &pkt_ctl_fops }; @@ -2625,28 +2623,28 @@ static int __init pkt_init(void) if (!psd_pool) return -ENOMEM; - ret = register_blkdev(pktdev_major, DRIVER_NAME); + ret = register_blkdev(pkt_major, "pktcdvd"); if (ret < 0) { - printk(DRIVER_NAME": Unable to register block device\n"); + printk("pktcdvd: Unable to register block device\n"); goto out2; } - if (!pktdev_major) - pktdev_major = ret; + if (!pkt_major) + pkt_major = ret; ret = misc_register(&pkt_misc); if (ret) { - printk(DRIVER_NAME": Unable to register misc device\n"); + printk("pktcdvd: Unable to register misc device\n"); goto out; } mutex_init(&ctl_mutex); - pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver); + pkt_proc = proc_mkdir("pktcdvd", proc_root_driver); return 0; out: - unregister_blkdev(pktdev_major, DRIVER_NAME); + unregister_blkdev(pkt_major, "pktcdvd"); out2: mempool_destroy(psd_pool); return ret; @@ -2654,9 +2652,9 @@ static int __init pkt_init(void) static void __exit pkt_exit(void) { - remove_proc_entry(DRIVER_NAME, proc_root_driver); + remove_proc_entry("pktcdvd", proc_root_driver); misc_deregister(&pkt_misc); - unregister_blkdev(pktdev_major, DRIVER_NAME); + unregister_blkdev(pkt_major, "pktcdvd"); mempool_destroy(psd_pool); } diff --git a/trunk/drivers/block/swim3.c b/trunk/drivers/block/swim3.c index fdc8f892eb86..f2305ee792a1 100644 --- a/trunk/drivers/block/swim3.c +++ b/trunk/drivers/block/swim3.c @@ -636,7 +636,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs) intr = in_8(&sw->intr); err = (intr & ERROR_INTR)? in_8(&sw->error): 0; if ((intr & ERROR_INTR) && fs->state != do_transfer) - printk(KERN_ERR "swim3_interrupt, state=%d, dir=%x, intr=%x, err=%x\n", + printk(KERN_ERR "swim3_interrupt, state=%d, dir=%lx, intr=%x, err=%x\n", fs->state, rq_data_dir(fd_req), intr, err); switch (fs->state) { case locating: @@ -742,7 +742,7 @@ static irqreturn_t swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs) if ((stat & ACTIVE) == 0 || resid != 0) { /* musta been an error */ printk(KERN_ERR "swim3: fd dma: stat=%x resid=%d\n", stat, resid); - printk(KERN_ERR " state=%d, dir=%x, intr=%x, err=%x\n", + printk(KERN_ERR " state=%d, dir=%lx, intr=%x, err=%x\n", fs->state, rq_data_dir(fd_req), intr, err); end_request(fd_req, 0); fs->state = idle; diff --git a/trunk/drivers/char/agp/Kconfig b/trunk/drivers/char/agp/Kconfig index c603bf291580..22f8cf218cc6 100644 --- a/trunk/drivers/char/agp/Kconfig +++ b/trunk/drivers/char/agp/Kconfig @@ -1,6 +1,6 @@ config AGP tristate "/dev/agpgart (AGP Support)" - depends on ALPHA || IA64 || PARISC || PPC || X86 + depends on ALPHA || IA64 || PPC || X86 depends on PCI ---help--- AGP (Accelerated Graphics Port) is a bus system mainly used to @@ -122,14 +122,6 @@ config AGP_HP_ZX1 This option gives you AGP GART support for the HP ZX1 chipset for IA64 processors. -config AGP_PARISC - tristate "HP Quicksilver AGP support" - depends on AGP && PARISC && 64BIT - help - This option gives you AGP GART support for the HP Quicksilver - AGP bus adapter on HP PA-RISC machines (Ok, just on the C8000 - workstation...) - config AGP_ALPHA_CORE tristate "Alpha AGP support" depends on AGP && (ALPHA_GENERIC || ALPHA_TITAN || ALPHA_MARVEL) diff --git a/trunk/drivers/char/agp/Makefile b/trunk/drivers/char/agp/Makefile index 3e581603d0a8..d33a22f2fa0b 100644 --- a/trunk/drivers/char/agp/Makefile +++ b/trunk/drivers/char/agp/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_AGP_AMD64) += amd64-agp.o obj-$(CONFIG_AGP_ALPHA_CORE) += alpha-agp.o obj-$(CONFIG_AGP_EFFICEON) += efficeon-agp.o obj-$(CONFIG_AGP_HP_ZX1) += hp-agp.o -obj-$(CONFIG_AGP_PARISC) += parisc-agp.o obj-$(CONFIG_AGP_I460) += i460-agp.o obj-$(CONFIG_AGP_INTEL) += intel-agp.o obj-$(CONFIG_AGP_NVIDIA) += nvidia-agp.o diff --git a/trunk/drivers/char/agp/parisc-agp.c b/trunk/drivers/char/agp/parisc-agp.c deleted file mode 100644 index 17c50b0f83f0..000000000000 --- a/trunk/drivers/char/agp/parisc-agp.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * HP Quicksilver AGP GART routines - * - * Copyright (c) 2006, Kyle McMartin - * - * Based on drivers/char/agpgart/hp-agp.c which is - * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P. - * Bjorn Helgaas - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "agp.h" - -#define DRVNAME "quicksilver" -#define DRVPFX DRVNAME ": " - -#ifndef log2 -#define log2(x) ffz(~(x)) -#endif - -#define AGP8X_MODE_BIT 3 -#define AGP8X_MODE (1 << AGP8X_MODE_BIT) - -static struct _parisc_agp_info { - void __iomem *ioc_regs; - void __iomem *lba_regs; - - int lba_cap_offset; - - u64 *gatt; - u64 gatt_entries; - - u64 gart_base; - u64 gart_size; - - int io_page_size; - int io_pages_per_kpage; -} parisc_agp_info; - -static struct gatt_mask parisc_agp_masks[] = -{ - { - .mask = SBA_PDIR_VALID_BIT, - .type = 0 - } -}; - -static struct aper_size_info_fixed parisc_agp_sizes[] = -{ - {0, 0, 0}, /* filled in by parisc_agp_fetch_size() */ -}; - -static int -parisc_agp_fetch_size(void) -{ - int size; - - size = parisc_agp_info.gart_size / MB(1); - parisc_agp_sizes[0].size = size; - agp_bridge->current_size = (void *) &parisc_agp_sizes[0]; - - return size; -} - -static int -parisc_agp_configure(void) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - - agp_bridge->gart_bus_addr = info->gart_base; - agp_bridge->capndx = info->lba_cap_offset; - agp_bridge->mode = readl(info->lba_regs+info->lba_cap_offset+PCI_AGP_STATUS); - - return 0; -} - -static void -parisc_agp_tlbflush(struct agp_memory *mem) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - - writeq(info->gart_base | log2(info->gart_size), info->ioc_regs+IOC_PCOM); - readq(info->ioc_regs+IOC_PCOM); /* flush */ -} - -static int -parisc_agp_create_gatt_table(struct agp_bridge_data *bridge) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - int i; - - for (i = 0; i < info->gatt_entries; i++) { - info->gatt[i] = (unsigned long)agp_bridge->scratch_page; - } - - return 0; -} - -static int -parisc_agp_free_gatt_table(struct agp_bridge_data *bridge) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - - info->gatt[0] = SBA_AGPGART_COOKIE; - - return 0; -} - -static int -parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - int i, k; - off_t j, io_pg_start; - int io_pg_count; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - io_pg_start = info->io_pages_per_kpage * pg_start; - io_pg_count = info->io_pages_per_kpage * mem->page_count; - if ((io_pg_start + io_pg_count) > info->gatt_entries) { - return -EINVAL; - } - - j = io_pg_start; - while (j < (io_pg_start + io_pg_count)) { - if (info->gatt[j]) - return -EBUSY; - j++; - } - - if (mem->is_flushed == FALSE) { - global_cache_flush(); - mem->is_flushed = TRUE; - } - - for (i = 0, j = io_pg_start; i < mem->page_count; i++) { - unsigned long paddr; - - paddr = mem->memory[i]; - for (k = 0; - k < info->io_pages_per_kpage; - k++, j++, paddr += info->io_page_size) { - info->gatt[j] = - agp_bridge->driver->mask_memory(agp_bridge, - paddr, type); - } - } - - agp_bridge->driver->tlb_flush(mem); - - return 0; -} - -static int -parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - int i, io_pg_start, io_pg_count; - - if (type != 0 || mem->type != 0) { - return -EINVAL; - } - - io_pg_start = info->io_pages_per_kpage * pg_start; - io_pg_count = info->io_pages_per_kpage * mem->page_count; - for (i = io_pg_start; i < io_pg_count + io_pg_start; i++) { - info->gatt[i] = agp_bridge->scratch_page; - } - - agp_bridge->driver->tlb_flush(mem); - return 0; -} - -static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, - unsigned long addr, int type) -{ - return SBA_PDIR_VALID_BIT | addr; -} - -static void -parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - u32 command; - - command = readl(info->lba_regs + info->lba_cap_offset + PCI_AGP_STATUS); - - command = agp_collect_device_status(bridge, mode, command); - command |= 0x00000100; - - writel(command, info->lba_regs + info->lba_cap_offset + PCI_AGP_COMMAND); - - agp_device_command(command, (mode & AGP8X_MODE) != 0); -} - -struct agp_bridge_driver parisc_agp_driver = { - .owner = THIS_MODULE, - .size_type = FIXED_APER_SIZE, - .configure = parisc_agp_configure, - .fetch_size = parisc_agp_fetch_size, - .tlb_flush = parisc_agp_tlbflush, - .mask_memory = parisc_agp_mask_memory, - .masks = parisc_agp_masks, - .agp_enable = parisc_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = parisc_agp_create_gatt_table, - .free_gatt_table = parisc_agp_free_gatt_table, - .insert_memory = parisc_agp_insert_memory, - .remove_memory = parisc_agp_remove_memory, - .alloc_by_type = agp_generic_alloc_by_type, - .free_by_type = agp_generic_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_destroy_page = agp_generic_destroy_page, - .cant_use_aperture = 1, -}; - -static int __init -agp_ioc_init(void __iomem *ioc_regs) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - u64 *iova_base, *io_pdir, io_tlb_ps; - int io_tlb_shift; - - printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n"); - - info->ioc_regs = ioc_regs; - - io_tlb_ps = readq(info->ioc_regs+IOC_TCNFG); - switch (io_tlb_ps) { - case 0: io_tlb_shift = 12; break; - case 1: io_tlb_shift = 13; break; - case 2: io_tlb_shift = 14; break; - case 3: io_tlb_shift = 16; break; - default: - printk(KERN_ERR DRVPFX "Invalid IOTLB page size " - "configuration 0x%llx\n", io_tlb_ps); - info->gatt = NULL; - info->gatt_entries = 0; - return -ENODEV; - } - info->io_page_size = 1 << io_tlb_shift; - info->io_pages_per_kpage = PAGE_SIZE / info->io_page_size; - - iova_base = readq(info->ioc_regs+IOC_IBASE) & ~0x1; - info->gart_base = iova_base + PLUTO_IOVA_SIZE - PLUTO_GART_SIZE; - - info->gart_size = PLUTO_GART_SIZE; - info->gatt_entries = info->gart_size / info->io_page_size; - - io_pdir = phys_to_virt(readq(info->ioc_regs+IOC_PDIR_BASE)); - info->gatt = &io_pdir[(PLUTO_IOVA_SIZE/2) >> PAGE_SHIFT]; - - if (info->gatt[0] != SBA_AGPGART_COOKIE) { - info->gatt = NULL; - info->gatt_entries = 0; - printk(KERN_ERR DRVPFX "No reserved IO PDIR entry found; " - "GART disabled\n"); - return -ENODEV; - } - - return 0; -} - -static int -lba_find_capability(int cap) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - u16 status; - u8 pos, id; - int ttl = 48; - - status = readw(info->lba_regs + PCI_STATUS); - if (!(status & PCI_STATUS_CAP_LIST)) - return 0; - pos = readb(info->lba_regs + PCI_CAPABILITY_LIST); - while (ttl-- && pos >= 0x40) { - pos &= ~3; - id = readb(info->lba_regs + pos + PCI_CAP_LIST_ID); - if (id == 0xff) - break; - if (id == cap) - return pos; - pos = readb(info->lba_regs + pos + PCI_CAP_LIST_NEXT); - } - return 0; -} - -static int __init -agp_lba_init(void __iomem *lba_hpa) -{ - struct _parisc_agp_info *info = &parisc_agp_info; - int cap; - - info->lba_regs = lba_hpa; - info->lba_cap_offset = lba_find_capability(PCI_CAP_ID_AGP); - - cap = readl(lba_hpa + info->lba_cap_offset) & 0xff; - if (cap != PCI_CAP_ID_AGP) { - printk(KERN_ERR DRVPFX "Invalid capability ID 0x%02x at 0x%x\n", - cap, info->lba_cap_offset); - return -ENODEV; - } - - return 0; -} - -static int __init -parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa) -{ - struct pci_dev *fake_bridge_dev = NULL; - struct agp_bridge_data *bridge; - int error = 0; - - fake_bridge_dev = kmalloc(sizeof (struct pci_dev), GFP_KERNEL); - if (!fake_bridge_dev) { - error = -ENOMEM; - goto fail; - } - - error = agp_ioc_init(ioc_hpa); - if (error) - goto fail; - - error = agp_lba_init(lba_hpa); - if (error) - goto fail; - - bridge = agp_alloc_bridge(); - if (!bridge) { - error = -ENOMEM; - goto fail; - } - bridge->driver = &parisc_agp_driver; - - fake_bridge_dev->vendor = PCI_VENDOR_ID_HP; - fake_bridge_dev->device = PCI_DEVICE_ID_HP_PCIX_LBA; - bridge->dev = fake_bridge_dev; - - error = agp_add_bridge(bridge); - -fail: - return error; -} - -static struct device *next_device(struct klist_iter *i) { - struct klist_node * n = klist_next(i); - return n ? container_of(n, struct device, knode_parent) : NULL; -} - -static int -parisc_agp_init(void) -{ - extern struct sba_device *sba_list; - - int err = -1; - struct parisc_device *sba = NULL, *lba = NULL; - struct lba_device *lbadev = NULL; - struct device *dev = NULL; - struct klist_iter i; - - if (!sba_list) - goto out; - - /* Find our parent Pluto */ - sba = sba_list->dev; - if (!IS_PLUTO(sba)) { - printk(KERN_INFO DRVPFX "No Pluto found, so no AGPGART for you.\n"); - goto out; - } - - /* Now search our Pluto for our precious AGP device... */ - klist_iter_init(&sba->dev.klist_children, &i); - while ((dev = next_device(&i))) { - struct parisc_device *padev = to_parisc_device(dev); - if (IS_QUICKSILVER(padev)) - lba = padev; - } - klist_iter_exit(&i); - - if (!lba) { - printk(KERN_INFO DRVPFX "No AGP devices found.\n"); - goto out; - } - - lbadev = parisc_get_drvdata(lba); - - /* w00t, let's go find our cookies... */ - parisc_agp_setup(sba_list->ioc[0].ioc_hpa, lbadev->hba.base_addr); - - return 0; - -out: - return err; -} - -module_init(parisc_agp_init); - -MODULE_AUTHOR("Kyle McMartin "); -MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/char/amiserial.c b/trunk/drivers/char/amiserial.c index 486f97c3f4e5..d0e92ed0a367 100644 --- a/trunk/drivers/char/amiserial.c +++ b/trunk/drivers/char/amiserial.c @@ -112,6 +112,17 @@ static struct serial_state rs_table[1]; #define NR_PORTS ARRAY_SIZE(rs_table) +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; + #include #define serial_isroot() (capable(CAP_SYS_ADMIN)) @@ -901,7 +912,7 @@ static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count if (serial_paranoia_check(info, tty->name, "rs_write")) return 0; - if (!info->xmit.buf) + if (!info->xmit.buf || !tmp_buf) return 0; local_save_flags(flags); @@ -1767,6 +1778,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) { struct async_struct *info; int retval, line; + unsigned long page; line = tty->index; if ((line < 0) || (line >= NR_PORTS)) { @@ -1786,6 +1798,17 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #endif info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + /* * If the port is the middle of closing, bail out now */ @@ -2067,6 +2090,11 @@ static __exit void rs_exit(void) kfree(info); } + if (tmp_buf) { + free_page((unsigned long) tmp_buf); + tmp_buf = NULL; + } + release_mem_region(CUSTOM_PHYSADDR+0x30, 4); } diff --git a/trunk/drivers/char/briq_panel.c b/trunk/drivers/char/briq_panel.c index b8c22255f6ad..9f8082f8dd29 100644 --- a/trunk/drivers/char/briq_panel.c +++ b/trunk/drivers/char/briq_panel.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/trunk/drivers/char/cyclades.c b/trunk/drivers/char/cyclades.c index 87b2fb510871..f85b4eb16618 100644 --- a/trunk/drivers/char/cyclades.c +++ b/trunk/drivers/char/cyclades.c @@ -747,6 +747,18 @@ static struct cyclades_port cy_port[NR_PORTS]; static int cy_next_channel; /* next minor available */ +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. This buffer is + * allocated when the first cy_open occurs. + */ +static unsigned char *tmp_buf; + /* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra @@ -2454,6 +2466,7 @@ cy_open(struct tty_struct *tty, struct file * filp) { struct cyclades_port *info; int retval, line; + unsigned long page; line = tty->index; if ((line < 0) || (NR_PORTS <= line)){ @@ -2532,6 +2545,15 @@ cy_open(struct tty_struct *tty, struct file * filp) printk("cyc:cy_open (%d): incrementing count to %d\n", current->pid, info->count); #endif + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } /* * If the port is the middle of closing, bail out now @@ -2810,7 +2832,7 @@ cy_write(struct tty_struct * tty, const unsigned char *buf, int count) return 0; } - if (!info->xmit_buf) + if (!info->xmit_buf || !tmp_buf) return 0; CY_LOCK(info, flags); @@ -5468,6 +5490,10 @@ cy_cleanup_module(void) #endif } } + if (tmp_buf) { + free_page((unsigned long) tmp_buf); + tmp_buf = NULL; + } } /* cy_cleanup_module */ module_init(cy_init); diff --git a/trunk/drivers/char/epca.c b/trunk/drivers/char/epca.c index c3f95583a120..3baa2ab8cbd4 100644 --- a/trunk/drivers/char/epca.c +++ b/trunk/drivers/char/epca.c @@ -1113,8 +1113,11 @@ static void __exit epca_module_exit(void) ch = card_ptr[crd]; for (count = 0; count < bd->numports; count++, ch++) { /* Begin for each port */ - if (ch && ch->tty) - tty_hangup(ch->tty); + if (ch) { + if (ch->tty) + tty_hangup(ch->tty); + kfree(ch->tmp_buf); + } } /* End for each port */ } /* End for each card */ pci_unregister_driver (&epca_driver); @@ -1632,6 +1635,16 @@ static void post_fep_init(unsigned int crd) init_waitqueue_head(&ch->close_wait); spin_unlock_irqrestore(&epca_lock, flags); + + ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); + if (!ch->tmp_buf) { + printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); + release_region((int)bd->port, 4); + while(i-- > 0) + kfree((ch--)->tmp_buf); + return; + } else + memset((void *)ch->tmp_buf,0,ch->txbufsize); } /* End for each port */ printk(KERN_INFO diff --git a/trunk/drivers/char/epca.h b/trunk/drivers/char/epca.h index a297238cd3ba..456d6c8f94a8 100644 --- a/trunk/drivers/char/epca.h +++ b/trunk/drivers/char/epca.h @@ -130,6 +130,7 @@ struct channel unsigned long c_oflag; unsigned char __iomem *txptr; unsigned char __iomem *rxptr; + unsigned char *tmp_buf; struct board_info *board; struct board_chan __iomem *brdchan; struct digi_struct digiext; diff --git a/trunk/drivers/char/ftape/lowlevel/fdc-io.c b/trunk/drivers/char/ftape/lowlevel/fdc-io.c index 65c9d2ec60bd..216532445652 100644 --- a/trunk/drivers/char/ftape/lowlevel/fdc-io.c +++ b/trunk/drivers/char/ftape/lowlevel/fdc-io.c @@ -26,7 +26,6 @@ * Linux. */ -#include /* for CONFIG_FT_* */ #include #include #include diff --git a/trunk/drivers/char/ftape/zftape/zftape-rw.c b/trunk/drivers/char/ftape/zftape/zftape-rw.c index a61ef50f3dfc..dab634686885 100644 --- a/trunk/drivers/char/ftape/zftape/zftape-rw.c +++ b/trunk/drivers/char/ftape/zftape/zftape-rw.c @@ -24,7 +24,6 @@ * zftape. */ -#include /* for CONFIG_ZFT_DFLT_BLK_SZ */ #include #include diff --git a/trunk/drivers/char/ftape/zftape/zftape-rw.h b/trunk/drivers/char/ftape/zftape/zftape-rw.h index 14c07f086575..1ceec22b60bd 100644 --- a/trunk/drivers/char/ftape/zftape/zftape-rw.h +++ b/trunk/drivers/char/ftape/zftape/zftape-rw.h @@ -28,7 +28,6 @@ * */ -#include /* for CONFIG_ZFT_DFLT_BLK_SZ */ #include "../zftape/zftape-buffers.h" #define SEGMENTS_PER_TAPE (ft_segments_per_track * ft_tracks_per_tape) diff --git a/trunk/drivers/char/generic_serial.c b/trunk/drivers/char/generic_serial.c index 87127e49c0db..4711d9b3a595 100644 --- a/trunk/drivers/char/generic_serial.c +++ b/trunk/drivers/char/generic_serial.c @@ -33,6 +33,8 @@ #define DEBUG +static char * tmp_buf; + static int gs_debug; #ifdef DEBUG @@ -203,7 +205,7 @@ int gs_write(struct tty_struct * tty, if (!tty) return -EIO; port = tty->driver_data; - if (!port || !port->xmit_buf) + if (!port || !port->xmit_buf || !tmp_buf) return -EIO; local_save_flags(flags); @@ -835,9 +837,24 @@ void gs_set_termios (struct tty_struct * tty, int gs_init_port(struct gs_port *port) { unsigned long flags; + unsigned long page; func_enter (); + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + spin_lock_irqsave (&port->driver_lock, flags); /* Don't expect this to make a difference. */ + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + spin_unlock_irqrestore (&port->driver_lock, flags); + if (!tmp_buf) { + func_exit (); + return -ENOMEM; + } + } + if (port->flags & ASYNC_INITIALIZED) { func_exit (); return 0; diff --git a/trunk/drivers/char/hvc_iseries.c b/trunk/drivers/char/hvc_iseries.c index f144a947bd17..8b6f197e5f8c 100644 --- a/trunk/drivers/char/hvc_iseries.c +++ b/trunk/drivers/char/hvc_iseries.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -489,9 +488,6 @@ static int hvc_vio_init(void) atomic_t wait_flag; int rc; - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - /* +2 for fudge */ rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario, VIOCHAR_WINDOW + 2); @@ -566,7 +562,7 @@ static int hvc_find_vtys(void) for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; + uint32_t *vtermno; /* We have statically defined space for only a certain number * of console adapters. @@ -575,7 +571,7 @@ static int hvc_find_vtys(void) (num_found >= VTTY_PORTS)) break; - vtermno = get_property(vty, "reg", NULL); + vtermno = (uint32_t *)get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/trunk/drivers/char/hvc_vio.c b/trunk/drivers/char/hvc_vio.c index f9c00844d2bf..cc95941148fb 100644 --- a/trunk/drivers/char/hvc_vio.c +++ b/trunk/drivers/char/hvc_vio.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "hvc_console.h" @@ -121,9 +120,6 @@ static int hvc_vio_init(void) { int rc; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - /* Register as a vio device to receive callbacks */ rc = vio_register_driver(&hvc_vio_driver); diff --git a/trunk/drivers/char/hw_random/ixp4xx-rng.c b/trunk/drivers/char/hw_random/ixp4xx-rng.c index 3cf4d641a51c..c9caff57db85 100644 --- a/trunk/drivers/char/hw_random/ixp4xx-rng.c +++ b/trunk/drivers/char/hw_random/ixp4xx-rng.c @@ -15,7 +15,6 @@ */ #include -#include #include #include #include diff --git a/trunk/drivers/char/mspec.c b/trunk/drivers/char/mspec.c index 5426b1e5595f..5c0dec39cf6c 100644 --- a/trunk/drivers/char/mspec.c +++ b/trunk/drivers/char/mspec.c @@ -30,7 +30,6 @@ * processor from ever speculating a cache line from this page. */ -#include #include #include #include diff --git a/trunk/drivers/char/nsc_gpio.c b/trunk/drivers/char/nsc_gpio.c index 7719bd75810b..4d47d79bcea7 100644 --- a/trunk/drivers/char/nsc_gpio.c +++ b/trunk/drivers/char/nsc_gpio.c @@ -7,7 +7,6 @@ Copyright (c) 2005 Jim Cromie */ -#include #include #include #include diff --git a/trunk/drivers/char/pcmcia/synclink_cs.c b/trunk/drivers/char/pcmcia/synclink_cs.c index d1ecb2c6de98..73e324209913 100644 --- a/trunk/drivers/char/pcmcia/synclink_cs.c +++ b/trunk/drivers/char/pcmcia/synclink_cs.c @@ -35,7 +35,6 @@ #define MAX_DEVICE_COUNT 4 -#include #include #include #include diff --git a/trunk/drivers/char/riscom8.c b/trunk/drivers/char/riscom8.c index b0ab3f28cc6a..214d850112fd 100644 --- a/trunk/drivers/char/riscom8.c +++ b/trunk/drivers/char/riscom8.c @@ -81,6 +81,7 @@ static struct riscom_board * IRQ_to_board[16]; static struct tty_driver *riscom_driver; +static unsigned char * tmp_buf; static unsigned long baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, @@ -1123,7 +1124,7 @@ static int rc_write(struct tty_struct * tty, bp = port_Board(port); - if (!tty || !port->xmit_buf) + if (!tty || !port->xmit_buf || !tmp_buf) return 0; save_flags(flags); @@ -1611,6 +1612,11 @@ static inline int rc_init_drivers(void) if (!riscom_driver) return -ENOMEM; + if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) { + printk(KERN_ERR "rc: Couldn't get free page.\n"); + put_tty_driver(riscom_driver); + return 1; + } memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); riscom_driver->owner = THIS_MODULE; riscom_driver->name = "ttyL"; @@ -1623,6 +1629,7 @@ static inline int rc_init_drivers(void) riscom_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(riscom_driver, &riscom_ops); if ((error = tty_register_driver(riscom_driver))) { + free_page((unsigned long)tmp_buf); put_tty_driver(riscom_driver); printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, " "error = %d\n", @@ -1650,6 +1657,7 @@ static void rc_release_drivers(void) save_flags(flags); cli(); + free_page((unsigned long)tmp_buf); tty_unregister_driver(riscom_driver); put_tty_driver(riscom_driver); restore_flags(flags); diff --git a/trunk/drivers/char/serial167.c b/trunk/drivers/char/serial167.c index f4809c8183cc..b4ea1266b663 100644 --- a/trunk/drivers/char/serial167.c +++ b/trunk/drivers/char/serial167.c @@ -118,6 +118,17 @@ struct cyclades_port cy_port[] = { }; #define NR_PORTS ARRAY_SIZE(cy_port) +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf = 0; + /* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra @@ -1121,7 +1132,7 @@ cy_put_char(struct tty_struct *tty, unsigned char ch) if (serial_paranoia_check(info, tty->name, "cy_put_char")) return; - if (!info->xmit_buf) + if (!tty || !info->xmit_buf) return; local_irq_save(flags); @@ -1187,7 +1198,7 @@ cy_write(struct tty_struct * tty, return 0; } - if (!info->xmit_buf){ + if (!tty || !info->xmit_buf || !tmp_buf){ return 0; } @@ -1972,6 +1983,13 @@ cy_open(struct tty_struct *tty, struct file * filp) tty->driver_data = info; info->tty = tty; + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } + /* * Start up serial port */ diff --git a/trunk/drivers/char/sx.c b/trunk/drivers/char/sx.c index 57e31e5eaedb..8fd71a5fc619 100644 --- a/trunk/drivers/char/sx.c +++ b/trunk/drivers/char/sx.c @@ -203,9 +203,7 @@ #define RCS_ID "$Id: sx.c,v 1.33 2000/03/08 10:01:02 wolff, pvdl Exp $" #define RCS_REV "$Revision: 1.33 $" - #include -#include #include #include #include diff --git a/trunk/drivers/char/synclink.c b/trunk/drivers/char/synclink.c index 38d94987de83..a4150c4519c4 100644 --- a/trunk/drivers/char/synclink.c +++ b/trunk/drivers/char/synclink.c @@ -63,7 +63,6 @@ #define MAX_PCI_DEVICES 10 #define MAX_TOTAL_DEVICES 20 -#include #include #include #include diff --git a/trunk/drivers/char/watchdog/iTCO_wdt.c b/trunk/drivers/char/watchdog/iTCO_wdt.c index 8f89948832fc..aaac94db0d8b 100644 --- a/trunk/drivers/char/watchdog/iTCO_wdt.c +++ b/trunk/drivers/char/watchdog/iTCO_wdt.c @@ -49,7 +49,6 @@ #define PFX DRV_NAME ": " /* Includes */ -#include /* For CONFIG_WATCHDOG_NOWAYOUT/... */ #include /* For module specific items */ #include /* For new moduleparam's */ #include /* For standard types (like size_t) */ diff --git a/trunk/drivers/char/watchdog/omap_wdt.c b/trunk/drivers/char/watchdog/omap_wdt.c index 8f90b90a5021..5dbd7dc2936f 100644 --- a/trunk/drivers/char/watchdog/omap_wdt.c +++ b/trunk/drivers/char/watchdog/omap_wdt.c @@ -27,7 +27,6 @@ */ #include -#include #include #include #include diff --git a/trunk/drivers/char/watchdog/pcwd.c b/trunk/drivers/char/watchdog/pcwd.c index 6f8515db5b07..8e1e6e48e0a7 100644 --- a/trunk/drivers/char/watchdog/pcwd.c +++ b/trunk/drivers/char/watchdog/pcwd.c @@ -49,7 +49,6 @@ * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ */ -#include /* For CONFIG_WATCHDOG_NOWAYOUT/... */ #include /* For module specific items */ #include /* For new moduleparam's */ #include /* For standard types (like size_t) */ diff --git a/trunk/drivers/char/watchdog/pcwd_pci.c b/trunk/drivers/char/watchdog/pcwd_pci.c index 2de6e497c140..f4872c871063 100644 --- a/trunk/drivers/char/watchdog/pcwd_pci.c +++ b/trunk/drivers/char/watchdog/pcwd_pci.c @@ -31,7 +31,6 @@ * Includes, defines, variables, module parameters, ... */ -#include /* For CONFIG_WATCHDOG_NOWAYOUT/... */ #include /* For module specific items */ #include /* For new moduleparam's */ #include /* For standard types (like size_t) */ diff --git a/trunk/drivers/char/watchdog/pnx4008_wdt.c b/trunk/drivers/char/watchdog/pnx4008_wdt.c index db2731ba88e3..3a55fc6abcd8 100644 --- a/trunk/drivers/char/watchdog/pnx4008_wdt.c +++ b/trunk/drivers/char/watchdog/pnx4008_wdt.c @@ -14,7 +14,6 @@ * or implied. */ -#include #include #include #include diff --git a/trunk/drivers/clocksource/scx200_hrt.c b/trunk/drivers/clocksource/scx200_hrt.c index 22915cc46ba7..d418b8297211 100644 --- a/trunk/drivers/clocksource/scx200_hrt.c +++ b/trunk/drivers/clocksource/scx200_hrt.c @@ -63,7 +63,7 @@ static struct clocksource cs_hrt = { static int __init init_hrt_clocksource(void) { - /* Make sure scx200 has initialized the configuration block */ + /* Make sure scx200 has initializedd the configuration block */ if (!scx200_cb_present()) return -ENODEV; @@ -76,7 +76,7 @@ static int __init init_hrt_clocksource(void) } /* write timer config */ - outb(HR_TMEN | (mhz27 ? HR_TMCLKSEL : 0), + outb(HR_TMEN | (mhz27) ? HR_TMCLKSEL : 0, scx200_cb_base + SCx200_TMCNFG_OFFSET); if (mhz27) { diff --git a/trunk/drivers/cpufreq/cpufreq.c b/trunk/drivers/cpufreq/cpufreq.c index 86e69b7f9122..2caaf71d80c8 100644 --- a/trunk/drivers/cpufreq/cpufreq.c +++ b/trunk/drivers/cpufreq/cpufreq.c @@ -52,14 +52,8 @@ static void handle_update(void *data); * The mutex locks both lists. */ static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list); -static struct srcu_notifier_head cpufreq_transition_notifier_list; +static BLOCKING_NOTIFIER_HEAD(cpufreq_transition_notifier_list); -static int __init init_cpufreq_transition_notifier_list(void) -{ - srcu_init_notifier_head(&cpufreq_transition_notifier_list); - return 0; -} -core_initcall(init_cpufreq_transition_notifier_list); static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX (cpufreq_governor_mutex); @@ -268,14 +262,14 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) freqs->old = policy->cur; } } - srcu_notifier_call_chain(&cpufreq_transition_notifier_list, + blocking_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); adjust_jiffies(CPUFREQ_PRECHANGE, freqs); break; case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); - srcu_notifier_call_chain(&cpufreq_transition_notifier_list, + blocking_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); if (likely(policy) && likely(policy->cpu == freqs->cpu)) policy->cur = freqs->new; @@ -1055,7 +1049,7 @@ static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg) freqs.old = cpu_policy->cur; freqs.new = cur_freq; - srcu_notifier_call_chain(&cpufreq_transition_notifier_list, + blocking_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_SUSPENDCHANGE, &freqs); adjust_jiffies(CPUFREQ_SUSPENDCHANGE, &freqs); @@ -1136,7 +1130,7 @@ static int cpufreq_resume(struct sys_device * sysdev) freqs.old = cpu_policy->cur; freqs.new = cur_freq; - srcu_notifier_call_chain( + blocking_notifier_call_chain( &cpufreq_transition_notifier_list, CPUFREQ_RESUMECHANGE, &freqs); adjust_jiffies(CPUFREQ_RESUMECHANGE, &freqs); @@ -1182,7 +1176,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: - ret = srcu_notifier_chain_register( + ret = blocking_notifier_chain_register( &cpufreq_transition_notifier_list, nb); break; case CPUFREQ_POLICY_NOTIFIER: @@ -1214,7 +1208,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: - ret = srcu_notifier_chain_unregister( + ret = blocking_notifier_chain_unregister( &cpufreq_transition_notifier_list, nb); break; case CPUFREQ_POLICY_NOTIFIER: diff --git a/trunk/drivers/hwmon/w83791d.c b/trunk/drivers/hwmon/w83791d.c index d965d074cd61..371ed4f69a97 100644 --- a/trunk/drivers/hwmon/w83791d.c +++ b/trunk/drivers/hwmon/w83791d.c @@ -32,7 +32,6 @@ The w83791g chip is the same as the w83791d but lead-free. */ -#include #include #include #include diff --git a/trunk/drivers/i2c/busses/i2c-ocores.c b/trunk/drivers/i2c/busses/i2c-ocores.c index 952a28d485ce..3e276e958ef7 100644 --- a/trunk/drivers/i2c/busses/i2c-ocores.c +++ b/trunk/drivers/i2c/busses/i2c-ocores.c @@ -9,7 +9,6 @@ * kind, whether express or implied. */ -#include #include #include #include diff --git a/trunk/drivers/ide/pci/generic.c b/trunk/drivers/ide/pci/generic.c index 0cb7b9b520ea..965c43659e35 100644 --- a/trunk/drivers/ide/pci/generic.c +++ b/trunk/drivers/ide/pci/generic.c @@ -23,7 +23,6 @@ #undef REALLY_SLOW_IO /* most systems can safely undef this */ -#include /* for CONFIG_BLK_DEV_IDEPCI */ #include #include #include diff --git a/trunk/drivers/ide/pci/jmicron.c b/trunk/drivers/ide/pci/jmicron.c index 68c74bbf8b06..c1cec236ecf0 100644 --- a/trunk/drivers/ide/pci/jmicron.c +++ b/trunk/drivers/ide/pci/jmicron.c @@ -5,7 +5,6 @@ * May be copied or modified under the terms of the GNU General Public License */ -#include #include #include #include diff --git a/trunk/drivers/ide/pci/rz1000.c b/trunk/drivers/ide/pci/rz1000.c index 608cd7609072..5f6950c2d1d1 100644 --- a/trunk/drivers/ide/pci/rz1000.c +++ b/trunk/drivers/ide/pci/rz1000.c @@ -17,7 +17,6 @@ #undef REALLY_SLOW_IO /* most systems can safely undef this */ -#include /* for CONFIG_BLK_DEV_IDEPCI */ #include #include #include diff --git a/trunk/drivers/infiniband/hw/ipath/ipath_mmap.c b/trunk/drivers/infiniband/hw/ipath/ipath_mmap.c index 11b7378ff214..a82157db4689 100644 --- a/trunk/drivers/infiniband/hw/ipath/ipath_mmap.c +++ b/trunk/drivers/infiniband/hw/ipath/ipath_mmap.c @@ -30,7 +30,6 @@ * SOFTWARE. */ -#include #include #include #include diff --git a/trunk/drivers/isdn/hisax/niccy.c b/trunk/drivers/isdn/hisax/niccy.c index 0945336c28da..489022bdef7b 100644 --- a/trunk/drivers/isdn/hisax/niccy.c +++ b/trunk/drivers/isdn/hisax/niccy.c @@ -13,6 +13,7 @@ * */ + #include #include "hisax.h" #include "isac.h" @@ -44,31 +45,33 @@ static const char *niccy_revision = "$Revision: 1.21.2.4 $"; #define PCI_IRQ_DISABLE 0xff0000 #define PCI_IRQ_ASSERT 0x800000 -static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off) +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) { register u_char ret; byteout(ale, off); ret = bytein(adr); - return ret; + return (ret); } -static inline void readfifo(unsigned int ale, unsigned int adr, u_char off, - u_char *data, int size) +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { byteout(ale, off); insb(adr, data, size); } -static inline void writereg(unsigned int ale, unsigned int adr, u_char off, - u_char data) + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { byteout(ale, off); byteout(adr, data); } -static inline void writefifo(unsigned int ale, unsigned int adr, u_char off, - u_char *data, int size) +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { byteout(ale, off); outsb(adr, data, size); @@ -76,34 +79,39 @@ static inline void writefifo(unsigned int ale, unsigned int adr, u_char off, /* Interface functions */ -static u_char ReadISAC(struct IsdnCardState *cs, u_char offset) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - return readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset); + return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset)); } -static void WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); } -static void ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); } -static void WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); } -static u_char ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - return readreg(cs->hw.niccy.hscx_ale, - cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)); + return (readreg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0))); } -static void WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, - u_char value) +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); @@ -122,8 +130,8 @@ static void WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, #include "hscx_irq.c" -static irqreturn_t niccy_interrupt(int intno, void *dev_id, - struct pt_regs *regs) +static irqreturn_t +niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) { struct IsdnCardState *cs = dev_id; u_char val; @@ -133,23 +141,21 @@ static irqreturn_t niccy_interrupt(int intno, void *dev_id, if (cs->subtyp == NICCY_PCI) { int ival; ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); - if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */ + if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */ spin_unlock_irqrestore(&cs->lock, flags); return IRQ_NONE; } outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); } - val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, - HSCX_ISTA + 0x40); -Start_HSCX: + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + Start_HSCX: if (val) hscx_int_main(cs, val); val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); -Start_ISAC: + Start_ISAC: if (val) isac_interrupt(cs, val); - val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, - HSCX_ISTA + 0x40); + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); if (val) { if (cs->debug & L1_DEB_HSCX) debugl1(cs, "HSCX IntStat after IntRoutine"); @@ -162,21 +168,21 @@ static irqreturn_t niccy_interrupt(int intno, void *dev_id, goto Start_ISAC; } writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, - 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); - writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40,0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); spin_unlock_irqrestore(&cs->lock, flags); return IRQ_HANDLED; } -static void release_io_niccy(struct IsdnCardState *cs) +static void +release_io_niccy(struct IsdnCardState *cs) { if (cs->subtyp == NICCY_PCI) { int val; - + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); val &= PCI_IRQ_DISABLE; outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); @@ -188,7 +194,8 @@ static void release_io_niccy(struct IsdnCardState *cs) } } -static void niccy_reset(struct IsdnCardState *cs) +static void +niccy_reset(struct IsdnCardState *cs) { if (cs->subtyp == NICCY_PCI) { int val; @@ -200,28 +207,29 @@ static void niccy_reset(struct IsdnCardState *cs) inithscxisac(cs, 3); } -static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) +static int +niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) { u_long flags; switch (mt) { - case CARD_RESET: - spin_lock_irqsave(&cs->lock, flags); - niccy_reset(cs); - spin_unlock_irqrestore(&cs->lock, flags); - return 0; - case CARD_RELEASE: - release_io_niccy(cs); - return 0; - case CARD_INIT: - spin_lock_irqsave(&cs->lock, flags); - niccy_reset(cs); - spin_unlock_irqrestore(&cs->lock, flags); - return 0; - case CARD_TEST: - return 0; + case CARD_RESET: + spin_lock_irqsave(&cs->lock, flags); + niccy_reset(cs); + spin_unlock_irqrestore(&cs->lock, flags); + return(0); + case CARD_RELEASE: + release_io_niccy(cs); + return(0); + case CARD_INIT: + spin_lock_irqsave(&cs->lock, flags); + niccy_reset(cs); + spin_unlock_irqrestore(&cs->lock, flags); + return(0); + case CARD_TEST: + return(0); } - return 0; + return(0); } static struct pci_dev *niccy_dev __devinitdata = NULL; @@ -229,7 +237,8 @@ static struct pci_dev *niccy_dev __devinitdata = NULL; static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __devinit setup_niccy(struct IsdnCard *card) +int __devinit +setup_niccy(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; char tmp[64]; @@ -237,44 +246,40 @@ int __devinit setup_niccy(struct IsdnCard *card) strcpy(tmp, niccy_revision); printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_NICCY) - return 0; + return (0); #ifdef __ISAPNP__ if (!card->para[1] && isapnp_present()) { struct pnp_dev *pnp_d = NULL; int err; - pnp_c = pnp_find_card(ISAPNP_VENDOR('S', 'D', 'A'), - ISAPNP_FUNCTION(0x0150), pnp_c); - if (pnp_c) { - pnp_d = pnp_find_dev(pnp_c, - ISAPNP_VENDOR('S', 'D', 'A'), - ISAPNP_FUNCTION(0x0150), pnp_d); - if (!pnp_d) { - printk(KERN_ERR "NiccyPnP: PnP error card " - "found, no device\n"); - return 0; + if ((pnp_c = pnp_find_card( + ISAPNP_VENDOR('S', 'D', 'A'), + ISAPNP_FUNCTION(0x0150), pnp_c))) { + if (!(pnp_d = pnp_find_dev(pnp_c, + ISAPNP_VENDOR('S', 'D', 'A'), + ISAPNP_FUNCTION(0x0150), pnp_d))) { + printk(KERN_ERR "NiccyPnP: PnP error card found, no device\n"); + return (0); } pnp_disable_dev(pnp_d); err = pnp_activate_dev(pnp_d); - if (err < 0) { - printk(KERN_WARNING "%s: pnp_activate_dev " - "ret(%d)\n", __FUNCTION__, err); - return 0; + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); } card->para[1] = pnp_port_start(pnp_d, 0); card->para[2] = pnp_port_start(pnp_d, 1); card->para[0] = pnp_irq(pnp_d, 0); - if (!card->para[0] || !card->para[1] || - !card->para[2]) { - printk(KERN_ERR "NiccyPnP:some resources are " - "missing %ld/%lx/%lx\n", - card->para[0], card->para[1], - card->para[2]); + if (!card->para[0] || !card->para[1] || !card->para[2]) { + printk(KERN_ERR "NiccyPnP:some resources are missing %ld/%lx/%lx\n", + card->para[0], card->para[1], card->para[2]); pnp_disable_dev(pnp_d); - return 0; + return(0); } - } else + } else { printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n"); + } } #endif if (card->para[1]) { @@ -286,51 +291,50 @@ int __devinit setup_niccy(struct IsdnCard *card) cs->subtyp = NICCY_PNP; cs->irq = card->para[0]; if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) { - printk(KERN_WARNING "HiSax: %s data port %x-%x " - "already in use\n", CardType[card->typ], - cs->hw.niccy.isac, cs->hw.niccy.isac + 1); - return 0; + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 1); + return (0); } if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) { - printk(KERN_WARNING "HiSax: %s address port %x-%x " - "already in use\n", CardType[card->typ], + printk(KERN_WARNING + "HiSax: %s address port %x-%x already in use\n", + CardType[card->typ], cs->hw.niccy.isac_ale, cs->hw.niccy.isac_ale + 1); release_region(cs->hw.niccy.isac, 2); - return 0; + return (0); } } else { #ifdef CONFIG_PCI u_int pci_ioaddr; cs->subtyp = 0; if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM, - PCI_DEVICE_ID_SATSAGEM_NICCY, - niccy_dev))) { + PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) { if (pci_enable_device(niccy_dev)) - return 0; + return(0); /* get IRQ */ if (!niccy_dev->irq) { - printk(KERN_WARNING - "Niccy: No IRQ for PCI card found\n"); - return 0; + printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); } cs->irq = niccy_dev->irq; cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0); if (!cs->hw.niccy.cfg_reg) { - printk(KERN_WARNING - "Niccy: No IO-Adr for PCI cfg found\n"); - return 0; + printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); + return(0); } pci_ioaddr = pci_resource_start(niccy_dev, 1); if (!pci_ioaddr) { - printk(KERN_WARNING - "Niccy: No IO-Adr for PCI card found\n"); - return 0; + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); } cs->subtyp = NICCY_PCI; } else { printk(KERN_WARNING "Niccy: No PCI card found\n"); - return 0; + return(0); } cs->irq_flags |= IRQF_SHARED; cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; @@ -339,28 +343,29 @@ int __devinit setup_niccy(struct IsdnCard *card) cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; if (!request_region(cs->hw.niccy.isac, 4, "niccy")) { printk(KERN_WARNING - "HiSax: %s data port %x-%x already in use\n", - CardType[card->typ], - cs->hw.niccy.isac, cs->hw.niccy.isac + 4); - return 0; + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 4); + return (0); } if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) { printk(KERN_WARNING "HiSax: %s pci port %x-%x already in use\n", - CardType[card->typ], - cs->hw.niccy.cfg_reg, - cs->hw.niccy.cfg_reg + 0x40); + CardType[card->typ], + cs->hw.niccy.cfg_reg, + cs->hw.niccy.cfg_reg + 0x40); release_region(cs->hw.niccy.isac, 4); - return 0; + return (0); } #else printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); - return 0; -#endif /* CONFIG_PCI */ + return (0); +#endif /* CONFIG_PCI */ } printk(KERN_INFO "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n", - CardType[cs->typ], (cs->subtyp == 1) ? "PnP" : "PCI", + CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI", cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); setup_isac(cs); cs->readisac = &ReadISAC; @@ -374,10 +379,10 @@ int __devinit setup_niccy(struct IsdnCard *card) cs->irq_func = &niccy_interrupt; ISACVersion(cs, "Niccy:"); if (HscxVersion(cs, "Niccy:")) { - printk(KERN_WARNING "Niccy: wrong HSCX versions check IO " - "address\n"); + printk(KERN_WARNING + "Niccy: wrong HSCX versions check IO address\n"); release_io_niccy(cs); - return 0; + return (0); } - return 1; + return (1); } diff --git a/trunk/drivers/leds/leds-ams-delta.c b/trunk/drivers/leds/leds-ams-delta.c index e9f06116c4d7..599878c8e714 100644 --- a/trunk/drivers/leds/leds-ams-delta.c +++ b/trunk/drivers/leds/leds-ams-delta.c @@ -8,7 +8,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/trunk/drivers/misc/Kconfig b/trunk/drivers/misc/Kconfig index 3df0e7a07c46..7fc692a8f5b0 100644 --- a/trunk/drivers/misc/Kconfig +++ b/trunk/drivers/misc/Kconfig @@ -18,7 +18,7 @@ config IBM_ASM service processor board as a regular serial port. To make use of this feature serial driver support (CONFIG_SERIAL_8250) must be enabled. - + WARNING: This software may not be supported or function correctly on your IBM server. Please consult the IBM ServerProven website for @@ -28,33 +28,5 @@ config IBM_ASM If unsure, say N. -config TIFM_CORE - tristate "TI Flash Media interface support (EXPERIMENTAL)" - depends on EXPERIMENTAL - help - If you want support for Texas Instruments(R) Flash Media adapters - you should select this option and then also choose an appropriate - host adapter, such as 'TI Flash Media PCI74xx/PCI76xx host adapter - support', if you have a TI PCI74xx compatible card reader, for - example. - You will also have to select some flash card format drivers. MMC/SD - cards are supported via 'MMC/SD Card support: TI Flash Media MMC/SD - Interface support (MMC_TIFM_SD)'. - - To compile this driver as a module, choose M here: the module will - be called tifm_core. - -config TIFM_7XX1 - tristate "TI Flash Media PCI74xx/PCI76xx host adapter support (EXPERIMENTAL)" - depends on PCI && TIFM_CORE && EXPERIMENTAL - default TIFM_CORE - help - This option enables support for Texas Instruments(R) PCI74xx and - PCI76xx families of Flash Media adapters, found in many laptops. - To make actual use of the device, you will have to select some - flash card format drivers, as outlined in the TIFM_CORE Help. - - To compile this driver as a module, choose M here: the module will - be called tifm_7xx1. - endmenu + diff --git a/trunk/drivers/misc/Makefile b/trunk/drivers/misc/Makefile index d65ece76095a..c1bf1fb04c5c 100644 --- a/trunk/drivers/misc/Makefile +++ b/trunk/drivers/misc/Makefile @@ -6,5 +6,3 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ obj-$(CONFIG_LKDTM) += lkdtm.o -obj-$(CONFIG_TIFM_CORE) += tifm_core.o -obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o diff --git a/trunk/drivers/misc/tifm_7xx1.c b/trunk/drivers/misc/tifm_7xx1.c deleted file mode 100644 index a7ed30446185..000000000000 --- a/trunk/drivers/misc/tifm_7xx1.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * tifm_7xx1.c - TI FlashMedia driver - * - * Copyright (C) 2006 Alex Dubov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include - -#define DRIVER_NAME "tifm_7xx1" -#define DRIVER_VERSION "0.6" - -static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) -{ - int cnt; - unsigned long flags; - - spin_lock_irqsave(&fm->lock, flags); - if (!fm->inhibit_new_cards) { - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - if (fm->sockets[cnt] == sock) { - fm->remove_mask |= (1 << cnt); - queue_work(fm->wq, &fm->media_remover); - break; - } - } - } - spin_unlock_irqrestore(&fm->lock, flags); -} - -static void tifm_7xx1_remove_media(void *adapter) -{ - struct tifm_adapter *fm = adapter; - unsigned long flags; - int cnt; - struct tifm_dev *sock; - - if (!class_device_get(&fm->cdev)) - return; - spin_lock_irqsave(&fm->lock, flags); - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - if (fm->sockets[cnt] && (fm->remove_mask & (1 << cnt))) { - printk(KERN_INFO DRIVER_NAME - ": demand removing card from socket %d\n", cnt); - sock = fm->sockets[cnt]; - fm->sockets[cnt] = 0; - fm->remove_mask &= ~(1 << cnt); - - writel(0x0e00, sock->addr + SOCK_CONTROL); - - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_SET_INTERRUPT_ENABLE); - - spin_unlock_irqrestore(&fm->lock, flags); - device_unregister(&sock->dev); - spin_lock_irqsave(&fm->lock, flags); - } - } - spin_unlock_irqrestore(&fm->lock, flags); - class_device_put(&fm->cdev); -} - -static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct tifm_adapter *fm = dev_id; - unsigned int irq_status; - unsigned int sock_irq_status, cnt; - - spin_lock(&fm->lock); - irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); - if (irq_status == 0 || irq_status == (~0)) { - spin_unlock(&fm->lock); - return IRQ_NONE; - } - - if (irq_status & TIFM_IRQ_ENABLE) { - writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - sock_irq_status = (irq_status >> cnt) & - (TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK); - - if (fm->sockets[cnt]) { - if (sock_irq_status && - fm->sockets[cnt]->signal_irq) - sock_irq_status = fm->sockets[cnt]-> - signal_irq(fm->sockets[cnt], - sock_irq_status); - - if (irq_status & (1 << cnt)) - fm->remove_mask |= 1 << cnt; - } else { - if (irq_status & (1 << cnt)) - fm->insert_mask |= 1 << cnt; - } - } - } - writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); - - if (!fm->inhibit_new_cards) { - if (!fm->remove_mask && !fm->insert_mask) { - writel(TIFM_IRQ_ENABLE, - fm->addr + FM_SET_INTERRUPT_ENABLE); - } else { - queue_work(fm->wq, &fm->media_remover); - queue_work(fm->wq, &fm->media_inserter); - } - } - - spin_unlock(&fm->lock); - return IRQ_HANDLED; -} - -static tifm_media_id tifm_7xx1_toggle_sock_power(char *sock_addr, int is_x2) -{ - unsigned int s_state; - int cnt; - - writel(0x0e00, sock_addr + SOCK_CONTROL); - - for (cnt = 0; cnt < 100; cnt++) { - if (!(TIFM_SOCK_STATE_POWERED & - readl(sock_addr + SOCK_PRESENT_STATE))) - break; - msleep(10); - } - - s_state = readl(sock_addr + SOCK_PRESENT_STATE); - if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) - return FM_NULL; - - if (is_x2) { - writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); - } else { - // SmartMedia cards need extra 40 msec - if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1) - msleep(40); - writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, - sock_addr + SOCK_CONTROL); - msleep(10); - writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED, - sock_addr + SOCK_CONTROL); - } - - for (cnt = 0; cnt < 100; cnt++) { - if ((TIFM_SOCK_STATE_POWERED & - readl(sock_addr + SOCK_PRESENT_STATE))) - break; - msleep(10); - } - - if (!is_x2) - writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), - sock_addr + SOCK_CONTROL); - - return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; -} - -inline static char *tifm_7xx1_sock_addr(char *base_addr, unsigned int sock_num) -{ - return base_addr + ((sock_num + 1) << 10); -} - -static void tifm_7xx1_insert_media(void *adapter) -{ - struct tifm_adapter *fm = adapter; - unsigned long flags; - tifm_media_id media_id; - char *card_name = "xx"; - int cnt, ok_to_register; - unsigned int insert_mask; - struct tifm_dev *new_sock = 0; - - if (!class_device_get(&fm->cdev)) - return; - spin_lock_irqsave(&fm->lock, flags); - insert_mask = fm->insert_mask; - fm->insert_mask = 0; - if (fm->inhibit_new_cards) { - spin_unlock_irqrestore(&fm->lock, flags); - class_device_put(&fm->cdev); - return; - } - spin_unlock_irqrestore(&fm->lock, flags); - - for (cnt = 0; cnt < fm->max_sockets; cnt++) { - if (!(insert_mask & (1 << cnt))) - continue; - - media_id = tifm_7xx1_toggle_sock_power(tifm_7xx1_sock_addr(fm->addr, cnt), - fm->max_sockets == 2); - if (media_id) { - ok_to_register = 0; - new_sock = tifm_alloc_device(fm, cnt); - if (new_sock) { - new_sock->addr = tifm_7xx1_sock_addr(fm->addr, - cnt); - new_sock->media_id = media_id; - switch (media_id) { - case 1: - card_name = "xd"; - break; - case 2: - card_name = "ms"; - break; - case 3: - card_name = "sd"; - break; - default: - break; - } - snprintf(new_sock->dev.bus_id, BUS_ID_SIZE, - "tifm_%s%u:%u", card_name, fm->id, cnt); - printk(KERN_INFO DRIVER_NAME - ": %s card detected in socket %d\n", - card_name, cnt); - spin_lock_irqsave(&fm->lock, flags); - if (!fm->sockets[cnt]) { - fm->sockets[cnt] = new_sock; - ok_to_register = 1; - } - spin_unlock_irqrestore(&fm->lock, flags); - if (!ok_to_register || - device_register(&new_sock->dev)) { - spin_lock_irqsave(&fm->lock, flags); - fm->sockets[cnt] = 0; - spin_unlock_irqrestore(&fm->lock, - flags); - tifm_free_device(&new_sock->dev); - } - } - } - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel((TIFM_IRQ_FIFOMASK | TIFM_IRQ_CARDMASK) << cnt, - fm->addr + FM_SET_INTERRUPT_ENABLE); - } - - writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); - class_device_put(&fm->cdev); -} - -static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) -{ - struct tifm_adapter *fm = pci_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&fm->lock, flags); - fm->inhibit_new_cards = 1; - fm->remove_mask = 0xf; - fm->insert_mask = 0; - writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - spin_unlock_irqrestore(&fm->lock, flags); - flush_workqueue(fm->wq); - - tifm_7xx1_remove_media(fm); - - pci_set_power_state(dev, PCI_D3hot); - pci_disable_device(dev); - pci_save_state(dev); - return 0; -} - -static int tifm_7xx1_resume(struct pci_dev *dev) -{ - struct tifm_adapter *fm = pci_get_drvdata(dev); - unsigned long flags; - - pci_restore_state(dev); - pci_enable_device(dev); - pci_set_power_state(dev, PCI_D0); - pci_set_master(dev); - - spin_lock_irqsave(&fm->lock, flags); - fm->inhibit_new_cards = 0; - writel(TIFM_IRQ_SETALL, fm->addr + FM_INTERRUPT_STATUS); - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, - fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->insert_mask = 0xf; - spin_unlock_irqrestore(&fm->lock, flags); - return 0; -} - -static int tifm_7xx1_probe(struct pci_dev *dev, - const struct pci_device_id *dev_id) -{ - struct tifm_adapter *fm; - int pci_dev_busy = 0; - int rc; - - rc = pci_set_dma_mask(dev, DMA_32BIT_MASK); - if (rc) - return rc; - - rc = pci_enable_device(dev); - if (rc) - return rc; - - pci_set_master(dev); - - rc = pci_request_regions(dev, DRIVER_NAME); - if (rc) { - pci_dev_busy = 1; - goto err_out; - } - - pci_intx(dev, 1); - - fm = tifm_alloc_adapter(); - if (!fm) { - rc = -ENOMEM; - goto err_out_int; - } - - fm->dev = &dev->dev; - fm->max_sockets = (dev->device == 0x803B) ? 2 : 4; - fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->max_sockets, - GFP_KERNEL); - if (!fm->sockets) - goto err_out_free; - - INIT_WORK(&fm->media_inserter, tifm_7xx1_insert_media, fm); - INIT_WORK(&fm->media_remover, tifm_7xx1_remove_media, fm); - fm->eject = tifm_7xx1_eject; - pci_set_drvdata(dev, fm); - - fm->addr = ioremap(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); - if (!fm->addr) - goto err_out_free; - - rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm); - if (rc) - goto err_out_unmap; - - rc = tifm_add_adapter(fm); - if (rc) - goto err_out_irq; - - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SETALLSOCK, - fm->addr + FM_SET_INTERRUPT_ENABLE); - - fm->insert_mask = 0xf; - - return 0; - -err_out_irq: - free_irq(dev->irq, fm); -err_out_unmap: - iounmap(fm->addr); -err_out_free: - pci_set_drvdata(dev, NULL); - tifm_free_adapter(fm); -err_out_int: - pci_intx(dev, 0); - pci_release_regions(dev); -err_out: - if (!pci_dev_busy) - pci_disable_device(dev); - return rc; -} - -static void tifm_7xx1_remove(struct pci_dev *dev) -{ - struct tifm_adapter *fm = pci_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&fm->lock, flags); - fm->inhibit_new_cards = 1; - fm->remove_mask = 0xf; - fm->insert_mask = 0; - writel(TIFM_IRQ_ENABLE, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - spin_unlock_irqrestore(&fm->lock, flags); - - flush_workqueue(fm->wq); - - tifm_7xx1_remove_media(fm); - - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - free_irq(dev->irq, fm); - - tifm_remove_adapter(fm); - - pci_set_drvdata(dev, 0); - - iounmap(fm->addr); - pci_intx(dev, 0); - pci_release_regions(dev); - - pci_disable_device(dev); - tifm_free_adapter(fm); -} - -static struct pci_device_id tifm_7xx1_pci_tbl [] = { - { PCI_VENDOR_ID_TI, 0x8033, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - 0 }, /* xx21 - the one I have */ - { PCI_VENDOR_ID_TI, 0x803B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - 0 }, /* xx12 - should be also supported */ - { } -}; - -static struct pci_driver tifm_7xx1_driver = { - .name = DRIVER_NAME, - .id_table = tifm_7xx1_pci_tbl, - .probe = tifm_7xx1_probe, - .remove = tifm_7xx1_remove, - .suspend = tifm_7xx1_suspend, - .resume = tifm_7xx1_resume, -}; - -static int __init tifm_7xx1_init(void) -{ - return pci_register_driver(&tifm_7xx1_driver); -} - -static void __exit tifm_7xx1_exit(void) -{ - pci_unregister_driver(&tifm_7xx1_driver); -} - -MODULE_AUTHOR("Alex Dubov"); -MODULE_DESCRIPTION("TI FlashMedia host driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl); -MODULE_VERSION(DRIVER_VERSION); - -module_init(tifm_7xx1_init); -module_exit(tifm_7xx1_exit); diff --git a/trunk/drivers/misc/tifm_core.c b/trunk/drivers/misc/tifm_core.c deleted file mode 100644 index cca5f8522469..000000000000 --- a/trunk/drivers/misc/tifm_core.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * tifm_core.c - TI FlashMedia driver - * - * Copyright (C) 2006 Alex Dubov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include -#include -#include - -#define DRIVER_NAME "tifm_core" -#define DRIVER_VERSION "0.6" - -static DEFINE_IDR(tifm_adapter_idr); -static DEFINE_SPINLOCK(tifm_adapter_lock); - -static tifm_media_id *tifm_device_match(tifm_media_id *ids, - struct tifm_dev *dev) -{ - while (*ids) { - if (dev->media_id == *ids) - return ids; - ids++; - } - return NULL; -} - -static int tifm_match(struct device *dev, struct device_driver *drv) -{ - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - struct tifm_driver *fm_drv; - - fm_drv = container_of(drv, struct tifm_driver, driver); - if (!fm_drv->id_table) - return -EINVAL; - if (tifm_device_match(fm_drv->id_table, fm_dev)) - return 1; - return -ENODEV; -} - -static int tifm_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) -{ - struct tifm_dev *fm_dev; - int i = 0; - int length = 0; - const char *card_type_name[] = {"INV", "SM", "MS", "SD"}; - - if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev))) - return -ENODEV; - if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id])) - return -ENOMEM; - - return 0; -} - -static struct bus_type tifm_bus_type = { - .name = "tifm", - .match = tifm_match, - .uevent = tifm_uevent, -}; - -static void tifm_free(struct class_device *cdev) -{ - struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); - - kfree(fm->sockets); - if (fm->wq) - destroy_workqueue(fm->wq); - kfree(fm); -} - -static struct class tifm_adapter_class = { - .name = "tifm_adapter", - .release = tifm_free -}; - -struct tifm_adapter *tifm_alloc_adapter(void) -{ - struct tifm_adapter *fm; - - fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL); - if (fm) { - fm->cdev.class = &tifm_adapter_class; - spin_lock_init(&fm->lock); - class_device_initialize(&fm->cdev); - } - return fm; -} -EXPORT_SYMBOL(tifm_alloc_adapter); - -void tifm_free_adapter(struct tifm_adapter *fm) -{ - class_device_put(&fm->cdev); -} -EXPORT_SYMBOL(tifm_free_adapter); - -int tifm_add_adapter(struct tifm_adapter *fm) -{ - int rc; - - if (!idr_pre_get(&tifm_adapter_idr, GFP_KERNEL)) - return -ENOMEM; - - spin_lock(&tifm_adapter_lock); - rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); - spin_unlock(&tifm_adapter_lock); - if (!rc) { - snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); - strncpy(fm->wq_name, fm->cdev.class_id, KOBJ_NAME_LEN); - - fm->wq = create_singlethread_workqueue(fm->wq_name); - if (fm->wq) - return class_device_add(&fm->cdev); - - spin_lock(&tifm_adapter_lock); - idr_remove(&tifm_adapter_idr, fm->id); - spin_unlock(&tifm_adapter_lock); - rc = -ENOMEM; - } - return rc; -} -EXPORT_SYMBOL(tifm_add_adapter); - -void tifm_remove_adapter(struct tifm_adapter *fm) -{ - class_device_del(&fm->cdev); - - spin_lock(&tifm_adapter_lock); - idr_remove(&tifm_adapter_idr, fm->id); - spin_unlock(&tifm_adapter_lock); -} -EXPORT_SYMBOL(tifm_remove_adapter); - -void tifm_free_device(struct device *dev) -{ - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - if (fm_dev->wq) - destroy_workqueue(fm_dev->wq); - kfree(fm_dev); -} -EXPORT_SYMBOL(tifm_free_device); - -struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id) -{ - struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); - - if (dev) { - spin_lock_init(&dev->lock); - snprintf(dev->wq_name, KOBJ_NAME_LEN, "tifm%u:%u", fm->id, id); - dev->wq = create_singlethread_workqueue(dev->wq_name); - if (!dev->wq) { - kfree(dev); - return 0; - } - dev->dev.parent = fm->dev; - dev->dev.bus = &tifm_bus_type; - dev->dev.release = tifm_free_device; - } - return dev; -} -EXPORT_SYMBOL(tifm_alloc_device); - -void tifm_eject(struct tifm_dev *sock) -{ - struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent); - fm->eject(fm, sock); -} -EXPORT_SYMBOL(tifm_eject); - -int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, - int direction) -{ - return pci_map_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); -} -EXPORT_SYMBOL(tifm_map_sg); - -void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, - int direction) -{ - pci_unmap_sg(to_pci_dev(sock->dev.parent), sg, nents, direction); -} -EXPORT_SYMBOL(tifm_unmap_sg); - -static int tifm_device_probe(struct device *dev) -{ - struct tifm_driver *drv; - struct tifm_dev *fm_dev; - int rc = 0; - const tifm_media_id *id; - - drv = container_of(dev->driver, struct tifm_driver, driver); - fm_dev = container_of(dev, struct tifm_dev, dev); - get_device(dev); - if (!fm_dev->drv && drv->probe && drv->id_table) { - rc = -ENODEV; - id = tifm_device_match(drv->id_table, fm_dev); - if (id) - rc = drv->probe(fm_dev); - if (rc >= 0) { - rc = 0; - fm_dev->drv = drv; - } - } - if (rc) - put_device(dev); - return rc; -} - -static int tifm_device_remove(struct device *dev) -{ - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - struct tifm_driver *drv = fm_dev->drv; - - if (drv) { - if (drv->remove) drv->remove(fm_dev); - fm_dev->drv = 0; - } - - put_device(dev); - return 0; -} - -int tifm_register_driver(struct tifm_driver *drv) -{ - drv->driver.bus = &tifm_bus_type; - drv->driver.probe = tifm_device_probe; - drv->driver.remove = tifm_device_remove; - - return driver_register(&drv->driver); -} -EXPORT_SYMBOL(tifm_register_driver); - -void tifm_unregister_driver(struct tifm_driver *drv) -{ - driver_unregister(&drv->driver); -} -EXPORT_SYMBOL(tifm_unregister_driver); - -static int __init tifm_init(void) -{ - int rc = bus_register(&tifm_bus_type); - - if (!rc) { - rc = class_register(&tifm_adapter_class); - if (rc) - bus_unregister(&tifm_bus_type); - } - - return rc; -} - -static void __exit tifm_exit(void) -{ - class_unregister(&tifm_adapter_class); - bus_unregister(&tifm_bus_type); -} - -subsys_initcall(tifm_init); -module_exit(tifm_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alex Dubov"); -MODULE_DESCRIPTION("TI FlashMedia core driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); diff --git a/trunk/drivers/mmc/Kconfig b/trunk/drivers/mmc/Kconfig index ea41852ec8cd..f540bd88dc5a 100644 --- a/trunk/drivers/mmc/Kconfig +++ b/trunk/drivers/mmc/Kconfig @@ -109,20 +109,4 @@ config MMC_IMX If unsure, say N. -config MMC_TIFM_SD - tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)" - depends on MMC && EXPERIMENTAL - select TIFM_CORE - help - Say Y here if you want to be able to access MMC/SD cards with - the Texas Instruments(R) Flash Media card reader, found in many - laptops. - This option 'selects' (turns on, enables) 'TIFM_CORE', but you - probably also need appropriate card reader host adapter, such as - 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support - (TIFM_7XX1)'. - - To compile this driver as a module, choose M here: the - module will be called tifm_sd. - endmenu diff --git a/trunk/drivers/mmc/Makefile b/trunk/drivers/mmc/Makefile index acfd4de0aba5..b1f6e03e7aa9 100644 --- a/trunk/drivers/mmc/Makefile +++ b/trunk/drivers/mmc/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o -obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o mmc_core-y := mmc.o mmc_sysfs.o mmc_core-$(CONFIG_BLOCK) += mmc_queue.o diff --git a/trunk/drivers/mmc/mmc.c b/trunk/drivers/mmc/mmc.c index ee8863c123e3..5b9caa7978d3 100644 --- a/trunk/drivers/mmc/mmc.c +++ b/trunk/drivers/mmc/mmc.c @@ -1166,9 +1166,9 @@ static void mmc_setup(struct mmc_host *host) void mmc_detect_change(struct mmc_host *host, unsigned long delay) { if (delay) - mmc_schedule_delayed_work(&host->detect, delay); + schedule_delayed_work(&host->detect, delay); else - mmc_schedule_work(&host->detect); + schedule_work(&host->detect); } EXPORT_SYMBOL(mmc_detect_change); @@ -1311,7 +1311,7 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { - mmc_flush_scheduled_work(); + flush_scheduled_work(); mmc_free_host_sysfs(host); } diff --git a/trunk/drivers/mmc/mmc.h b/trunk/drivers/mmc/mmc.h index cd5e0ab3d84b..97bae00292fa 100644 --- a/trunk/drivers/mmc/mmc.h +++ b/trunk/drivers/mmc/mmc.h @@ -18,8 +18,4 @@ struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); int mmc_add_host_sysfs(struct mmc_host *host); void mmc_remove_host_sysfs(struct mmc_host *host); void mmc_free_host_sysfs(struct mmc_host *host); - -int mmc_schedule_work(struct work_struct *work); -int mmc_schedule_delayed_work(struct work_struct *work, unsigned long delay); -void mmc_flush_scheduled_work(void); #endif diff --git a/trunk/drivers/mmc/mmc_block.c b/trunk/drivers/mmc/mmc_block.c index c1293f1bda87..db0e8ad439a5 100644 --- a/trunk/drivers/mmc/mmc_block.c +++ b/trunk/drivers/mmc/mmc_block.c @@ -158,13 +158,13 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; - struct mmc_blk_request brq; int ret; if (mmc_card_claim_host(card)) goto cmd_err; do { + struct mmc_blk_request brq; struct mmc_command cmd; u32 readcmd, writecmd; @@ -278,27 +278,17 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) cmd_err: mmc_card_release_host(card); - ret = 1; - /* - * For writes and where the host claims to support proper - * error reporting, we first ok the successful blocks. - * - * For reads we just fail the entire chunk as that should - * be safe in all cases. + * This is a little draconian, but until we get proper + * error handling sorted out here, its the best we can + * do - especially as some hosts have no idea how much + * data was transferred before the error occurred. */ - if (rq_data_dir(req) != READ && - (card->host->caps & MMC_CAP_MULTIWRITE)) { - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); - spin_unlock_irq(&md->lock); - } - spin_lock_irq(&md->lock); - while (ret) { + do { ret = end_that_request_chunk(req, 0, req->current_nr_sectors << 9); - } + } while (ret); add_disk_randomness(req->rq_disk); blkdev_dequeue_request(req); diff --git a/trunk/drivers/mmc/mmc_sysfs.c b/trunk/drivers/mmc/mmc_sysfs.c index 10cc9734eaa0..a2a35fd946ee 100644 --- a/trunk/drivers/mmc/mmc_sysfs.c +++ b/trunk/drivers/mmc/mmc_sysfs.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -318,41 +317,10 @@ void mmc_free_host_sysfs(struct mmc_host *host) class_device_put(&host->class_dev); } -static struct workqueue_struct *workqueue; - -/* - * Internal function. Schedule work in the MMC work queue. - */ -int mmc_schedule_work(struct work_struct *work) -{ - return queue_work(workqueue, work); -} - -/* - * Internal function. Schedule delayed work in the MMC work queue. - */ -int mmc_schedule_delayed_work(struct work_struct *work, unsigned long delay) -{ - return queue_delayed_work(workqueue, work, delay); -} - -/* - * Internal function. Flush all scheduled work from the MMC work queue. - */ -void mmc_flush_scheduled_work(void) -{ - flush_workqueue(workqueue); -} static int __init mmc_init(void) { - int ret; - - workqueue = create_singlethread_workqueue("kmmcd"); - if (!workqueue) - return -ENOMEM; - - ret = bus_register(&mmc_bus_type); + int ret = bus_register(&mmc_bus_type); if (ret == 0) { ret = class_register(&mmc_host_class); if (ret) @@ -365,7 +333,6 @@ static void __exit mmc_exit(void) { class_unregister(&mmc_host_class); bus_unregister(&mmc_bus_type); - destroy_workqueue(workqueue); } module_init(mmc_init); diff --git a/trunk/drivers/mmc/sdhci.c b/trunk/drivers/mmc/sdhci.c index 20711acb0120..4dab5ec392ea 100644 --- a/trunk/drivers/mmc/sdhci.c +++ b/trunk/drivers/mmc/sdhci.c @@ -35,8 +35,6 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) #define SDHCI_QUIRK_FORCE_DMA (1<<1) -/* Controller doesn't like some resets when there is no card inserted. */ -#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -53,8 +51,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .device = PCI_DEVICE_ID_RICOH_R5C822, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_FORCE_DMA | - SDHCI_QUIRK_NO_CARD_NO_RESET, + .driver_data = SDHCI_QUIRK_FORCE_DMA, }, { @@ -128,12 +125,6 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; - if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT)) - return; - } - writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); if (mask & SDHCI_RESET_ALL) @@ -726,7 +717,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) } else sdhci_send_command(host, mrq->cmd); - mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -763,7 +753,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl &= ~SDHCI_CTRL_4BITBUS; writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -871,7 +860,6 @@ static void sdhci_tasklet_finish(unsigned long param) sdhci_deactivate_led(host); - mmiowb(); spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); @@ -905,7 +893,6 @@ static void sdhci_timeout_timer(unsigned long data) } } - mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -1043,7 +1030,6 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id, struct pt_regs *regs) result = IRQ_HANDLED; - mmiowb(); out: spin_unlock(&host->lock); @@ -1109,7 +1095,6 @@ static int sdhci_resume (struct pci_dev *pdev) if (chip->hosts[i]->flags & SDHCI_USE_DMA) pci_set_master(pdev); sdhci_init(chip->hosts[i]); - mmiowb(); ret = mmc_resume_host(chip->hosts[i]->mmc); if (ret) return ret; @@ -1183,9 +1168,6 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) host = mmc_priv(mmc); host->mmc = mmc; - host->chip = chip; - chip->hosts[slot] = host; - host->bar = first_bar + slot; host->addr = pci_resource_start(pdev, host->bar); @@ -1342,7 +1324,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) sdhci_dumpregs(host); #endif - mmiowb(); + host->chip = chip; + chip->hosts[slot] = host; mmc_add_host(mmc); diff --git a/trunk/drivers/mmc/tifm_sd.c b/trunk/drivers/mmc/tifm_sd.c deleted file mode 100644 index 6d23dc08d169..000000000000 --- a/trunk/drivers/mmc/tifm_sd.c +++ /dev/null @@ -1,933 +0,0 @@ -/* - * tifm_sd.c - TI FlashMedia driver - * - * Copyright (C) 2006 Alex Dubov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - - -#include -#include -#include -#include - -#define DRIVER_NAME "tifm_sd" -#define DRIVER_VERSION "0.6" - -static int no_dma = 0; -static int fixed_timeout = 0; -module_param(no_dma, bool, 0644); -module_param(fixed_timeout, bool, 0644); - -/* Constants here are mostly from OMAP5912 datasheet */ -#define TIFM_MMCSD_RESET 0x0002 -#define TIFM_MMCSD_CLKMASK 0x03ff -#define TIFM_MMCSD_POWER 0x0800 -#define TIFM_MMCSD_4BBUS 0x8000 -#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */ -#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */ -#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */ -#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */ -#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ -#define TIFM_MMCSD_READ 0x8000 - -#define TIFM_MMCSD_DATAMASK 0x001d /* set bits: EOFB, BRS, CB, EOC */ -#define TIFM_MMCSD_ERRMASK 0x41e0 /* set bits: CERR, CCRC, CTO, DCRC, DTO */ -#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ -#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ -#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ -#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ -#define TIFM_MMCSD_DTO 0x0020 /* data time-out */ -#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */ -#define TIFM_MMCSD_CTO 0x0080 /* command time-out */ -#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ -#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ -#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ -#define TIFM_MMCSD_CERR 0x4000 /* card status error */ - -#define TIFM_MMCSD_FIFO_SIZE 0x0020 - -#define TIFM_MMCSD_RSP_R0 0x0000 -#define TIFM_MMCSD_RSP_R1 0x0100 -#define TIFM_MMCSD_RSP_R2 0x0200 -#define TIFM_MMCSD_RSP_R3 0x0300 -#define TIFM_MMCSD_RSP_R4 0x0400 -#define TIFM_MMCSD_RSP_R5 0x0500 -#define TIFM_MMCSD_RSP_R6 0x0600 - -#define TIFM_MMCSD_RSP_BUSY 0x0800 - -#define TIFM_MMCSD_CMD_BC 0x0000 -#define TIFM_MMCSD_CMD_BCR 0x1000 -#define TIFM_MMCSD_CMD_AC 0x2000 -#define TIFM_MMCSD_CMD_ADTC 0x3000 - -typedef enum { - IDLE = 0, - CMD, /* main command ended */ - BRS, /* block transfer finished */ - SCMD, /* stop command ended */ - CARD, /* card left busy state */ - FIFO, /* FIFO operation completed (uncertain) */ - READY -} card_state_t; - -enum { - FIFO_RDY = 0x0001, /* hardware dependent value */ - HOST_REG = 0x0002, - EJECT = 0x0004, - EJECT_DONE = 0x0008, - CARD_BUSY = 0x0010, - OPENDRAIN = 0x0040, /* hardware dependent value */ - CARD_EVENT = 0x0100, /* hardware dependent value */ - CARD_RO = 0x0200, /* hardware dependent value */ - FIFO_EVENT = 0x10000 }; /* hardware dependent value */ - -struct tifm_sd { - struct tifm_dev *dev; - - unsigned int flags; - card_state_t state; - unsigned int clk_freq; - unsigned int clk_div; - unsigned long timeout_jiffies; // software timeout - 2 sec - - struct mmc_request *req; - struct work_struct cmd_handler; - struct work_struct abort_handler; - wait_queue_head_t can_eject; - - size_t written_blocks; - char *buffer; - size_t buffer_size; - size_t buffer_pos; - -}; - -static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, - unsigned int host_status) -{ - struct mmc_command *cmd = host->req->cmd; - unsigned int t_val = 0, cnt = 0; - - if (host_status & TIFM_MMCSD_BRS) { - /* in non-dma rx mode BRS fires when fifo is still not empty */ - if (host->buffer && (cmd->data->flags & MMC_DATA_READ)) { - while (host->buffer_size > host->buffer_pos) { - t_val = readl(sock->addr + SOCK_MMCSD_DATA); - host->buffer[host->buffer_pos++] = t_val & 0xff; - host->buffer[host->buffer_pos++] = - (t_val >> 8) & 0xff; - } - } - return 1; - } else if (host->buffer) { - if ((cmd->data->flags & MMC_DATA_READ) && - (host_status & TIFM_MMCSD_AF)) { - for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { - t_val = readl(sock->addr + SOCK_MMCSD_DATA); - if (host->buffer_size > host->buffer_pos) { - host->buffer[host->buffer_pos++] = - t_val & 0xff; - host->buffer[host->buffer_pos++] = - (t_val >> 8) & 0xff; - } - } - } else if ((cmd->data->flags & MMC_DATA_WRITE) - && (host_status & TIFM_MMCSD_AE)) { - for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { - if (host->buffer_size > host->buffer_pos) { - t_val = host->buffer[host->buffer_pos++] & 0x00ff; - t_val |= ((host->buffer[host->buffer_pos++]) << 8) - & 0xff00; - writel(t_val, - sock->addr + SOCK_MMCSD_DATA); - } - } - } - } - return 0; -} - -static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) -{ - unsigned int rc = 0; - - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - rc |= TIFM_MMCSD_RSP_R0; - break; - case MMC_RSP_R1B: - rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through - case MMC_RSP_R1: - rc |= TIFM_MMCSD_RSP_R1; - break; - case MMC_RSP_R2: - rc |= TIFM_MMCSD_RSP_R2; - break; - case MMC_RSP_R3: - rc |= TIFM_MMCSD_RSP_R3; - break; - case MMC_RSP_R6: - rc |= TIFM_MMCSD_RSP_R6; - break; - default: - BUG(); - } - - switch (mmc_cmd_type(cmd)) { - case MMC_CMD_BC: - rc |= TIFM_MMCSD_CMD_BC; - break; - case MMC_CMD_BCR: - rc |= TIFM_MMCSD_CMD_BCR; - break; - case MMC_CMD_AC: - rc |= TIFM_MMCSD_CMD_AC; - break; - case MMC_CMD_ADTC: - rc |= TIFM_MMCSD_CMD_ADTC; - break; - default: - BUG(); - } - return rc; -} - -static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) -{ - struct tifm_dev *sock = host->dev; - unsigned int cmd_mask = tifm_sd_op_flags(cmd) | - (host->flags & OPENDRAIN); - - if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) - cmd_mask |= TIFM_MMCSD_READ; - - dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", - cmd->opcode, cmd->arg, cmd_mask); - - writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); - writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); - writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); -} - -static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) -{ - cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); - cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); - cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); - cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); -} - -static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, - unsigned int host_status) -{ - struct mmc_command *cmd = host->req->cmd; - -change_state: - switch (host->state) { - case IDLE: - return; - case CMD: - if (host_status & TIFM_MMCSD_EOC) { - tifm_sd_fetch_resp(cmd, sock); - if (cmd->data) { - host->state = BRS; - } else - host->state = READY; - goto change_state; - } - break; - case BRS: - if (tifm_sd_transfer_data(sock, host, host_status)) { - if (!host->req->stop) { - if (cmd->data->flags & MMC_DATA_WRITE) { - host->state = CARD; - } else { - host->state = - host->buffer ? READY : FIFO; - } - goto change_state; - } - tifm_sd_exec(host, host->req->stop); - host->state = SCMD; - } - break; - case SCMD: - if (host_status & TIFM_MMCSD_EOC) { - tifm_sd_fetch_resp(host->req->stop, sock); - if (cmd->error) { - host->state = READY; - } else if (cmd->data->flags & MMC_DATA_WRITE) { - host->state = CARD; - } else { - host->state = host->buffer ? READY : FIFO; - } - goto change_state; - } - break; - case CARD: - if (!(host->flags & CARD_BUSY) - && (host->written_blocks == cmd->data->blocks)) { - host->state = host->buffer ? READY : FIFO; - goto change_state; - } - break; - case FIFO: - if (host->flags & FIFO_RDY) { - host->state = READY; - host->flags &= ~FIFO_RDY; - goto change_state; - } - break; - case READY: - queue_work(sock->wq, &host->cmd_handler); - return; - } - - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); -} - -/* Called from interrupt handler */ -static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, - unsigned int sock_irq_status) -{ - struct tifm_sd *host; - unsigned int host_status = 0, fifo_status = 0; - int error_code = 0; - - spin_lock(&sock->lock); - host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - cancel_delayed_work(&host->abort_handler); - - if (sock_irq_status & FIFO_EVENT) { - fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); - writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); - - host->flags |= fifo_status & FIFO_RDY; - } - - if (sock_irq_status & CARD_EVENT) { - host_status = readl(sock->addr + SOCK_MMCSD_STATUS); - writel(host_status, sock->addr + SOCK_MMCSD_STATUS); - - if (!(host->flags & HOST_REG)) - queue_work(sock->wq, &host->cmd_handler); - if (!host->req) - goto done; - - if (host_status & TIFM_MMCSD_ERRMASK) { - if (host_status & TIFM_MMCSD_CERR) - error_code = MMC_ERR_FAILED; - else if (host_status & - (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) - error_code = MMC_ERR_TIMEOUT; - else if (host_status & - (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) - error_code = MMC_ERR_BADCRC; - - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); - - if (host->req->stop) { - if (host->state == SCMD) { - host->req->stop->error = error_code; - } else if(host->state == BRS) { - host->req->cmd->error = error_code; - tifm_sd_exec(host, host->req->stop); - queue_delayed_work(sock->wq, - &host->abort_handler, - host->timeout_jiffies); - host->state = SCMD; - goto done; - } else { - host->req->cmd->error = error_code; - } - } else { - host->req->cmd->error = error_code; - } - host->state = READY; - } - - if (host_status & TIFM_MMCSD_CB) - host->flags |= CARD_BUSY; - if ((host_status & TIFM_MMCSD_EOFB) && - (host->flags & CARD_BUSY)) { - host->written_blocks++; - host->flags &= ~CARD_BUSY; - } - } - - if (host->req) - tifm_sd_process_cmd(sock, host, host_status); -done: - dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", - host_status, fifo_status); - spin_unlock(&sock->lock); - return sock_irq_status; -} - -static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) -{ - struct tifm_dev *sock = card->dev; - unsigned int dest_cnt; - - /* DMA style IO */ - - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(long_log2(cmd->data->blksz) - 2, - sock->addr + SOCK_FIFO_PAGE_SIZE); - writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); - writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); - - dest_cnt = (cmd->data->blocks) << 8; - - writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS); - - writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(cmd->data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); - - if (cmd->data->flags & MMC_DATA_WRITE) { - writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN, - sock->addr + SOCK_DMA_CONTROL); - } else { - writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); - } -} - -static void tifm_sd_set_data_timeout(struct tifm_sd *host, - struct mmc_data *data) -{ - struct tifm_dev *sock = host->dev; - unsigned int data_timeout = data->timeout_clks; - - if (fixed_timeout) - return; - - data_timeout += data->timeout_ns / - ((1000000000 / host->clk_freq) * host->clk_div); - data_timeout *= 10; // call it fudge factor for now - - if (data_timeout < 0xffff) { - writel((~TIFM_MMCSD_DPE) & - readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), - sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); - writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); - } else { - writel(TIFM_MMCSD_DPE | - readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), - sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); - data_timeout = (data_timeout >> 10) + 1; - if(data_timeout > 0xffff) - data_timeout = 0; /* set to unlimited */ - writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); - } -} - -static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned long flags; - int sg_count = 0; - struct mmc_data *r_data = mrq->cmd->data; - - spin_lock_irqsave(&sock->lock, flags); - if (host->flags & EJECT) { - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (host->req) { - printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (r_data) { - tifm_sd_set_data_timeout(host, r_data); - - sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, - mrq->cmd->flags & MMC_DATA_WRITE - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - if (sg_count != 1) { - printk(KERN_ERR DRIVER_NAME - ": scatterlist map failed\n"); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - host->written_blocks = 0; - host->flags &= ~CARD_BUSY; - tifm_sd_prepare_data(host, mrq->cmd); - } - - host->req = mrq; - host->state = CMD; - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); - writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - tifm_sd_exec(host, mrq->cmd); - spin_unlock_irqrestore(&sock->lock, flags); - return; - -err_out: - if (sg_count > 0) - tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, - (r_data->flags & MMC_DATA_WRITE) - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - mrq->cmd->error = MMC_ERR_TIMEOUT; - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_end_cmd(void *data) -{ - struct tifm_sd *host = data; - struct tifm_dev *sock = host->dev; - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct mmc_request *mrq; - struct mmc_data *r_data = 0; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - mrq = host->req; - host->req = 0; - host->state = IDLE; - - if (!mrq) { - printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); - spin_unlock_irqrestore(&sock->lock, flags); - return; - } - - r_data = mrq->cmd->data; - if (r_data) { - if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks * - r_data->blksz; - } else { - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; - } - tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, - (r_data->flags & MMC_DATA_WRITE) - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - } - - writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - - spin_unlock_irqrestore(&sock->lock, flags); - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned long flags; - struct mmc_data *r_data = mrq->cmd->data; - char *t_buffer = 0; - - if (r_data) { - t_buffer = kmap(r_data->sg->page); - if (!t_buffer) { - printk(KERN_ERR DRIVER_NAME ": kmap failed\n"); - goto err_out; - } - } - - spin_lock_irqsave(&sock->lock, flags); - if (host->flags & EJECT) { - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (host->req) { - printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (r_data) { - tifm_sd_set_data_timeout(host, r_data); - - host->buffer = t_buffer + r_data->sg->offset; - host->buffer_size = mrq->cmd->data->blocks * - mrq->cmd->data->blksz; - - writel(TIFM_MMCSD_BUFINT | - readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | - (TIFM_MMCSD_FIFO_SIZE - 1), - sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - - host->written_blocks = 0; - host->flags &= ~CARD_BUSY; - host->buffer_pos = 0; - writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); - } - - host->req = mrq; - host->state = CMD; - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); - writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - tifm_sd_exec(host, mrq->cmd); - spin_unlock_irqrestore(&sock->lock, flags); - return; - -err_out: - if (t_buffer) - kunmap(r_data->sg->page); - - mrq->cmd->error = MMC_ERR_TIMEOUT; - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_end_cmd_nodma(void *data) -{ - struct tifm_sd *host = (struct tifm_sd*)data; - struct tifm_dev *sock = host->dev; - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct mmc_request *mrq; - struct mmc_data *r_data = 0; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - mrq = host->req; - host->req = 0; - host->state = IDLE; - - if (!mrq) { - printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); - spin_unlock_irqrestore(&sock->lock, flags); - return; - } - - r_data = mrq->cmd->data; - if (r_data) { - writel((~TIFM_MMCSD_BUFINT) & - readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - - if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks * - r_data->blksz; - } else { - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; - } - host->buffer = 0; - host->buffer_pos = 0; - host->buffer_size = 0; - } - - writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - - spin_unlock_irqrestore(&sock->lock, flags); - - if (r_data) - kunmap(r_data->sg->page); - - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_abort(void *data) -{ - printk(KERN_ERR DRIVER_NAME - ": card failed to respond for a long period of time"); - tifm_eject(((struct tifm_sd*)data)->dev); -} - -static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned int clk_div1, clk_div2; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - dev_dbg(&sock->dev, "Setting bus width %d, power %d\n", ios->bus_width, - ios->power_mode); - if (ios->bus_width == MMC_BUS_WIDTH_4) { - writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), - sock->addr + SOCK_MMCSD_CONFIG); - } else { - writel((~TIFM_MMCSD_4BBUS) & - readl(sock->addr + SOCK_MMCSD_CONFIG), - sock->addr + SOCK_MMCSD_CONFIG); - } - - if (ios->clock) { - clk_div1 = 20000000 / ios->clock; - if (!clk_div1) - clk_div1 = 1; - - clk_div2 = 24000000 / ios->clock; - if (!clk_div2) - clk_div2 = 1; - - if ((20000000 / clk_div1) > ios->clock) - clk_div1++; - if ((24000000 / clk_div2) > ios->clock) - clk_div2++; - if ((20000000 / clk_div1) > (24000000 / clk_div2)) { - host->clk_freq = 20000000; - host->clk_div = clk_div1; - writel((~TIFM_CTRL_FAST_CLK) & - readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - } else { - host->clk_freq = 24000000; - host->clk_div = clk_div2; - writel(TIFM_CTRL_FAST_CLK | - readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - } - } else { - host->clk_div = 0; - } - host->clk_div &= TIFM_MMCSD_CLKMASK; - writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & - readl(sock->addr + SOCK_MMCSD_CONFIG)), - sock->addr + SOCK_MMCSD_CONFIG); - - if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) - host->flags |= OPENDRAIN; - else - host->flags &= ~OPENDRAIN; - - /* chip_select : maybe later */ - //vdd - //power is set before probe / after remove - //I believe, power_off when already marked for eject is sufficient to - // allow removal. - if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) { - host->flags |= EJECT_DONE; - wake_up_all(&host->can_eject); - } - - spin_unlock_irqrestore(&sock->lock, flags); -} - -static int tifm_sd_ro(struct mmc_host *mmc) -{ - int rc; - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - host->flags |= (CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)); - rc = (host->flags & CARD_RO) ? 1 : 0; - - spin_unlock_irqrestore(&sock->lock, flags); - return rc; -} - -static struct mmc_host_ops tifm_sd_ops = { - .request = tifm_sd_request, - .set_ios = tifm_sd_ios, - .get_ro = tifm_sd_ro -}; - -static void tifm_sd_register_host(void *data) -{ - struct tifm_sd *host = (struct tifm_sd*)data; - struct tifm_dev *sock = host->dev; - struct mmc_host *mmc = tifm_get_drvdata(sock); - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - host->flags |= HOST_REG; - PREPARE_WORK(&host->cmd_handler, - no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, - data); - spin_unlock_irqrestore(&sock->lock, flags); - dev_dbg(&sock->dev, "adding host\n"); - mmc_add_host(mmc); -} - -static int tifm_sd_probe(struct tifm_dev *sock) -{ - struct mmc_host *mmc; - struct tifm_sd *host; - int rc = -EIO; - - if (!(TIFM_SOCK_STATE_OCCUPIED & - readl(sock->addr + SOCK_PRESENT_STATE))) { - printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n"); - return rc; - } - - mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->dev = sock; - host->clk_div = 61; - init_waitqueue_head(&host->can_eject); - INIT_WORK(&host->cmd_handler, tifm_sd_register_host, host); - INIT_WORK(&host->abort_handler, tifm_sd_abort, host); - - tifm_set_drvdata(sock, mmc); - sock->signal_irq = tifm_sd_signal_irq; - - host->clk_freq = 20000000; - host->timeout_jiffies = msecs_to_jiffies(1000); - - tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request; - mmc->ops = &tifm_sd_ops; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA; - mmc->f_min = 20000000 / 60; - mmc->f_max = 24000000; - mmc->max_hw_segs = 1; - mmc->max_phys_segs = 1; - mmc->max_sectors = 127; - mmc->max_seg_size = mmc->max_sectors << 11; //2k maximum hw block length - - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); - - for (rc = 0; rc < 50; rc++) { - /* Wait for reset ack */ - if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { - rc = 0; - break; - } - msleep(10); - } - - if (rc) { - printk(KERN_ERR DRIVER_NAME - ": card not ready - probe failed\n"); - mmc_free_host(mmc); - return -ENODEV; - } - - writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); - writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, - sock->addr + SOCK_MMCSD_INT_ENABLE); - - writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); // command timeout 64 clocks for now - writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); - - queue_delayed_work(sock->wq, &host->abort_handler, - host->timeout_jiffies); - - return 0; -} - -static int tifm_sd_host_is_down(struct tifm_dev *sock) -{ - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&sock->lock, flags); - rc = (host->flags & EJECT_DONE); - spin_unlock_irqrestore(&sock->lock, flags); - return rc; -} - -static void tifm_sd_remove(struct tifm_dev *sock) -{ - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - host->flags |= EJECT; - if (host->req) - queue_work(sock->wq, &host->cmd_handler); - spin_unlock_irqrestore(&sock->lock, flags); - wait_event_timeout(host->can_eject, tifm_sd_host_is_down(sock), - host->timeout_jiffies); - - if (host->flags & HOST_REG) - mmc_remove_host(mmc); - - /* The meaning of the bit majority in this constant is unknown. */ - writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); - - tifm_set_drvdata(sock, 0); - mmc_free_host(mmc); -} - -static tifm_media_id tifm_sd_id_tbl[] = { - FM_SD, 0 -}; - -static struct tifm_driver tifm_sd_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE - }, - .id_table = tifm_sd_id_tbl, - .probe = tifm_sd_probe, - .remove = tifm_sd_remove -}; - -static int __init tifm_sd_init(void) -{ - return tifm_register_driver(&tifm_sd_driver); -} - -static void __exit tifm_sd_exit(void) -{ - tifm_unregister_driver(&tifm_sd_driver); -} - -MODULE_AUTHOR("Alex Dubov"); -MODULE_DESCRIPTION("TI FlashMedia SD driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); -MODULE_VERSION(DRIVER_VERSION); - -module_init(tifm_sd_init); -module_exit(tifm_sd_exit); diff --git a/trunk/drivers/net/arm/ep93xx_eth.c b/trunk/drivers/net/arm/ep93xx_eth.c index cef00744a9dc..d231efa624d4 100644 --- a/trunk/drivers/net/arm/ep93xx_eth.c +++ b/trunk/drivers/net/arm/ep93xx_eth.c @@ -9,7 +9,6 @@ * (at your option) any later version. */ -#include #include #include #include diff --git a/trunk/drivers/net/fs_enet/mii-fec.c b/trunk/drivers/net/fs_enet/mii-fec.c index 1328e10caa35..baaae3dbf2e6 100644 --- a/trunk/drivers/net/fs_enet/mii-fec.c +++ b/trunk/drivers/net/fs_enet/mii-fec.c @@ -12,8 +12,6 @@ * kind, whether express or implied. */ - -#include #include #include #include diff --git a/trunk/drivers/net/pcnet32.c b/trunk/drivers/net/pcnet32.c index 21dc68eff514..a43e24245b7e 100644 --- a/trunk/drivers/net/pcnet32.c +++ b/trunk/drivers/net/pcnet32.c @@ -21,8 +21,6 @@ * *************************************************************************/ -#include - #define DRV_NAME "pcnet32" #ifdef CONFIG_PCNET32_NAPI #define DRV_VERSION "1.33-NAPI" diff --git a/trunk/drivers/net/phy/fixed.c b/trunk/drivers/net/phy/fixed.c index 94b47c8d0ab4..f14e99276dba 100644 --- a/trunk/drivers/net/phy/fixed.c +++ b/trunk/drivers/net/phy/fixed.c @@ -13,7 +13,6 @@ * option) any later version. * */ -#include #include #include #include diff --git a/trunk/drivers/net/ucc_geth_phy.c b/trunk/drivers/net/ucc_geth_phy.c index f91028c5386d..67260eb3188a 100644 --- a/trunk/drivers/net/ucc_geth_phy.c +++ b/trunk/drivers/net/ucc_geth_phy.c @@ -17,7 +17,6 @@ * */ -#include #include #include #include diff --git a/trunk/drivers/parisc/iosapic.c b/trunk/drivers/parisc/iosapic.c index c2949b4367e5..1fbda77cefc2 100644 --- a/trunk/drivers/parisc/iosapic.c +++ b/trunk/drivers/parisc/iosapic.c @@ -146,7 +146,7 @@ #include #endif -#include +#include #include "./iosapic_private.h" #define MODULE_NAME "iosapic" @@ -692,7 +692,6 @@ static void iosapic_end_irq(unsigned int irq) DBG(KERN_DEBUG "end_irq(%d): eoi(%p, 0x%x)\n", irq, vi->eoi_addr, vi->eoi_data); iosapic_eoi(vi->eoi_addr, vi->eoi_data); - cpu_end_irq(irq); } static unsigned int iosapic_startup_irq(unsigned int irq) @@ -729,7 +728,7 @@ static struct hw_interrupt_type iosapic_interrupt_type = { .shutdown = iosapic_disable_irq, .enable = iosapic_enable_irq, .disable = iosapic_disable_irq, - .ack = cpu_ack_irq, + .ack = no_ack_irq, .end = iosapic_end_irq, #ifdef CONFIG_SMP .set_affinity = iosapic_set_affinity_irq, diff --git a/trunk/drivers/parisc/lba_pci.c b/trunk/drivers/parisc/lba_pci.c index ba6769934c77..3fe4a77fa16a 100644 --- a/trunk/drivers/parisc/lba_pci.c +++ b/trunk/drivers/parisc/lba_pci.c @@ -46,9 +46,9 @@ #include #include -#include #include /* for register_parisc_driver() stuff */ #include +#include /* for iosapic_register() */ #include /* read/write stuff */ #undef DEBUG_LBA /* general stuff */ @@ -100,10 +100,113 @@ #define MODULE_NAME "LBA" +#define LBA_FUNC_ID 0x0000 /* function id */ +#define LBA_FCLASS 0x0008 /* function class, bist, header, rev... */ +#define LBA_CAPABLE 0x0030 /* capabilities register */ + +#define LBA_PCI_CFG_ADDR 0x0040 /* poke CFG address here */ +#define LBA_PCI_CFG_DATA 0x0048 /* read or write data here */ + +#define LBA_PMC_MTLT 0x0050 /* Firmware sets this - read only. */ +#define LBA_FW_SCRATCH 0x0058 /* Firmware writes the PCI bus number here. */ +#define LBA_ERROR_ADDR 0x0070 /* On error, address gets logged here */ + +#define LBA_ARB_MASK 0x0080 /* bit 0 enable arbitration. PAT/PDC enables */ +#define LBA_ARB_PRI 0x0088 /* firmware sets this. */ +#define LBA_ARB_MODE 0x0090 /* firmware sets this. */ +#define LBA_ARB_MTLT 0x0098 /* firmware sets this. */ + +#define LBA_MOD_ID 0x0100 /* Module ID. PDC_PAT_CELL reports 4 */ + +#define LBA_STAT_CTL 0x0108 /* Status & Control */ +#define LBA_BUS_RESET 0x01 /* Deassert PCI Bus Reset Signal */ +#define CLEAR_ERRLOG 0x10 /* "Clear Error Log" cmd */ +#define CLEAR_ERRLOG_ENABLE 0x20 /* "Clear Error Log" Enable */ +#define HF_ENABLE 0x40 /* enable HF mode (default is -1 mode) */ + +#define LBA_LMMIO_BASE 0x0200 /* < 4GB I/O address range */ +#define LBA_LMMIO_MASK 0x0208 + +#define LBA_GMMIO_BASE 0x0210 /* > 4GB I/O address range */ +#define LBA_GMMIO_MASK 0x0218 + +#define LBA_WLMMIO_BASE 0x0220 /* All < 4GB ranges under the same *SBA* */ +#define LBA_WLMMIO_MASK 0x0228 + +#define LBA_WGMMIO_BASE 0x0230 /* All > 4GB ranges under the same *SBA* */ +#define LBA_WGMMIO_MASK 0x0238 + +#define LBA_IOS_BASE 0x0240 /* I/O port space for this LBA */ +#define LBA_IOS_MASK 0x0248 + +#define LBA_ELMMIO_BASE 0x0250 /* Extra LMMIO range */ +#define LBA_ELMMIO_MASK 0x0258 + +#define LBA_EIOS_BASE 0x0260 /* Extra I/O port space */ +#define LBA_EIOS_MASK 0x0268 + +#define LBA_GLOBAL_MASK 0x0270 /* Mercury only: Global Address Mask */ +#define LBA_DMA_CTL 0x0278 /* firmware sets this */ + +#define LBA_IBASE 0x0300 /* SBA DMA support */ +#define LBA_IMASK 0x0308 + +/* FIXME: ignore DMA Hint stuff until we can measure performance */ +#define LBA_HINT_CFG 0x0310 +#define LBA_HINT_BASE 0x0380 /* 14 registers at every 8 bytes. */ + +#define LBA_BUS_MODE 0x0620 + +/* ERROR regs are needed for config cycle kluges */ +#define LBA_ERROR_CONFIG 0x0680 +#define LBA_SMART_MODE 0x20 +#define LBA_ERROR_STATUS 0x0688 +#define LBA_ROPE_CTL 0x06A0 + +#define LBA_IOSAPIC_BASE 0x800 /* Offset of IRQ logic */ + /* non-postable I/O port space, densely packed */ #define LBA_PORT_BASE (PCI_F_EXTEND | 0xfee00000UL) static void __iomem *astro_iop_base __read_mostly; +#define ELROY_HVERS 0x782 +#define MERCURY_HVERS 0x783 +#define QUICKSILVER_HVERS 0x784 + +static inline int IS_ELROY(struct parisc_device *d) +{ + return (d->id.hversion == ELROY_HVERS); +} + +static inline int IS_MERCURY(struct parisc_device *d) +{ + return (d->id.hversion == MERCURY_HVERS); +} + +static inline int IS_QUICKSILVER(struct parisc_device *d) +{ + return (d->id.hversion == QUICKSILVER_HVERS); +} + + +/* +** lba_device: Per instance Elroy data structure +*/ +struct lba_device { + struct pci_hba_data hba; + + spinlock_t lba_lock; + void *iosapic_obj; + +#ifdef CONFIG_64BIT + void __iomem * iop_base; /* PA_VIEW - for IO port accessor funcs */ +#endif + + int flags; /* state/functionality enabled */ + int hw_rev; /* HW revision of chip */ +}; + + static u32 lba_t32; /* lba flags */ @@ -1439,8 +1542,8 @@ lba_driver_probe(struct parisc_device *dev) default: version = "TR4+"; } - printk(KERN_INFO "Elroy version %s (0x%x) found at 0x%lx\n", - version, func_class & 0xf, dev->hpa.start); + printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n", + MODULE_NAME, version, func_class & 0xf, dev->hpa.start); if (func_class < 2) { printk(KERN_WARNING "Can't support LBA older than " @@ -1460,18 +1563,14 @@ lba_driver_probe(struct parisc_device *dev) } } else if (IS_MERCURY(dev) || IS_QUICKSILVER(dev)) { - int major, minor; - func_class &= 0xff; - major = func_class >> 4, minor = func_class & 0xf; - + version = kmalloc(6, GFP_KERNEL); + snprintf(version, 6, "TR%d.%d",(func_class >> 4),(func_class & 0xf)); /* We could use one printk for both Elroy and Mercury, * but for the mask for func_class. */ - printk(KERN_INFO "%s version TR%d.%d (0x%x) found at 0x%lx\n", - IS_MERCURY(dev) ? "Mercury" : "Quicksilver", major, - minor, func_class, dev->hpa.start); - + printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n", + MODULE_NAME, version, func_class & 0xff, dev->hpa.start); cfg_ops = &mercury_cfg_ops; } else { printk(KERN_ERR "Unknown LBA found at 0x%lx\n", dev->hpa.start); @@ -1501,7 +1600,6 @@ lba_driver_probe(struct parisc_device *dev) lba_dev->hba.dev = dev; lba_dev->iosapic_obj = tmp_obj; /* save interrupt handle */ lba_dev->hba.iommu = sba_get_iommu(dev); /* get iommu data */ - parisc_set_drvdata(dev, lba_dev); /* ------------ Second : initialize common stuff ---------- */ pci_bios = &lba_bios_ops; diff --git a/trunk/drivers/parisc/sba_iommu.c b/trunk/drivers/parisc/sba_iommu.c index 294c1117098d..8b4732815511 100644 --- a/trunk/drivers/parisc/sba_iommu.c +++ b/trunk/drivers/parisc/sba_iommu.c @@ -38,15 +38,22 @@ #include #include -#include -#include /* for proc_mckinley_root */ #include /* for proc_runway_root */ #include /* for PDC_MODEL_* */ #include /* for is_pdc_pat() */ #include + +/* declared in arch/parisc/kernel/setup.c */ +extern struct proc_dir_entry * proc_mckinley_root; + #define MODULE_NAME "SBA" +#ifdef CONFIG_PROC_FS +/* depends on proc fs support. But costs CPU performance */ +#undef SBA_COLLECT_STATS +#endif + /* ** The number of debug flags is a clue - this code is fragile. ** Don't even think about messing with it unless you have @@ -85,12 +92,202 @@ #define DBG_RES(x...) #endif +#if defined(CONFIG_64BIT) +/* "low end" PA8800 machines use ZX1 chipset: PAT PDC and only run 64-bit */ +#define ZX1_SUPPORT +#endif + #define SBA_INLINE __inline__ + +/* +** The number of pdir entries to "free" before issueing +** a read to PCOM register to flush out PCOM writes. +** Interacts with allocation granularity (ie 4 or 8 entries +** allocated and free'd/purged at a time might make this +** less interesting). +*/ +#define DELAYED_RESOURCE_CNT 16 + #define DEFAULT_DMA_HINT_REG 0 -struct sba_device *sba_list; -EXPORT_SYMBOL_GPL(sba_list); +#define ASTRO_RUNWAY_PORT 0x582 +#define IKE_MERCED_PORT 0x803 +#define REO_MERCED_PORT 0x804 +#define REOG_MERCED_PORT 0x805 +#define PLUTO_MCKINLEY_PORT 0x880 + +#define SBA_FUNC_ID 0x0000 /* function id */ +#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ + +#define IS_ASTRO(id) ((id)->hversion == ASTRO_RUNWAY_PORT) +#define IS_IKE(id) ((id)->hversion == IKE_MERCED_PORT) +#define IS_PLUTO(id) ((id)->hversion == PLUTO_MCKINLEY_PORT) + +#define SBA_FUNC_SIZE 4096 /* SBA configuration function reg set */ + +#define ASTRO_IOC_OFFSET (32 * SBA_FUNC_SIZE) +#define PLUTO_IOC_OFFSET (1 * SBA_FUNC_SIZE) +/* Ike's IOC's occupy functions 2 and 3 */ +#define IKE_IOC_OFFSET(p) ((p+2) * SBA_FUNC_SIZE) + +#define IOC_CTRL 0x8 /* IOC_CTRL offset */ +#define IOC_CTRL_TC (1 << 0) /* TOC Enable */ +#define IOC_CTRL_CE (1 << 1) /* Coalesce Enable */ +#define IOC_CTRL_DE (1 << 2) /* Dillon Enable */ +#define IOC_CTRL_RM (1 << 8) /* Real Mode */ +#define IOC_CTRL_NC (1 << 9) /* Non Coherent Mode */ +#define IOC_CTRL_D4 (1 << 11) /* Disable 4-byte coalescing */ +#define IOC_CTRL_DD (1 << 13) /* Disable distr. LMMIO range coalescing */ + +#define MAX_IOC 2 /* per Ike. Pluto/Astro only have 1. */ + +#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ + + +/* +** Offsets into MBIB (Function 0 on Ike and hopefully Astro) +** Firmware programs this stuff. Don't touch it. +*/ +#define LMMIO_DIRECT0_BASE 0x300 +#define LMMIO_DIRECT0_MASK 0x308 +#define LMMIO_DIRECT0_ROUTE 0x310 + +#define LMMIO_DIST_BASE 0x360 +#define LMMIO_DIST_MASK 0x368 +#define LMMIO_DIST_ROUTE 0x370 + +#define IOS_DIST_BASE 0x390 +#define IOS_DIST_MASK 0x398 +#define IOS_DIST_ROUTE 0x3A0 + +#define IOS_DIRECT_BASE 0x3C0 +#define IOS_DIRECT_MASK 0x3C8 +#define IOS_DIRECT_ROUTE 0x3D0 + +/* +** Offsets into I/O TLB (Function 2 and 3 on Ike) +*/ +#define ROPE0_CTL 0x200 /* "regbus pci0" */ +#define ROPE1_CTL 0x208 +#define ROPE2_CTL 0x210 +#define ROPE3_CTL 0x218 +#define ROPE4_CTL 0x220 +#define ROPE5_CTL 0x228 +#define ROPE6_CTL 0x230 +#define ROPE7_CTL 0x238 + +#define IOC_ROPE0_CFG 0x500 /* pluto only */ +#define IOC_ROPE_AO 0x10 /* Allow "Relaxed Ordering" */ + + + +#define HF_ENABLE 0x40 + + +#define IOC_IBASE 0x300 /* IO TLB */ +#define IOC_IMASK 0x308 +#define IOC_PCOM 0x310 +#define IOC_TCNFG 0x318 +#define IOC_PDIR_BASE 0x320 + +/* AGP GART driver looks for this */ +#define SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL + + +/* +** IOC supports 4/8/16/64KB page sizes (see TCNFG register) +** It's safer (avoid memory corruption) to keep DMA page mappings +** equivalently sized to VM PAGE_SIZE. +** +** We really can't avoid generating a new mapping for each +** page since the Virtual Coherence Index has to be generated +** and updated for each page. +** +** PAGE_SIZE could be greater than IOVP_SIZE. But not the inverse. +*/ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +#define SBA_PERF_CFG 0x708 /* Performance Counter stuff */ +#define SBA_PERF_MASK1 0x718 +#define SBA_PERF_MASK2 0x730 + + +/* +** Offsets into PCI Performance Counters (functions 12 and 13) +** Controlled by PERF registers in function 2 & 3 respectively. +*/ +#define SBA_PERF_CNT1 0x200 +#define SBA_PERF_CNT2 0x208 +#define SBA_PERF_CNT3 0x210 + + +struct ioc { + void __iomem *ioc_hpa; /* I/O MMU base address */ + char *res_map; /* resource map, bit == pdir entry */ + u64 *pdir_base; /* physical base address */ + unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */ + unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */ +#ifdef ZX1_SUPPORT + unsigned long iovp_mask; /* help convert IOVA to IOVP */ +#endif + unsigned long *res_hint; /* next avail IOVP - circular search */ + spinlock_t res_lock; + unsigned int res_bitshift; /* from the LEFT! */ + unsigned int res_size; /* size of resource map in bytes */ +#ifdef SBA_HINT_SUPPORT +/* FIXME : DMA HINTs not used */ + unsigned long hint_mask_pdir; /* bits used for DMA hints */ + unsigned int hint_shift_pdir; +#endif +#if DELAYED_RESOURCE_CNT > 0 + int saved_cnt; + struct sba_dma_pair { + dma_addr_t iova; + size_t size; + } saved[DELAYED_RESOURCE_CNT]; +#endif + +#ifdef SBA_COLLECT_STATS +#define SBA_SEARCH_SAMPLE 0x100 + unsigned long avg_search[SBA_SEARCH_SAMPLE]; + unsigned long avg_idx; /* current index into avg_search */ + unsigned long used_pages; + unsigned long msingle_calls; + unsigned long msingle_pages; + unsigned long msg_calls; + unsigned long msg_pages; + unsigned long usingle_calls; + unsigned long usingle_pages; + unsigned long usg_calls; + unsigned long usg_pages; +#endif + + /* STUFF We don't need in performance path */ + unsigned int pdir_size; /* in bytes, determined by IOV Space size */ +}; + +struct sba_device { + struct sba_device *next; /* list of SBA's in system */ + struct parisc_device *dev; /* dev found in bus walk */ + struct parisc_device_id *iodc; /* data about dev from firmware */ + const char *name; + void __iomem *sba_hpa; /* base address */ + spinlock_t sba_lock; + unsigned int flags; /* state/functionality enabled */ + unsigned int hw_rev; /* HW revision of chip */ + + struct resource chip_resv; /* MMIO reserved for chip */ + struct resource iommu_resv; /* MMIO reserved for iommu */ + + unsigned int num_ioc; /* number of on-board IOC's */ + struct ioc ioc[MAX_IOC]; +}; + + +static struct sba_device *sba_list; static unsigned long ioc_needs_fdc = 0; @@ -103,14 +300,8 @@ static unsigned long piranha_bad_128k = 0; /* Looks nice and keeps the compiler happy */ #define SBA_DEV(d) ((struct sba_device *) (d)) -#ifdef CONFIG_AGP_PARISC -#define SBA_AGP_SUPPORT -#endif /*CONFIG_AGP_PARISC*/ - #ifdef SBA_AGP_SUPPORT -static int sba_reserve_agpgart = 1; -module_param(sba_reserve_agpgart, int, 1); -MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART"); +static int reserve_sba_gart = 1; #endif #define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1)) @@ -550,7 +741,7 @@ sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, asm("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba)); pa |= (ci >> 12) & 0xff; /* move CI (8 bits) into lowest byte */ - pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */ + pa |= 0x8000000000000000ULL; /* set "valid" bit */ *pdir_ptr = cpu_to_le64(pa); /* swap and store into I/O Pdir */ /* @@ -1307,10 +1498,6 @@ sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num) WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM); #ifdef SBA_AGP_SUPPORT -{ - struct klist_iter i; - struct device *dev = NULL; - /* ** If an AGP device is present, only use half of the IOV space ** for PCI DMA. Unfortunately we can't know ahead of time @@ -1319,22 +1506,20 @@ sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ** We program the next pdir index after we stop w/ a key for ** the GART code to handshake on. */ - klist_iter_init(&sba->dev.klist_children, &i); - while (dev = next_device(&i)) { - struct parisc_device *lba = to_parisc_device(dev); + device=NULL; + for (lba = sba->child; lba; lba = lba->sibling) { if (IS_QUICKSILVER(lba)) - agp_found = 1; + break; } - klist_iter_exit(&sba->dev.klist_children, &i); - if (agp_found && sba_reserve_agpgart) { - printk(KERN_INFO "%s: reserving %dMb of IOVA space for agpgart\n", - __FUNCTION__, (iova_space_size/2) >> 20); + if (lba) { + DBG_INIT("%s: Reserving half of IOVA space for AGP GART support\n", __FUNCTION__); ioc->pdir_size /= 2; - ioc->pdir_base[PDIR_INDEX(iova_space_size/2)] = SBA_AGPGART_COOKIE; + ((u64 *)ioc->pdir_base)[PDIR_INDEX(iova_space_size/2)] = SBA_IOMMU_COOKIE; + } else { + DBG_INIT("%s: No GART needed - no AGP controller found\n", __FUNCTION__); } -} -#endif /*SBA_AGP_SUPPORT*/ +#endif /* 0 */ } @@ -1516,7 +1701,7 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, } #endif - if (!IS_PLUTO(sba_dev->dev)) { + if (!IS_PLUTO(sba_dev->iodc)) { ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL); DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->", __FUNCTION__, sba_dev->sba_hpa, ioc_ctl); @@ -1533,8 +1718,9 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, #endif } /* if !PLUTO */ - if (IS_ASTRO(sba_dev->dev)) { + if (IS_ASTRO(sba_dev->iodc)) { int err; + /* PAT_PDC (L-class) also reports the same goofy base */ sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, ASTRO_IOC_OFFSET); num_ioc = 1; @@ -1544,9 +1730,13 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, err = request_resource(&iomem_resource, &(sba_dev->chip_resv)); BUG_ON(err < 0); - } else if (IS_PLUTO(sba_dev->dev)) { + } else if (IS_PLUTO(sba_dev->iodc)) { int err; + /* We use a negative value for IOC HPA so it gets + * corrected when we add it with IKE's IOC offset. + * Doesnt look clean, but fewer code. + */ sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, PLUTO_IOC_OFFSET); num_ioc = 1; @@ -1562,14 +1752,14 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, err = request_resource(&iomem_resource, &(sba_dev->iommu_resv)); WARN_ON(err < 0); } else { - /* IKE, REO */ + /* IS_IKE (ie N-class, L3000, L1500) */ sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(0)); sba_dev->ioc[1].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(1)); num_ioc = 2; /* TODO - LOOKUP Ike/Stretch chipset mem map */ } - /* XXX: What about Reo Grande? */ + /* XXX: What about Reo? */ sba_dev->num_ioc = num_ioc; for (i = 0; i < num_ioc; i++) { @@ -1584,7 +1774,7 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, * Overrides bit 1 in DMA Hint Sets. * Improves netperf UDP_STREAM by ~10% for bcm5701. */ - if (IS_PLUTO(sba_dev->dev)) { + if (IS_PLUTO(sba_dev->iodc)) { void __iomem *rope_cfg; unsigned long cfg_val; @@ -1613,7 +1803,7 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) ); - if (IS_PLUTO(sba_dev->dev)) { + if (IS_PLUTO(sba_dev->iodc)) { sba_ioc_init_pluto(sba_dev->dev, &(sba_dev->ioc[i]), i); } else { sba_ioc_init(sba_dev->dev, &(sba_dev->ioc[i]), i); @@ -1877,7 +2067,7 @@ sba_driver_callback(struct parisc_device *dev) /* Read HW Rev First */ func_class = READ_REG(sba_addr + SBA_FCLASS); - if (IS_ASTRO(dev)) { + if (IS_ASTRO(&dev->id)) { unsigned long fclass; static char astro_rev[]="Astro ?.?"; @@ -1888,11 +2078,11 @@ sba_driver_callback(struct parisc_device *dev) astro_rev[8] = '0' + (char) ((fclass & 0x18) >> 3); version = astro_rev; - } else if (IS_IKE(dev)) { + } else if (IS_IKE(&dev->id)) { static char ike_rev[] = "Ike rev ?"; ike_rev[8] = '0' + (char) (func_class & 0xff); version = ike_rev; - } else if (IS_PLUTO(dev)) { + } else if (IS_PLUTO(&dev->id)) { static char pluto_rev[]="Pluto ?.?"; pluto_rev[6] = '0' + (char) ((func_class & 0xf0) >> 4); pluto_rev[8] = '0' + (char) (func_class & 0x0f); @@ -1907,7 +2097,7 @@ sba_driver_callback(struct parisc_device *dev) global_ioc_cnt = count_parisc_driver(&sba_driver); /* Astro and Pluto have one IOC per SBA */ - if ((!IS_ASTRO(dev)) || (!IS_PLUTO(dev))) + if ((!IS_ASTRO(&dev->id)) || (!IS_PLUTO(&dev->id))) global_ioc_cnt *= 2; } @@ -1927,6 +2117,7 @@ sba_driver_callback(struct parisc_device *dev) sba_dev->dev = dev; sba_dev->hw_rev = func_class; + sba_dev->iodc = &dev->id; sba_dev->name = dev->name; sba_dev->sba_hpa = sba_addr; diff --git a/trunk/drivers/pci/Kconfig b/trunk/drivers/pci/Kconfig index 30294127a0aa..c27e782e6df9 100644 --- a/trunk/drivers/pci/Kconfig +++ b/trunk/drivers/pci/Kconfig @@ -52,11 +52,3 @@ config PCI_DEBUG When in doubt, say N. -config HT_IRQ - bool "Interrupts on hypertransport devices" - default y - depends on X86_LOCAL_APIC && X86_IO_APIC - help - This allows native hypertransport devices to use interrupts. - - If unsure say Y. diff --git a/trunk/drivers/pci/Makefile b/trunk/drivers/pci/Makefile index e3beb784406f..f2d152b818f0 100644 --- a/trunk/drivers/pci/Makefile +++ b/trunk/drivers/pci/Makefile @@ -14,12 +14,6 @@ obj-$(CONFIG_HOTPLUG) += hotplug.o # Build the PCI Hotplug drivers if we were asked to obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ -# Build the PCI MSI interrupt support -obj-$(CONFIG_PCI_MSI) += msi.o - -# Build the Hypertransport interrupt support -obj-$(CONFIG_HT_IRQ) += htirq.o - # # Some architectures use the generic PCI setup functions # @@ -33,6 +27,11 @@ obj-$(CONFIG_PPC64) += setup-bus.o obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o +msiobj-y := msi.o msi-apic.o +msiobj-$(CONFIG_IA64_GENERIC) += msi-altix.o +msiobj-$(CONFIG_IA64_SGI_SN2) += msi-altix.o +obj-$(CONFIG_PCI_MSI) += $(msiobj-y) + # # ACPI Related PCI FW Functions # diff --git a/trunk/drivers/pci/htirq.c b/trunk/drivers/pci/htirq.c deleted file mode 100644 index 0e27f2404a83..000000000000 --- a/trunk/drivers/pci/htirq.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * File: htirq.c - * Purpose: Hypertransport Interrupt Capability - * - * Copyright (C) 2006 Linux Networx - * Copyright (C) Eric Biederman - */ - -#include -#include -#include -#include -#include -#include - -/* Global ht irq lock. - * - * This is needed to serialize access to the data port in hypertransport - * irq capability. - * - * With multiple simultaneous hypertransport irq devices it might pay - * to make this more fine grained. But start with simple, stupid, and correct. - */ -static DEFINE_SPINLOCK(ht_irq_lock); - -struct ht_irq_cfg { - struct pci_dev *dev; - unsigned pos; - unsigned idx; -}; - -void write_ht_irq_low(unsigned int irq, u32 data) -{ - struct ht_irq_cfg *cfg = get_irq_data(irq); - unsigned long flags; - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); - pci_write_config_dword(cfg->dev, cfg->pos + 4, data); - spin_unlock_irqrestore(&ht_irq_lock, flags); -} - -void write_ht_irq_high(unsigned int irq, u32 data) -{ - struct ht_irq_cfg *cfg = get_irq_data(irq); - unsigned long flags; - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1); - pci_write_config_dword(cfg->dev, cfg->pos + 4, data); - spin_unlock_irqrestore(&ht_irq_lock, flags); -} - -u32 read_ht_irq_low(unsigned int irq) -{ - struct ht_irq_cfg *cfg = get_irq_data(irq); - unsigned long flags; - u32 data; - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); - pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); - spin_unlock_irqrestore(&ht_irq_lock, flags); - return data; -} - -u32 read_ht_irq_high(unsigned int irq) -{ - struct ht_irq_cfg *cfg = get_irq_data(irq); - unsigned long flags; - u32 data; - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1); - pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); - spin_unlock_irqrestore(&ht_irq_lock, flags); - return data; -} - -void mask_ht_irq(unsigned int irq) -{ - struct ht_irq_cfg *cfg; - unsigned long flags; - u32 data; - - cfg = get_irq_data(irq); - - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); - pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); - data |= 1; - pci_write_config_dword(cfg->dev, cfg->pos + 4, data); - spin_unlock_irqrestore(&ht_irq_lock, flags); -} - -void unmask_ht_irq(unsigned int irq) -{ - struct ht_irq_cfg *cfg; - unsigned long flags; - u32 data; - - cfg = get_irq_data(irq); - - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx); - pci_read_config_dword(cfg->dev, cfg->pos + 4, &data); - data &= ~1; - pci_write_config_dword(cfg->dev, cfg->pos + 4, data); - spin_unlock_irqrestore(&ht_irq_lock, flags); -} - -/** - * ht_create_irq - create an irq and attach it to a device. - * @dev: The hypertransport device to find the irq capability on. - * @idx: Which of the possible irqs to attach to. - * - * ht_create_irq is needs to be called for all hypertransport devices - * that generate irqs. - * - * The irq number of the new irq or a negative error value is returned. - */ -int ht_create_irq(struct pci_dev *dev, int idx) -{ - struct ht_irq_cfg *cfg; - unsigned long flags; - u32 data; - int max_irq; - int pos; - int irq; - - pos = pci_find_capability(dev, PCI_CAP_ID_HT); - while (pos) { - u8 subtype; - pci_read_config_byte(dev, pos + 3, &subtype); - if (subtype == HT_CAPTYPE_IRQ) - break; - pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT); - } - if (!pos) - return -EINVAL; - - /* Verify the idx I want to use is in range */ - spin_lock_irqsave(&ht_irq_lock, flags); - pci_write_config_byte(dev, pos + 2, 1); - pci_read_config_dword(dev, pos + 4, &data); - spin_unlock_irqrestore(&ht_irq_lock, flags); - - max_irq = (data >> 16) & 0xff; - if ( idx > max_irq) - return -EINVAL; - - cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; - - cfg->dev = dev; - cfg->pos = pos; - cfg->idx = 0x10 + (idx * 2); - - irq = create_irq(); - if (irq < 0) { - kfree(cfg); - return -EBUSY; - } - set_irq_data(irq, cfg); - - if (arch_setup_ht_irq(irq, dev) < 0) { - ht_destroy_irq(irq); - return -EBUSY; - } - - return irq; -} - -/** - * ht_destroy_irq - destroy an irq created with ht_create_irq - * - * This reverses ht_create_irq removing the specified irq from - * existence. The irq should be free before this happens. - */ -void ht_destroy_irq(unsigned int irq) -{ - struct ht_irq_cfg *cfg; - - cfg = get_irq_data(irq); - set_irq_chip(irq, NULL); - set_irq_data(irq, NULL); - destroy_irq(irq); - - kfree(cfg); -} - -EXPORT_SYMBOL(ht_create_irq); -EXPORT_SYMBOL(ht_destroy_irq); diff --git a/trunk/arch/ia64/sn/kernel/msi_sn.c b/trunk/drivers/pci/msi-altix.c similarity index 65% rename from trunk/arch/ia64/sn/kernel/msi_sn.c rename to trunk/drivers/pci/msi-altix.c index 6ffd1f850d41..bed4183a5e39 100644 --- a/trunk/arch/ia64/sn/kernel/msi_sn.c +++ b/trunk/drivers/pci/msi-altix.c @@ -7,10 +7,8 @@ */ #include -#include #include #include -#include #include #include @@ -18,16 +16,17 @@ #include #include +#include "msi.h" + struct sn_msi_info { u64 pci_addr; struct sn_irq_info *sn_irq_info; }; -static struct sn_msi_info sn_msi_info[NR_IRQS]; - -static struct irq_chip sn_msi_chip; +static struct sn_msi_info *sn_msi_info; -void sn_teardown_msi_irq(unsigned int irq) +static void +sn_msi_teardown(unsigned int vector) { nasid_t nasid; int widget; @@ -37,7 +36,7 @@ void sn_teardown_msi_irq(unsigned int irq) struct pcibus_bussoft *bussoft; struct sn_pcibus_provider *provider; - sn_irq_info = sn_msi_info[irq].sn_irq_info; + sn_irq_info = sn_msi_info[vector].sn_irq_info; if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) return; @@ -46,9 +45,9 @@ void sn_teardown_msi_irq(unsigned int irq) provider = SN_PCIDEV_BUSPROVIDER(pdev); (*provider->dma_unmap)(pdev, - sn_msi_info[irq].pci_addr, + sn_msi_info[vector].pci_addr, PCI_DMA_FROMDEVICE); - sn_msi_info[irq].pci_addr = 0; + sn_msi_info[vector].pci_addr = 0; bussoft = SN_PCIDEV_BUSSOFT(pdev); nasid = NASID_GET(bussoft->bs_base); @@ -57,15 +56,15 @@ void sn_teardown_msi_irq(unsigned int irq) SWIN_WIDGETNUM(bussoft->bs_base); sn_intr_free(nasid, widget, sn_irq_info); - sn_msi_info[irq].sn_irq_info = NULL; + sn_msi_info[vector].sn_irq_info = NULL; return; } -int sn_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) +int +sn_msi_setup(struct pci_dev *pdev, unsigned int vector, + u32 *addr_hi, u32 *addr_lo, u32 *data) { - struct msi_msg msg; - struct msi_desc *entry; int widget; int status; nasid_t nasid; @@ -74,10 +73,6 @@ int sn_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev); struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); - entry = get_irq_data(irq); - if (!entry->msi_attrib.is_64) - return -EINVAL; - if (bussoft == NULL) return -EINVAL; @@ -98,7 +93,7 @@ int sn_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) if (! sn_irq_info) return -ENOMEM; - status = sn_intr_alloc(nasid, widget, sn_irq_info, irq, -1, -1); + status = sn_intr_alloc(nasid, widget, sn_irq_info, vector, -1, -1); if (status) { kfree(sn_irq_info); return -ENOMEM; @@ -124,32 +119,29 @@ int sn_setup_msi_irq(unsigned int irq, struct pci_dev *pdev) return -ENOMEM; } - sn_msi_info[irq].sn_irq_info = sn_irq_info; - sn_msi_info[irq].pci_addr = bus_addr; + sn_msi_info[vector].sn_irq_info = sn_irq_info; + sn_msi_info[vector].pci_addr = bus_addr; - msg.address_hi = (u32)(bus_addr >> 32); - msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); + *addr_hi = (u32)(bus_addr >> 32); + *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); /* * In the SN platform, bit 16 is a "send vector" bit which * must be present in order to move the vector through the system. */ - msg.data = 0x100 + irq; + *data = 0x100 + (unsigned int)vector; #ifdef CONFIG_SMP - set_irq_affinity_info(irq, sn_irq_info->irq_cpuid, 0); + set_irq_affinity_info((vector & 0xff), sn_irq_info->irq_cpuid, 0); #endif - write_msi_msg(irq, &msg); - set_irq_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq); - return 0; } -#ifdef CONFIG_SMP -static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) +static void +sn_msi_target(unsigned int vector, unsigned int cpu, + u32 *addr_hi, u32 *addr_lo) { - struct msi_msg msg; int slice; nasid_t nasid; u64 bus_addr; @@ -158,10 +150,8 @@ static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) struct sn_irq_info *sn_irq_info; struct sn_irq_info *new_irq_info; struct sn_pcibus_provider *provider; - unsigned int cpu; - cpu = first_cpu(cpu_mask); - sn_irq_info = sn_msi_info[irq].sn_irq_info; + sn_irq_info = sn_msi_info[vector].sn_irq_info; if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) return; @@ -169,20 +159,19 @@ static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) * Release XIO resources for the old MSI PCI address */ - read_msi_msg(irq, &msg); sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; pdev = sn_pdev->pdi_linux_pcidev; provider = SN_PCIDEV_BUSPROVIDER(pdev); - bus_addr = (u64)(msg.address_hi) << 32 | (u64)(msg.address_lo); + bus_addr = (u64)(*addr_hi) << 32 | (u64)(*addr_lo); (*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE); - sn_msi_info[irq].pci_addr = 0; + sn_msi_info[vector].pci_addr = 0; nasid = cpuid_to_nasid(cpu); slice = cpuid_to_slice(cpu); new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice); - sn_msi_info[irq].sn_irq_info = new_irq_info; + sn_msi_info[vector].sn_irq_info = new_irq_info; if (new_irq_info == NULL) return; @@ -195,36 +184,27 @@ static void sn_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) sizeof(new_irq_info->irq_xtalkaddr), SN_DMA_MSI|SN_DMA_ADDR_XIO); - sn_msi_info[irq].pci_addr = bus_addr; - msg.address_hi = (u32)(bus_addr >> 32); - msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff); - - write_msi_msg(irq, &msg); - set_native_irq_info(irq, cpu_mask); + sn_msi_info[vector].pci_addr = bus_addr; + *addr_hi = (u32)(bus_addr >> 32); + *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); } -#endif /* CONFIG_SMP */ -static void sn_ack_msi_irq(unsigned int irq) -{ - move_native_irq(irq); - ia64_eoi(); -} +struct msi_ops sn_msi_ops = { + .setup = sn_msi_setup, + .teardown = sn_msi_teardown, +#ifdef CONFIG_SMP + .target = sn_msi_target, +#endif +}; -static int sn_msi_retrigger_irq(unsigned int irq) +int +sn_msi_init(void) { - unsigned int vector = irq; - ia64_resend_irq(vector); + sn_msi_info = + kzalloc(sizeof(struct sn_msi_info) * NR_VECTORS, GFP_KERNEL); + if (! sn_msi_info) + return -ENOMEM; - return 1; + msi_register(&sn_msi_ops); + return 0; } - -static struct irq_chip sn_msi_chip = { - .name = "PCI-MSI", - .mask = mask_msi_irq, - .unmask = unmask_msi_irq, - .ack = sn_ack_msi_irq, -#ifdef CONFIG_SMP - .set_affinity = sn_set_msi_irq_affinity, -#endif - .retrigger = sn_msi_retrigger_irq, -}; diff --git a/trunk/drivers/pci/msi-apic.c b/trunk/drivers/pci/msi-apic.c new file mode 100644 index 000000000000..5ed798b319c7 --- /dev/null +++ b/trunk/drivers/pci/msi-apic.c @@ -0,0 +1,101 @@ +/* + * MSI hooks for standard x86 apic + */ + +#include +#include +#include + +#include "msi.h" + +/* + * Shifts for APIC-based data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) + +#define MSI_DATA_DELIVERY_SHIFT 8 +#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) +#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) + +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) +#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) + +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) +#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) + +/* + * Shift/mask fields for APIC-based bus address + */ + +#define MSI_ADDR_HEADER 0xfee00000 + +#define MSI_ADDR_DESTID_MASK 0xfff0000f +#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) + +#define MSI_ADDR_DESTMODE_SHIFT 2 +#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) +#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) + +#define MSI_ADDR_REDIRECTION_SHIFT 3 +#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) +#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) + + +static void +msi_target_apic(unsigned int vector, + unsigned int dest_cpu, + u32 *address_hi, /* in/out */ + u32 *address_lo) /* in/out */ +{ + u32 addr = *address_lo; + + addr &= MSI_ADDR_DESTID_MASK; + addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(dest_cpu)); + + *address_lo = addr; +} + +static int +msi_setup_apic(struct pci_dev *pdev, /* unused in generic */ + unsigned int vector, + u32 *address_hi, + u32 *address_lo, + u32 *data) +{ + unsigned long dest_phys_id; + + dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); + + *address_hi = 0; + *address_lo = MSI_ADDR_HEADER | + MSI_ADDR_DESTMODE_PHYS | + MSI_ADDR_REDIRECTION_CPU | + MSI_ADDR_DESTID_CPU(dest_phys_id); + + *data = MSI_DATA_TRIGGER_EDGE | + MSI_DATA_LEVEL_ASSERT | + MSI_DATA_DELIVERY_FIXED | + MSI_DATA_VECTOR(vector); + + return 0; +} + +static void +msi_teardown_apic(unsigned int vector) +{ + return; /* no-op */ +} + +/* + * Generic ops used on most IA archs/platforms. Set with msi_register() + */ + +struct msi_ops msi_apic_ops = { + .setup = msi_setup_apic, + .teardown = msi_teardown_apic, + .target = msi_target_apic, +}; diff --git a/trunk/drivers/pci/msi.c b/trunk/drivers/pci/msi.c index f9fdc54473c4..27a057409eca 100644 --- a/trunk/drivers/pci/msi.c +++ b/trunk/drivers/pci/msi.c @@ -6,7 +6,6 @@ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) */ -#include #include #include #include @@ -15,7 +14,6 @@ #include #include #include -#include #include #include @@ -29,6 +27,23 @@ static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; static kmem_cache_t* msi_cachep; static int pci_msi_enable = 1; +static int last_alloc_vector; +static int nr_released_vectors; +static int nr_reserved_vectors = NR_HP_RESERVED_VECTORS; +static int nr_msix_devices; + +#ifndef CONFIG_X86_IO_APIC +int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1}; +#endif + +static struct msi_ops *msi_ops; + +int +msi_register(struct msi_ops *ops) +{ + msi_ops = ops; + return 0; +} static int msi_cache_init(void) { @@ -40,25 +55,26 @@ static int msi_cache_init(void) return 0; } -static void msi_set_mask_bit(unsigned int irq, int flag) +static void msi_set_mask_bit(unsigned int vector, int flag) { struct msi_desc *entry; - entry = msi_desc[irq]; - BUG_ON(!entry || !entry->dev); + entry = (struct msi_desc *)msi_desc[vector]; + if (!entry || !entry->dev || !entry->mask_base) + return; switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: - if (entry->msi_attrib.maskbit) { - int pos; - u32 mask_bits; - - pos = (long)entry->mask_base; - pci_read_config_dword(entry->dev, pos, &mask_bits); - mask_bits &= ~(1); - mask_bits |= flag; - pci_write_config_dword(entry->dev, pos, mask_bits); - } + { + int pos; + u32 mask_bits; + + pos = (long)entry->mask_base; + pci_read_config_dword(entry->dev, pos, &mask_bits); + mask_bits &= ~(1); + mask_bits |= flag; + pci_write_config_dword(entry->dev, pos, mask_bits); break; + } case PCI_CAP_ID_MSIX: { int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + @@ -67,101 +83,261 @@ static void msi_set_mask_bit(unsigned int irq, int flag) break; } default: - BUG(); break; } } -void read_msi_msg(unsigned int irq, struct msi_msg *msg) +#ifdef CONFIG_SMP +static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) { - struct msi_desc *entry = get_irq_data(irq); - switch(entry->msi_attrib.type) { - case PCI_CAP_ID_MSI: - { - struct pci_dev *dev = entry->dev; - int pos = entry->msi_attrib.pos; - u16 data; - - pci_read_config_dword(dev, msi_lower_address_reg(pos), - &msg->address_lo); - if (entry->msi_attrib.is_64) { - pci_read_config_dword(dev, msi_upper_address_reg(pos), - &msg->address_hi); - pci_read_config_word(dev, msi_data_reg(pos, 1), &data); - } else { - msg->address_hi = 0; - pci_read_config_word(dev, msi_data_reg(pos, 1), &data); - } - msg->data = data; - break; - } - case PCI_CAP_ID_MSIX: - { - void __iomem *base; - base = entry->mask_base + - entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; + struct msi_desc *entry; + u32 address_hi, address_lo; + unsigned int irq = vector; + unsigned int dest_cpu = first_cpu(cpu_mask); - msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); - break; - } - default: - BUG(); - } -} + entry = (struct msi_desc *)msi_desc[vector]; + if (!entry || !entry->dev) + return; -void write_msi_msg(unsigned int irq, struct msi_msg *msg) -{ - struct msi_desc *entry = get_irq_data(irq); switch (entry->msi_attrib.type) { case PCI_CAP_ID_MSI: { - struct pci_dev *dev = entry->dev; - int pos = entry->msi_attrib.pos; - - pci_write_config_dword(dev, msi_lower_address_reg(pos), - msg->address_lo); - if (entry->msi_attrib.is_64) { - pci_write_config_dword(dev, msi_upper_address_reg(pos), - msg->address_hi); - pci_write_config_word(dev, msi_data_reg(pos, 1), - msg->data); - } else { - pci_write_config_word(dev, msi_data_reg(pos, 0), - msg->data); - } + int pos = pci_find_capability(entry->dev, PCI_CAP_ID_MSI); + + if (!pos) + return; + + pci_read_config_dword(entry->dev, msi_upper_address_reg(pos), + &address_hi); + pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), + &address_lo); + + msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); + + pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), + address_hi); + pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), + address_lo); + set_native_irq_info(irq, cpu_mask); break; } case PCI_CAP_ID_MSIX: { - void __iomem *base; - base = entry->mask_base + - entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; - - writel(msg->address_lo, - base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel(msg->address_hi, - base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel(msg->data, base + PCI_MSIX_ENTRY_DATA_OFFSET); + int offset_hi = + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET; + int offset_lo = + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; + + address_hi = readl(entry->mask_base + offset_hi); + address_lo = readl(entry->mask_base + offset_lo); + + msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); + + writel(address_hi, entry->mask_base + offset_hi); + writel(address_lo, entry->mask_base + offset_lo); + set_native_irq_info(irq, cpu_mask); break; } default: - BUG(); + break; } } +#else +#define set_msi_affinity NULL +#endif /* CONFIG_SMP */ + +static void mask_MSI_irq(unsigned int vector) +{ + msi_set_mask_bit(vector, 1); +} -void mask_msi_irq(unsigned int irq) +static void unmask_MSI_irq(unsigned int vector) { - msi_set_mask_bit(irq, 1); + msi_set_mask_bit(vector, 0); +} + +static unsigned int startup_msi_irq_wo_maskbit(unsigned int vector) +{ + struct msi_desc *entry; + unsigned long flags; + + spin_lock_irqsave(&msi_lock, flags); + entry = msi_desc[vector]; + if (!entry || !entry->dev) { + spin_unlock_irqrestore(&msi_lock, flags); + return 0; + } + entry->msi_attrib.state = 1; /* Mark it active */ + spin_unlock_irqrestore(&msi_lock, flags); + + return 0; /* never anything pending */ +} + +static unsigned int startup_msi_irq_w_maskbit(unsigned int vector) +{ + startup_msi_irq_wo_maskbit(vector); + unmask_MSI_irq(vector); + return 0; /* never anything pending */ +} + +static void shutdown_msi_irq(unsigned int vector) +{ + struct msi_desc *entry; + unsigned long flags; + + spin_lock_irqsave(&msi_lock, flags); + entry = msi_desc[vector]; + if (entry && entry->dev) + entry->msi_attrib.state = 0; /* Mark it not active */ + spin_unlock_irqrestore(&msi_lock, flags); } -void unmask_msi_irq(unsigned int irq) +static void end_msi_irq_wo_maskbit(unsigned int vector) { - msi_set_mask_bit(irq, 0); + move_native_irq(vector); + ack_APIC_irq(); +} + +static void end_msi_irq_w_maskbit(unsigned int vector) +{ + move_native_irq(vector); + unmask_MSI_irq(vector); + ack_APIC_irq(); +} + +static void do_nothing(unsigned int vector) +{ +} + +/* + * Interrupt Type for MSI-X PCI/PCI-X/PCI-Express Devices, + * which implement the MSI-X Capability Structure. + */ +static struct hw_interrupt_type msix_irq_type = { + .typename = "PCI-MSI-X", + .startup = startup_msi_irq_w_maskbit, + .shutdown = shutdown_msi_irq, + .enable = unmask_MSI_irq, + .disable = mask_MSI_irq, + .ack = mask_MSI_irq, + .end = end_msi_irq_w_maskbit, + .set_affinity = set_msi_affinity +}; + +/* + * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, + * which implement the MSI Capability Structure with + * Mask-and-Pending Bits. + */ +static struct hw_interrupt_type msi_irq_w_maskbit_type = { + .typename = "PCI-MSI", + .startup = startup_msi_irq_w_maskbit, + .shutdown = shutdown_msi_irq, + .enable = unmask_MSI_irq, + .disable = mask_MSI_irq, + .ack = mask_MSI_irq, + .end = end_msi_irq_w_maskbit, + .set_affinity = set_msi_affinity +}; + +/* + * Interrupt Type for MSI PCI/PCI-X/PCI-Express Devices, + * which implement the MSI Capability Structure without + * Mask-and-Pending Bits. + */ +static struct hw_interrupt_type msi_irq_wo_maskbit_type = { + .typename = "PCI-MSI", + .startup = startup_msi_irq_wo_maskbit, + .shutdown = shutdown_msi_irq, + .enable = do_nothing, + .disable = do_nothing, + .ack = do_nothing, + .end = end_msi_irq_wo_maskbit, + .set_affinity = set_msi_affinity +}; + +static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); +static int assign_msi_vector(void) +{ + static int new_vector_avail = 1; + int vector; + unsigned long flags; + + /* + * msi_lock is provided to ensure that successful allocation of MSI + * vector is assigned unique among drivers. + */ + spin_lock_irqsave(&msi_lock, flags); + + if (!new_vector_avail) { + int free_vector = 0; + + /* + * vector_irq[] = -1 indicates that this specific vector is: + * - assigned for MSI (since MSI have no associated IRQ) or + * - assigned for legacy if less than 16, or + * - having no corresponding 1:1 vector-to-IOxAPIC IRQ mapping + * vector_irq[] = 0 indicates that this vector, previously + * assigned for MSI, is freed by hotplug removed operations. + * This vector will be reused for any subsequent hotplug added + * operations. + * vector_irq[] > 0 indicates that this vector is assigned for + * IOxAPIC IRQs. This vector and its value provides a 1-to-1 + * vector-to-IOxAPIC IRQ mapping. + */ + for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) { + if (vector_irq[vector] != 0) + continue; + free_vector = vector; + if (!msi_desc[vector]) + break; + else + continue; + } + if (!free_vector) { + spin_unlock_irqrestore(&msi_lock, flags); + return -EBUSY; + } + vector_irq[free_vector] = -1; + nr_released_vectors--; + spin_unlock_irqrestore(&msi_lock, flags); + if (msi_desc[free_vector] != NULL) { + struct pci_dev *dev; + int tail; + + /* free all linked vectors before re-assign */ + do { + spin_lock_irqsave(&msi_lock, flags); + dev = msi_desc[free_vector]->dev; + tail = msi_desc[free_vector]->link.tail; + spin_unlock_irqrestore(&msi_lock, flags); + msi_free_vector(dev, tail, 1); + } while (free_vector != tail); + } + + return free_vector; + } + vector = assign_irq_vector(AUTO_ASSIGN); + last_alloc_vector = vector; + if (vector == LAST_DEVICE_VECTOR) + new_vector_avail = 0; + + spin_unlock_irqrestore(&msi_lock, flags); + return vector; +} + +static int get_new_vector(void) +{ + int vector = assign_msi_vector(); + + if (vector > 0) + set_intr_gate(vector, interrupt[vector]); + + return vector; } -static int msi_free_irq(struct pci_dev* dev, int irq); static int msi_init(void) { static int status = -ENOMEM; @@ -176,6 +352,22 @@ static int msi_init(void) return status; } + status = msi_arch_init(); + if (status < 0) { + pci_msi_enable = 0; + printk(KERN_WARNING + "PCI: MSI arch init failed. MSI disabled.\n"); + return status; + } + + if (! msi_ops) { + printk(KERN_WARNING + "PCI: MSI ops not registered. MSI disabled.\n"); + status = -EINVAL; + return status; + } + + last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); status = msi_cache_init(); if (status < 0) { pci_msi_enable = 0; @@ -183,9 +375,23 @@ static int msi_init(void) return status; } + if (last_alloc_vector < 0) { + pci_msi_enable = 0; + printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); + status = -EBUSY; + return status; + } + vector_irq[last_alloc_vector] = 0; + nr_released_vectors++; + return status; } +static int get_msi_vector(struct pci_dev *dev) +{ + return get_new_vector(); +} + static struct msi_desc* alloc_msi_entry(void) { struct msi_desc *entry; @@ -200,44 +406,29 @@ static struct msi_desc* alloc_msi_entry(void) return entry; } -static void attach_msi_entry(struct msi_desc *entry, int irq) +static void attach_msi_entry(struct msi_desc *entry, int vector) { unsigned long flags; spin_lock_irqsave(&msi_lock, flags); - msi_desc[irq] = entry; + msi_desc[vector] = entry; spin_unlock_irqrestore(&msi_lock, flags); } -static int create_msi_irq(void) +static void irq_handler_init(int cap_id, int pos, int mask) { - struct msi_desc *entry; - int irq; - - entry = alloc_msi_entry(); - if (!entry) - return -ENOMEM; + unsigned long flags; - irq = create_irq(); - if (irq < 0) { - kmem_cache_free(msi_cachep, entry); - return -EBUSY; + spin_lock_irqsave(&irq_desc[pos].lock, flags); + if (cap_id == PCI_CAP_ID_MSIX) + irq_desc[pos].chip = &msix_irq_type; + else { + if (!mask) + irq_desc[pos].chip = &msi_irq_wo_maskbit_type; + else + irq_desc[pos].chip = &msi_irq_w_maskbit_type; } - - set_irq_data(irq, entry); - - return irq; -} - -static void destroy_msi_irq(unsigned int irq) -{ - struct msi_desc *entry; - - entry = get_irq_data(irq); - set_irq_chip(irq, NULL); - set_irq_data(irq, NULL); - destroy_irq(irq); - kmem_cache_free(msi_cachep, entry); + spin_unlock_irqrestore(&irq_desc[pos].lock, flags); } static void enable_msi_mode(struct pci_dev *dev, int pos, int type) @@ -282,21 +473,21 @@ void disable_msi_mode(struct pci_dev *dev, int pos, int type) } } -static int msi_lookup_irq(struct pci_dev *dev, int type) +static int msi_lookup_vector(struct pci_dev *dev, int type) { - int irq; + int vector; unsigned long flags; spin_lock_irqsave(&msi_lock, flags); - for (irq = 0; irq < NR_IRQS; irq++) { - if (!msi_desc[irq] || msi_desc[irq]->dev != dev || - msi_desc[irq]->msi_attrib.type != type || - msi_desc[irq]->msi_attrib.default_irq != dev->irq) + for (vector = FIRST_DEVICE_VECTOR; vector < NR_IRQS; vector++) { + if (!msi_desc[vector] || msi_desc[vector]->dev != dev || + msi_desc[vector]->msi_attrib.type != type || + msi_desc[vector]->msi_attrib.default_vector != dev->irq) continue; spin_unlock_irqrestore(&msi_lock, flags); - /* This pre-assigned MSI irq for this device - already exits. Override dev->irq with this irq */ - dev->irq = irq; + /* This pre-assigned MSI vector for this device + already exits. Override dev->irq with this vector */ + dev->irq = vector; return 0; } spin_unlock_irqrestore(&msi_lock, flags); @@ -308,6 +499,11 @@ void pci_scan_msi_device(struct pci_dev *dev) { if (!dev) return; + + if (pci_find_capability(dev, PCI_CAP_ID_MSIX) > 0) + nr_msix_devices++; + else if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0) + nr_reserved_vectors++; } #ifdef CONFIG_PM @@ -381,7 +577,7 @@ int pci_save_msix_state(struct pci_dev *dev) { int pos; int temp; - int irq, head, tail = 0; + int vector, head, tail = 0; u16 control; struct pci_cap_saved_state *save_state; @@ -403,20 +599,33 @@ int pci_save_msix_state(struct pci_dev *dev) /* save the table */ temp = dev->irq; - if (msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { + if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { kfree(save_state); return -EINVAL; } - irq = head = dev->irq; + vector = head = dev->irq; while (head != tail) { + int j; + void __iomem *base; struct msi_desc *entry; - entry = msi_desc[irq]; - read_msi_msg(irq, &entry->msg_save); - - tail = msi_desc[irq]->link.tail; - irq = tail; + entry = msi_desc[vector]; + base = entry->mask_base; + j = entry->msi_attrib.entry_nr; + + entry->address_lo_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + entry->address_hi_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + entry->data_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + + tail = msi_desc[vector]->link.tail; + vector = tail; } dev->irq = temp; @@ -429,7 +638,9 @@ void pci_restore_msix_state(struct pci_dev *dev) { u16 save; int pos; - int irq, head, tail = 0; + int vector, head, tail = 0; + void __iomem *base; + int j; struct msi_desc *entry; int temp; struct pci_cap_saved_state *save_state; @@ -447,15 +658,26 @@ void pci_restore_msix_state(struct pci_dev *dev) /* route the table */ temp = dev->irq; - if (msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) + if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) return; - irq = head = dev->irq; + vector = head = dev->irq; while (head != tail) { - entry = msi_desc[irq]; - write_msi_msg(irq, &entry->msg_save); - - tail = msi_desc[irq]->link.tail; - irq = tail; + entry = msi_desc[vector]; + base = entry->mask_base; + j = entry->msi_attrib.entry_nr; + + writel(entry->address_lo_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(entry->address_hi_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(entry->data_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + + tail = msi_desc[vector]->link.tail; + vector = tail; } dev->irq = temp; @@ -464,68 +686,104 @@ void pci_restore_msix_state(struct pci_dev *dev) } #endif +static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry) +{ + int status; + u32 address_hi; + u32 address_lo; + u32 data; + int pos, vector = dev->irq; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + pci_read_config_word(dev, msi_control_reg(pos), &control); + + /* Configure MSI capability structure */ + status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data); + if (status < 0) + return status; + + pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo); + if (is_64bit_address(control)) { + pci_write_config_dword(dev, + msi_upper_address_reg(pos), address_hi); + pci_write_config_word(dev, + msi_data_reg(pos, 1), data); + } else + pci_write_config_word(dev, + msi_data_reg(pos, 0), data); + if (entry->msi_attrib.maskbit) { + unsigned int maskbits, temp; + /* All MSIs are unmasked by default, Mask them all */ + pci_read_config_dword(dev, + msi_mask_bits_reg(pos, is_64bit_address(control)), + &maskbits); + temp = (1 << multi_msi_capable(control)); + temp = ((temp - 1) & ~temp); + maskbits |= temp; + pci_write_config_dword(dev, + msi_mask_bits_reg(pos, is_64bit_address(control)), + maskbits); + } + + return 0; +} + /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with a single - * MSI irq, regardless of device function is capable of handling + * MSI vector, regardless of device function is capable of handling * multiple messages. A return of zero indicates the successful setup - * of an entry zero with the new MSI irq or non-zero for otherwise. + * of an entry zero with the new MSI vector or non-zero for otherwise. **/ static int msi_capability_init(struct pci_dev *dev) { int status; struct msi_desc *entry; - int pos, irq; + int pos, vector; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSI); pci_read_config_word(dev, msi_control_reg(pos), &control); /* MSI Entry Initialization */ - irq = create_msi_irq(); - if (irq < 0) - return irq; + entry = alloc_msi_entry(); + if (!entry) + return -ENOMEM; - entry = get_irq_data(irq); - entry->link.head = irq; - entry->link.tail = irq; + vector = get_msi_vector(dev); + if (vector < 0) { + kmem_cache_free(msi_cachep, entry); + return -EBUSY; + } + entry->link.head = vector; + entry->link.tail = vector; entry->msi_attrib.type = PCI_CAP_ID_MSI; - entry->msi_attrib.is_64 = is_64bit_address(control); + entry->msi_attrib.state = 0; /* Mark it not active */ entry->msi_attrib.entry_nr = 0; entry->msi_attrib.maskbit = is_mask_bit_support(control); - entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ - entry->msi_attrib.pos = pos; + entry->msi_attrib.default_vector = dev->irq; /* Save IOAPIC IRQ */ + dev->irq = vector; + entry->dev = dev; if (is_mask_bit_support(control)) { entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos, is_64bit_address(control)); } - entry->dev = dev; - if (entry->msi_attrib.maskbit) { - unsigned int maskbits, temp; - /* All MSIs are unmasked by default, Mask them all */ - pci_read_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - &maskbits); - temp = (1 << multi_msi_capable(control)); - temp = ((temp - 1) & ~temp); - maskbits |= temp; - pci_write_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - maskbits); - } + /* Replace with MSI handler */ + irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); /* Configure MSI capability structure */ - status = arch_setup_msi_irq(irq, dev); - if (status < 0) { - destroy_msi_irq(irq); + status = msi_register_init(dev, entry); + if (status != 0) { + dev->irq = entry->msi_attrib.default_vector; + kmem_cache_free(msi_cachep, entry); return status; } - attach_msi_entry(entry, irq); + attach_msi_entry(entry, vector); /* Set MSI enabled bits */ enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); - dev->irq = irq; return 0; } @@ -536,15 +794,18 @@ static int msi_capability_init(struct pci_dev *dev) * @nvec: number of @entries * * Setup the MSI-X capability structure of device function with a - * single MSI-X irq. A return of zero indicates the successful setup of - * requested MSI-X entries with allocated irqs or non-zero for otherwise. + * single MSI-X vector. A return of zero indicates the successful setup of + * requested MSI-X entries with allocated vectors or non-zero for otherwise. **/ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; + u32 address_hi; + u32 address_lo; + u32 data; int status; - int irq, pos, i, j, nr_entries, temp = 0; + int vector, pos, i, j, nr_entries, temp = 0; unsigned long phys_addr; u32 table_offset; u16 control; @@ -566,56 +827,65 @@ static int msix_capability_init(struct pci_dev *dev, /* MSI-X Table Initialization */ for (i = 0; i < nvec; i++) { - irq = create_msi_irq(); - if (irq < 0) + entry = alloc_msi_entry(); + if (!entry) break; + vector = get_msi_vector(dev); + if (vector < 0) { + kmem_cache_free(msi_cachep, entry); + break; + } - entry = get_irq_data(irq); j = entries[i].entry; - entries[i].vector = irq; + entries[i].vector = vector; entry->msi_attrib.type = PCI_CAP_ID_MSIX; - entry->msi_attrib.is_64 = 1; + entry->msi_attrib.state = 0; /* Mark it not active */ entry->msi_attrib.entry_nr = j; entry->msi_attrib.maskbit = 1; - entry->msi_attrib.default_irq = dev->irq; - entry->msi_attrib.pos = pos; + entry->msi_attrib.default_vector = dev->irq; entry->dev = dev; entry->mask_base = base; if (!head) { - entry->link.head = irq; - entry->link.tail = irq; + entry->link.head = vector; + entry->link.tail = vector; head = entry; } else { entry->link.head = temp; entry->link.tail = tail->link.tail; - tail->link.tail = irq; - head->link.head = irq; + tail->link.tail = vector; + head->link.head = vector; } - temp = irq; + temp = vector; tail = entry; + /* Replace with MSI-X handler */ + irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); /* Configure MSI-X capability structure */ - status = arch_setup_msi_irq(irq, dev); - if (status < 0) { - destroy_msi_irq(irq); + status = msi_ops->setup(dev, vector, + &address_hi, + &address_lo, + &data); + if (status < 0) break; - } - attach_msi_entry(entry, irq); + writel(address_lo, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(address_hi, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(data, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + attach_msi_entry(entry, vector); } if (i != nvec) { - int avail = i - 1; i--; for (; i >= 0; i--) { - irq = (entries + i)->vector; - msi_free_irq(dev, irq); + vector = (entries + i)->vector; + msi_free_vector(dev, vector, 0); (entries + i)->vector = 0; } - /* If we had some success report the number of irqs - * we succeeded in setting up. - */ - if (avail <= 0) - avail = -EBUSY; - return avail; + return -EBUSY; } /* Set MSI-X enabled bits */ enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); @@ -655,14 +925,15 @@ int pci_msi_supported(struct pci_dev * dev) * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with - * a single MSI irq upon its software driver call to request for + * a single MSI vector upon its software driver call to request for * MSI mode enabled on its hardware device function. A return of zero * indicates the successful setup of an entry zero with the new MSI - * irq or non-zero for otherwise. + * vector or non-zero for otherwise. **/ int pci_enable_msi(struct pci_dev* dev) { int pos, temp, status; + u16 control; if (pci_msi_supported(dev) < 0) return -EINVAL; @@ -677,25 +948,52 @@ int pci_enable_msi(struct pci_dev* dev) if (!pos) return -EINVAL; - WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSI)); + if (!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { + /* Lookup Sucess */ + unsigned long flags; - /* Check whether driver already requested for MSI-X irqs */ + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (control & PCI_MSI_FLAGS_ENABLE) + return 0; /* Already in MSI mode */ + spin_lock_irqsave(&msi_lock, flags); + if (!vector_irq[dev->irq]) { + msi_desc[dev->irq]->msi_attrib.state = 0; + vector_irq[dev->irq] = -1; + nr_released_vectors--; + spin_unlock_irqrestore(&msi_lock, flags); + status = msi_register_init(dev, msi_desc[dev->irq]); + if (status == 0) + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + return status; + } + spin_unlock_irqrestore(&msi_lock, flags); + dev->irq = temp; + } + /* Check whether driver already requested for MSI-X vectors */ pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { + if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { printk(KERN_INFO "PCI: %s: Can't enable MSI. " - "Device already has MSI-X irq assigned\n", + "Device already has MSI-X vectors assigned\n", pci_name(dev)); dev->irq = temp; return -EINVAL; } status = msi_capability_init(dev); + if (!status) { + if (!pos) + nr_reserved_vectors--; /* Only MSI capable */ + else if (nr_msix_devices > 0) + nr_msix_devices--; /* Both MSI and MSI-X capable, + but choose enabling MSI */ + } + return status; } void pci_disable_msi(struct pci_dev* dev) { struct msi_desc *entry; - int pos, default_irq; + int pos, default_vector; u16 control; unsigned long flags; @@ -712,41 +1010,41 @@ void pci_disable_msi(struct pci_dev* dev) if (!(control & PCI_MSI_FLAGS_ENABLE)) return; - disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); - spin_lock_irqsave(&msi_lock, flags); entry = msi_desc[dev->irq]; if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) { spin_unlock_irqrestore(&msi_lock, flags); return; } - if (irq_has_action(dev->irq)) { + if (entry->msi_attrib.state) { spin_unlock_irqrestore(&msi_lock, flags); printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without " - "free_irq() on MSI irq %d\n", + "free_irq() on MSI vector %d\n", pci_name(dev), dev->irq); - BUG_ON(irq_has_action(dev->irq)); + BUG_ON(entry->msi_attrib.state > 0); } else { - default_irq = entry->msi_attrib.default_irq; + vector_irq[dev->irq] = 0; /* free it */ + nr_released_vectors++; + default_vector = entry->msi_attrib.default_vector; spin_unlock_irqrestore(&msi_lock, flags); - msi_free_irq(dev, dev->irq); - - /* Restore dev->irq to its default pin-assertion irq */ - dev->irq = default_irq; + /* Restore dev->irq to its default pin-assertion vector */ + dev->irq = default_vector; + disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), + PCI_CAP_ID_MSI); } } -static int msi_free_irq(struct pci_dev* dev, int irq) +static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) { struct msi_desc *entry; int head, entry_nr, type; void __iomem *base; unsigned long flags; - arch_teardown_msi_irq(irq); + msi_ops->teardown(vector); spin_lock_irqsave(&msi_lock, flags); - entry = msi_desc[irq]; + entry = msi_desc[vector]; if (!entry || entry->dev != dev) { spin_unlock_irqrestore(&msi_lock, flags); return -EINVAL; @@ -758,42 +1056,100 @@ static int msi_free_irq(struct pci_dev* dev, int irq) msi_desc[entry->link.head]->link.tail = entry->link.tail; msi_desc[entry->link.tail]->link.head = entry->link.head; entry->dev = NULL; - msi_desc[irq] = NULL; + if (!reassign) { + vector_irq[vector] = 0; + nr_released_vectors++; + } + msi_desc[vector] = NULL; spin_unlock_irqrestore(&msi_lock, flags); - destroy_msi_irq(irq); + kmem_cache_free(msi_cachep, entry); if (type == PCI_CAP_ID_MSIX) { - writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); + if (!reassign) + writel(1, base + + entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); - if (head == irq) + if (head == vector) iounmap(base); } return 0; } +static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec) +{ + int vector = head, tail = 0; + int i, j = 0, nr_entries = 0; + void __iomem *base; + unsigned long flags; + + spin_lock_irqsave(&msi_lock, flags); + while (head != tail) { + nr_entries++; + tail = msi_desc[vector]->link.tail; + if (entries[0].entry == msi_desc[vector]->msi_attrib.entry_nr) + j = vector; + vector = tail; + } + if (*nvec > nr_entries) { + spin_unlock_irqrestore(&msi_lock, flags); + *nvec = nr_entries; + return -EINVAL; + } + vector = ((j > 0) ? j : head); + for (i = 0; i < *nvec; i++) { + j = msi_desc[vector]->msi_attrib.entry_nr; + msi_desc[vector]->msi_attrib.state = 0; /* Mark it not active */ + vector_irq[vector] = -1; /* Mark it busy */ + nr_released_vectors--; + entries[i].vector = vector; + if (j != (entries + i)->entry) { + base = msi_desc[vector]->mask_base; + msi_desc[vector]->msi_attrib.entry_nr = + (entries + i)->entry; + writel( readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET), base + + (entries + i)->entry * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel( readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET), base + + (entries + i)->entry * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel( (readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET) & 0xff00) | vector, + base + (entries+i)->entry*PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + } + vector = msi_desc[vector]->link.tail; + } + spin_unlock_irqrestore(&msi_lock, flags); + + return 0; +} + /** * pci_enable_msix - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of MSI-X entries - * @nvec: number of MSI-X irqs requested for allocation by device driver + * @nvec: number of MSI-X vectors requested for allocation by device driver * * Setup the MSI-X capability structure of device function with the number - * of requested irqs upon its software driver call to request for + * of requested vectors upon its software driver call to request for * MSI-X mode enabled on its hardware device function. A return of zero * indicates the successful configuration of MSI-X capability structure - * with new allocated MSI-X irqs. A return of < 0 indicates a failure. + * with new allocated MSI-X vectors. A return of < 0 indicates a failure. * Or a return of > 0 indicates that driver request is exceeding the number - * of irqs available. Driver should use the returned value to re-send + * of vectors available. Driver should use the returned value to re-send * its request. **/ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) { - int status, pos, nr_entries; + int status, pos, nr_entries, free_vectors; int i, j, temp; u16 control; + unsigned long flags; if (!entries || pci_msi_supported(dev) < 0) return -EINVAL; @@ -807,6 +1163,9 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) return -EINVAL; pci_read_config_word(dev, msi_control_reg(pos), &control); + if (control & PCI_MSIX_FLAGS_ENABLE) + return -EINVAL; /* Already in MSI-X mode */ + nr_entries = multi_msix_capable(control); if (nvec > nr_entries) return -EINVAL; @@ -821,18 +1180,56 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) } } temp = dev->irq; - WARN_ON(!msi_lookup_irq(dev, PCI_CAP_ID_MSIX)); - - /* Check whether driver already requested for MSI irq */ + if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { + /* Lookup Sucess */ + nr_entries = nvec; + /* Reroute MSI-X table */ + if (reroute_msix_table(dev->irq, entries, &nr_entries)) { + /* #requested > #previous-assigned */ + dev->irq = temp; + return nr_entries; + } + dev->irq = temp; + enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + return 0; + } + /* Check whether driver already requested for MSI vector */ if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 && - !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) { + !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { printk(KERN_INFO "PCI: %s: Can't enable MSI-X. " - "Device already has an MSI irq assigned\n", + "Device already has an MSI vector assigned\n", pci_name(dev)); dev->irq = temp; return -EINVAL; } + + spin_lock_irqsave(&msi_lock, flags); + /* + * msi_lock is provided to ensure that enough vectors resources are + * available before granting. + */ + free_vectors = pci_vector_resources(last_alloc_vector, + nr_released_vectors); + /* Ensure that each MSI/MSI-X device has one vector reserved by + default to avoid any MSI-X driver to take all available + resources */ + free_vectors -= nr_reserved_vectors; + /* Find the average of free vectors among MSI-X devices */ + if (nr_msix_devices > 0) + free_vectors /= nr_msix_devices; + spin_unlock_irqrestore(&msi_lock, flags); + + if (nvec > free_vectors) { + if (free_vectors > 0) + return free_vectors; + else + return -EBUSY; + } + status = msix_capability_init(dev, entries, nvec); + if (!status && nr_msix_devices > 0) + nr_msix_devices--; + return status; } @@ -854,47 +1251,53 @@ void pci_disable_msix(struct pci_dev* dev) if (!(control & PCI_MSIX_FLAGS_ENABLE)) return; - disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); - temp = dev->irq; - if (!msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { - int irq, head, tail = 0, warning = 0; + if (!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { + int state, vector, head, tail = 0, warning = 0; unsigned long flags; - irq = head = dev->irq; - dev->irq = temp; /* Restore pin IRQ */ + vector = head = dev->irq; + spin_lock_irqsave(&msi_lock, flags); while (head != tail) { - spin_lock_irqsave(&msi_lock, flags); - tail = msi_desc[irq]->link.tail; - spin_unlock_irqrestore(&msi_lock, flags); - if (irq_has_action(irq)) + state = msi_desc[vector]->msi_attrib.state; + if (state) warning = 1; - else if (irq != head) /* Release MSI-X irq */ - msi_free_irq(dev, irq); - irq = tail; + else { + vector_irq[vector] = 0; /* free it */ + nr_released_vectors++; + } + tail = msi_desc[vector]->link.tail; + vector = tail; } - msi_free_irq(dev, irq); + spin_unlock_irqrestore(&msi_lock, flags); if (warning) { + dev->irq = temp; printk(KERN_WARNING "PCI: %s: pci_disable_msix() called without " - "free_irq() on all MSI-X irqs\n", + "free_irq() on all MSI-X vectors\n", pci_name(dev)); BUG_ON(warning > 0); + } else { + dev->irq = temp; + disable_msi_mode(dev, + pci_find_capability(dev, PCI_CAP_ID_MSIX), + PCI_CAP_ID_MSIX); + } } } /** - * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state + * msi_remove_pci_irq_vectors - reclaim MSI(X) vectors to unused state * @dev: pointer to the pci_dev data structure of MSI(X) device function * * Being called during hotplug remove, from which the device function - * is hot-removed. All previous assigned MSI/MSI-X irqs, if + * is hot-removed. All previous assigned MSI/MSI-X vectors, if * allocated for this device function, are reclaimed to unused state, * which may be used later on. **/ void msi_remove_pci_irq_vectors(struct pci_dev* dev) { - int pos, temp; + int state, pos, temp; unsigned long flags; if (!pci_msi_enable || !dev) @@ -902,38 +1305,42 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev) temp = dev->irq; /* Save IOAPIC IRQ */ pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSI)) { - if (irq_has_action(dev->irq)) { + if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { + spin_lock_irqsave(&msi_lock, flags); + state = msi_desc[dev->irq]->msi_attrib.state; + spin_unlock_irqrestore(&msi_lock, flags); + if (state) { printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() " - "called without free_irq() on MSI irq %d\n", + "called without free_irq() on MSI vector %d\n", pci_name(dev), dev->irq); - BUG_ON(irq_has_action(dev->irq)); - } else /* Release MSI irq assigned to this device */ - msi_free_irq(dev, dev->irq); + BUG_ON(state > 0); + } else /* Release MSI vector assigned to this device */ + msi_free_vector(dev, dev->irq, 0); dev->irq = temp; /* Restore IOAPIC IRQ */ } pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos > 0 && !msi_lookup_irq(dev, PCI_CAP_ID_MSIX)) { - int irq, head, tail = 0, warning = 0; + if (pos > 0 && !msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { + int vector, head, tail = 0, warning = 0; void __iomem *base = NULL; - irq = head = dev->irq; + vector = head = dev->irq; while (head != tail) { spin_lock_irqsave(&msi_lock, flags); - tail = msi_desc[irq]->link.tail; - base = msi_desc[irq]->mask_base; + state = msi_desc[vector]->msi_attrib.state; + tail = msi_desc[vector]->link.tail; + base = msi_desc[vector]->mask_base; spin_unlock_irqrestore(&msi_lock, flags); - if (irq_has_action(irq)) + if (state) warning = 1; - else if (irq != head) /* Release MSI-X irq */ - msi_free_irq(dev, irq); - irq = tail; + else if (vector != head) /* Release MSI-X vector */ + msi_free_vector(dev, vector, 0); + vector = tail; } - msi_free_irq(dev, irq); + msi_free_vector(dev, vector, 0); if (warning) { iounmap(base); printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() " - "called without free_irq() on all MSI-X irqs\n", + "called without free_irq() on all MSI-X vectors\n", pci_name(dev)); BUG_ON(warning > 0); } diff --git a/trunk/drivers/pci/msi.h b/trunk/drivers/pci/msi.h index f0cca1772f9c..56951c39d3a3 100644 --- a/trunk/drivers/pci/msi.h +++ b/trunk/drivers/pci/msi.h @@ -6,6 +6,84 @@ #ifndef MSI_H #define MSI_H +/* + * MSI operation vector. Used by the msi core code (drivers/pci/msi.c) + * to abstract platform-specific tasks relating to MSI address generation + * and resource management. + */ +struct msi_ops { + /** + * setup - generate an MSI bus address and data for a given vector + * @pdev: PCI device context (in) + * @vector: vector allocated by the msi core (in) + * @addr_hi: upper 32 bits of PCI bus MSI address (out) + * @addr_lo: lower 32 bits of PCI bus MSI address (out) + * @data: MSI data payload (out) + * + * Description: The setup op is used to generate a PCI bus addres and + * data which the msi core will program into the card MSI capability + * registers. The setup routine is responsible for picking an initial + * cpu to target the MSI at. The setup routine is responsible for + * examining pdev to determine the MSI capabilities of the card and + * generating a suitable address/data. The setup routine is + * responsible for allocating and tracking any system resources it + * needs to route the MSI to the cpu it picks, and for associating + * those resources with the passed in vector. + * + * Returns 0 if the MSI address/data was successfully setup. + **/ + + int (*setup) (struct pci_dev *pdev, unsigned int vector, + u32 *addr_hi, u32 *addr_lo, u32 *data); + + /** + * teardown - release resources allocated by setup + * @vector: vector context for resources (in) + * + * Description: The teardown op is used to release any resources + * that were allocated in the setup routine associated with the passed + * in vector. + **/ + + void (*teardown) (unsigned int vector); + + /** + * target - retarget an MSI at a different cpu + * @vector: vector context for resources (in) + * @cpu: new cpu to direct vector at (in) + * @addr_hi: new value of PCI bus upper 32 bits (in/out) + * @addr_lo: new value of PCI bus lower 32 bits (in/out) + * + * Description: The target op is used to redirect an MSI vector + * at a different cpu. addr_hi/addr_lo coming in are the existing + * values that the MSI core has programmed into the card. The + * target code is responsible for freeing any resources (if any) + * associated with the old address, and generating a new PCI bus + * addr_hi/addr_lo that will redirect the vector at the indicated cpu. + **/ + + void (*target) (unsigned int vector, unsigned int cpu, + u32 *addr_hi, u32 *addr_lo); +}; + +extern int msi_register(struct msi_ops *ops); + +#include + +/* + * Assume the maximum number of hot plug slots supported by the system is about + * ten. The worstcase is that each of these slots is hot-added with a device, + * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which + * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined + * as below to ensure at least one message is assigned to each detected MSI/ + * MSI-X device function. + */ +#define NR_HP_RESERVED_VECTORS 20 + +extern int vector_irq[NR_VECTORS]; +extern void (*interrupt[NR_IRQS])(void); +extern int pci_vector_resources(int last, int nr_released); + /* * MSI-X Address Register */ @@ -32,8 +110,8 @@ (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) #define multi_msi_enable(control, num) \ control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); -#define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) -#define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) +#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) +#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) #define msi_enable(control, num) multi_msi_enable(control, num); \ control |= PCI_MSI_FLAGS_ENABLE @@ -47,4 +125,32 @@ #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) +struct msi_desc { + struct { + __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ + __u8 maskbit : 1; /* mask-pending bit supported ? */ + __u8 state : 1; /* {0: free, 1: busy} */ + __u8 reserved: 1; /* reserved */ + __u8 entry_nr; /* specific enabled entry */ + __u8 default_vector; /* default pre-assigned vector */ + __u8 unused; /* formerly unused destination cpu*/ + }msi_attrib; + + struct { + __u16 head; + __u16 tail; + }link; + + void __iomem *mask_base; + struct pci_dev *dev; + +#ifdef CONFIG_PM + /* PM save area for MSIX address/data */ + + u32 address_hi_save; + u32 address_lo_save; + u32 data_save; +#endif +}; + #endif /* MSI_H */ diff --git a/trunk/drivers/pci/setup-bus.c b/trunk/drivers/pci/setup-bus.c index 8f7bcf56f149..54404917be9a 100644 --- a/trunk/drivers/pci/setup-bus.c +++ b/trunk/drivers/pci/setup-bus.c @@ -55,16 +55,16 @@ pbus_assign_resources_sorted(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { u16 class = dev->class >> 8; - /* Don't touch classless devices or host bridges or ioapics. */ + /* Don't touch classless devices or host bridges. */ if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) continue; - /* Don't touch ioapic devices already enabled by firmware */ + /* Don't touch ioapics if it has the assigned resources. */ if (class == PCI_CLASS_SYSTEM_PIC) { - u16 command; - pci_read_config_word(dev, PCI_COMMAND, &command); - if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) + res = &dev->resource[0]; + if (res[0].start || res[1].start || res[2].start || + res[3].start || res[4].start || res[5].start) continue; } diff --git a/trunk/drivers/rtc/rtc-ds1307.c b/trunk/drivers/rtc/rtc-ds1307.c index 3f0f7b8fa813..cc5032b6f42a 100644 --- a/trunk/drivers/rtc/rtc-ds1307.c +++ b/trunk/drivers/rtc/rtc-ds1307.c @@ -141,9 +141,9 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", - "write", t->tm_sec, t->tm_min, - t->tm_hour, t->tm_mday, - t->tm_mon, t->tm_year, t->tm_wday); + "write", dt->tm_sec, dt->tm_min, + dt->tm_hour, dt->tm_mday, + dt->tm_mon, dt->tm_year, dt->tm_wday); *buf++ = 0; /* first register addr */ buf[DS1307_REG_SECS] = BIN2BCD(t->tm_sec); diff --git a/trunk/drivers/rtc/rtc-ds1672.c b/trunk/drivers/rtc/rtc-ds1672.c index 67e816a9a39f..9c68ec99afa5 100644 --- a/trunk/drivers/rtc/rtc-ds1672.c +++ b/trunk/drivers/rtc/rtc-ds1672.c @@ -55,7 +55,7 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) } dev_dbg(&client->dev, - "%s: raw read data - counters=%02x,%02x,%02x,%02x\n", + "%s: raw read data - counters=%02x,%02x,%02x,%02x\n" __FUNCTION__, buf[0], buf[1], buf[2], buf[3]); time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; @@ -96,7 +96,7 @@ static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm) unsigned long secs; dev_dbg(&client->dev, - "%s: secs=%d, mins=%d, hours=%d, " + "%s: secs=%d, mins=%d, hours=%d, ", "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, diff --git a/trunk/drivers/rtc/rtc-max6902.c b/trunk/drivers/rtc/rtc-max6902.c index 2f0b77724192..0b20dfacbf59 100644 --- a/trunk/drivers/rtc/rtc-max6902.c +++ b/trunk/drivers/rtc/rtc-max6902.c @@ -19,7 +19,6 @@ * - Initial driver creation. */ -#include #include #include diff --git a/trunk/drivers/rtc/rtc-rs5c372.c b/trunk/drivers/rtc/rtc-rs5c372.c index 2a86632580f1..bbdad099471d 100644 --- a/trunk/drivers/rtc/rtc-rs5c372.c +++ b/trunk/drivers/rtc/rtc-rs5c372.c @@ -91,7 +91,7 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) unsigned char buf[8] = { RS5C372_REG_BASE }; dev_dbg(&client->dev, - "%s: secs=%d, mins=%d, hours=%d " + "%s: secs=%d, mins=%d, hours=%d ", "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); @@ -126,7 +126,7 @@ static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) return -EIO; } - dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, *trim); + dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, trim); if (osc) *osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768; diff --git a/trunk/drivers/scsi/aic94xx/aic94xx_init.c b/trunk/drivers/scsi/aic94xx/aic94xx_init.c index ee2ccad70487..734adc9d5206 100644 --- a/trunk/drivers/scsi/aic94xx/aic94xx_init.c +++ b/trunk/drivers/scsi/aic94xx/aic94xx_init.c @@ -24,7 +24,6 @@ * */ -#include #include #include #include diff --git a/trunk/drivers/scsi/imm.h b/trunk/drivers/scsi/imm.h index ece936ac29c7..8f6f32fc61ff 100644 --- a/trunk/drivers/scsi/imm.h +++ b/trunk/drivers/scsi/imm.h @@ -66,7 +66,6 @@ */ /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ -#include #include #include #include diff --git a/trunk/drivers/scsi/ppa.h b/trunk/drivers/scsi/ppa.h index 7511df3588e4..ba8021427b88 100644 --- a/trunk/drivers/scsi/ppa.h +++ b/trunk/drivers/scsi/ppa.h @@ -73,7 +73,6 @@ */ /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ -#include #include #include #include diff --git a/trunk/drivers/serial/8250_gsc.c b/trunk/drivers/serial/8250_gsc.c index c5d0addfda4f..1ebe6b585d2d 100644 --- a/trunk/drivers/serial/8250_gsc.c +++ b/trunk/drivers/serial/8250_gsc.c @@ -22,6 +22,7 @@ #include #include #include +#include /* for LASI_BASE_BAUD */ #include "8250.h" @@ -53,8 +54,7 @@ serial_init_chip(struct parisc_device *dev) memset(&port, 0, sizeof(port)); port.iotype = UPIO_MEM; - /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - port.uartclk = 7272727; + port.uartclk = LASI_BASE_BAUD * 16; port.mapbase = address; port.membase = ioremap_nocache(address, 16); port.irq = dev->irq; diff --git a/trunk/drivers/serial/Kconfig b/trunk/drivers/serial/Kconfig index 8edee745888a..653098bc2dd5 100644 --- a/trunk/drivers/serial/Kconfig +++ b/trunk/drivers/serial/Kconfig @@ -556,11 +556,10 @@ config SERIAL_MUX default y ---help--- Saying Y here will enable the hardware MUX serial driver for - the Nova, K class systems and D class with a 'remote control card'. - The hardware MUX is not 8250/16550 compatible therefore the - /dev/ttyB0 device is shared between the Serial MUX and the PDC - software console. The following steps need to be completed to use - the Serial MUX: + the Nova and K class systems. The hardware MUX is not 8250/16550 + compatible therefore the /dev/ttyB0 device is shared between the + Serial MUX and the PDC software console. The following steps + need to be completed to use the Serial MUX: 1. create the device entry (mknod /dev/ttyB0 c 11 0) 2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0 diff --git a/trunk/drivers/serial/cpm_uart/cpm_uart.h b/trunk/drivers/serial/cpm_uart/cpm_uart.h index 3b35cb779539..a8f894c78194 100644 --- a/trunk/drivers/serial/cpm_uart/cpm_uart.h +++ b/trunk/drivers/serial/cpm_uart/cpm_uart.h @@ -16,7 +16,6 @@ #ifndef CPM_UART_H #define CPM_UART_H -#include #include #include diff --git a/trunk/drivers/serial/netx-serial.c b/trunk/drivers/serial/netx-serial.c index c1adc9e4b239..7502109d37f0 100644 --- a/trunk/drivers/serial/netx-serial.c +++ b/trunk/drivers/serial/netx-serial.c @@ -17,8 +17,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include - #if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif diff --git a/trunk/drivers/serial/sh-sci.c b/trunk/drivers/serial/sh-sci.c index f336ba6778dd..5c025d1190c1 100644 --- a/trunk/drivers/serial/sh-sci.c +++ b/trunk/drivers/serial/sh-sci.c @@ -20,7 +20,6 @@ #undef DEBUG -#include #include #include #include diff --git a/trunk/drivers/serial/sh-sci.h b/trunk/drivers/serial/sh-sci.h index 28643c4dc850..7ee992146ae9 100644 --- a/trunk/drivers/serial/sh-sci.h +++ b/trunk/drivers/serial/sh-sci.h @@ -10,7 +10,6 @@ * Modified to support SH7300(SH-Mobile) SCIF. Takashi Kusuda (Jun 2003). * Modified to support H8/300 Series Yoshinori Sato (Feb 2004). */ -#include #include #include diff --git a/trunk/drivers/spi/spi_s3c24xx.c b/trunk/drivers/spi/spi_s3c24xx.c index 5fc14563ee3a..20eb6e95a3a0 100644 --- a/trunk/drivers/spi/spi_s3c24xx.c +++ b/trunk/drivers/spi/spi_s3c24xx.c @@ -13,7 +13,6 @@ //#define DEBUG -#include #include #include #include diff --git a/trunk/drivers/spi/spi_s3c24xx_gpio.c b/trunk/drivers/spi/spi_s3c24xx_gpio.c index aacdceb8f44b..a5d2cdfff46f 100644 --- a/trunk/drivers/spi/spi_s3c24xx_gpio.c +++ b/trunk/drivers/spi/spi_s3c24xx_gpio.c @@ -11,7 +11,6 @@ * */ -#include #include #include #include diff --git a/trunk/drivers/usb/core/generic.c b/trunk/drivers/usb/core/generic.c index 16332cc57946..ebb20ff7ac58 100644 --- a/trunk/drivers/usb/core/generic.c +++ b/trunk/drivers/usb/core/generic.c @@ -17,7 +17,6 @@ * */ -#include #include #include "usb.h" diff --git a/trunk/drivers/usb/gadget/gmidi.c b/trunk/drivers/usb/gadget/gmidi.c index 83601d4009e3..64554acad63f 100644 --- a/trunk/drivers/usb/gadget/gmidi.c +++ b/trunk/drivers/usb/gadget/gmidi.c @@ -21,7 +21,6 @@ #define DEBUG 1 // #define VERBOSE -#include #include #include #include diff --git a/trunk/drivers/usb/host/u132-hcd.c b/trunk/drivers/usb/host/u132-hcd.c index cb2e2a604d1b..0a315200b331 100644 --- a/trunk/drivers/usb/host/u132-hcd.c +++ b/trunk/drivers/usb/host/u132-hcd.c @@ -35,7 +35,6 @@ * via an ELAN U132 adapter. * */ -#include #include #include #include diff --git a/trunk/drivers/usb/input/usbtouchscreen.c b/trunk/drivers/usb/input/usbtouchscreen.c index 4640d1000d83..923e22db18d4 100644 --- a/trunk/drivers/usb/input/usbtouchscreen.c +++ b/trunk/drivers/usb/input/usbtouchscreen.c @@ -35,7 +35,6 @@ //#define DEBUG -#include #include #include #include diff --git a/trunk/drivers/usb/misc/appledisplay.c b/trunk/drivers/usb/misc/appledisplay.c index bfde82f5d180..fc6cc147996f 100644 --- a/trunk/drivers/usb/misc/appledisplay.c +++ b/trunk/drivers/usb/misc/appledisplay.c @@ -20,7 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include #include #include #include diff --git a/trunk/drivers/usb/misc/ftdi-elan.c b/trunk/drivers/usb/misc/ftdi-elan.c index b88a09497c28..c6f2f488a40f 100644 --- a/trunk/drivers/usb/misc/ftdi-elan.c +++ b/trunk/drivers/usb/misc/ftdi-elan.c @@ -35,7 +35,6 @@ * via an ELAN U132 adapter. * */ -#include #include #include #include diff --git a/trunk/drivers/usb/misc/sisusbvga/sisusb.c b/trunk/drivers/usb/misc/sisusbvga/sisusb.c index a44124c7e851..a287836e39f1 100644 --- a/trunk/drivers/usb/misc/sisusbvga/sisusb.c +++ b/trunk/drivers/usb/misc/sisusbvga/sisusb.c @@ -36,7 +36,6 @@ * */ -#include #include #include #include diff --git a/trunk/drivers/usb/misc/sisusbvga/sisusb_con.c b/trunk/drivers/usb/misc/sisusbvga/sisusb_con.c index fb48feca8353..bf26c3c56990 100644 --- a/trunk/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/trunk/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -47,7 +47,6 @@ * */ -#include #include #include #include diff --git a/trunk/drivers/video/intelfb/intelfb_i2c.c b/trunk/drivers/video/intelfb/intelfb_i2c.c index c1113d6e941d..5686e2164e39 100644 --- a/trunk/drivers/video/intelfb/intelfb_i2c.c +++ b/trunk/drivers/video/intelfb/intelfb_i2c.c @@ -25,7 +25,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. **************************************************************************/ -#include #include #include #include diff --git a/trunk/drivers/video/riva/fbdev.c b/trunk/drivers/video/riva/fbdev.c index a433cc78ef90..b120896c8ab4 100644 --- a/trunk/drivers/video/riva/fbdev.c +++ b/trunk/drivers/video/riva/fbdev.c @@ -1843,7 +1843,7 @@ static int __devinit riva_get_EDID_OF(struct fb_info *info, struct pci_dev *pd) for (i = 0; propnames[i] != NULL; ++i) { pedid = get_property(dp, propnames[i], NULL); if (pedid != NULL) { - par->EDID = (unsigned char *)pedid; + par->EDID = pedid; NVTRACE("LCD found.\n"); return 1; } diff --git a/trunk/fs/Kconfig b/trunk/fs/Kconfig index 599de54451af..68f4561423ff 100644 --- a/trunk/fs/Kconfig +++ b/trunk/fs/Kconfig @@ -325,7 +325,6 @@ config FS_POSIX_ACL default n source "fs/xfs/Kconfig" -source "fs/gfs2/Kconfig" config OCFS2_FS tristate "OCFS2 file system support" @@ -996,18 +995,6 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. -config ECRYPT_FS - tristate "eCrypt filesystem layer support (EXPERIMENTAL)" - depends on EXPERIMENTAL && KEYS && CRYPTO - help - Encrypted filesystem that operates on the VFS layer. See - to learn more about - eCryptfs. Userspace components are required and can be - obtained from . - - To compile this file system support as a module, choose M here: the - module will be called ecryptfs. - config HFS_FS tristate "Apple Macintosh file system support (EXPERIMENTAL)" depends on BLOCK && EXPERIMENTAL @@ -1996,7 +1983,6 @@ endmenu endif source "fs/nls/Kconfig" -source "fs/dlm/Kconfig" endmenu diff --git a/trunk/fs/Makefile b/trunk/fs/Makefile index df614eacee86..819b2a93bebe 100644 --- a/trunk/fs/Makefile +++ b/trunk/fs/Makefile @@ -57,7 +57,6 @@ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-y += devpts/ obj-$(CONFIG_PROFILING) += dcookies.o -obj-$(CONFIG_DLM) += dlm/ # Do not add any filesystems before this line obj-$(CONFIG_REISERFS_FS) += reiserfs/ @@ -76,7 +75,6 @@ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ -obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ @@ -111,4 +109,3 @@ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ -obj-$(CONFIG_GFS2_FS) += gfs2/ diff --git a/trunk/fs/binfmt_som.c b/trunk/fs/binfmt_som.c index 5bcdaaf4eae0..32b5d625ce9c 100644 --- a/trunk/fs/binfmt_som.c +++ b/trunk/fs/binfmt_som.c @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -195,7 +194,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) unsigned long som_entry; struct som_hdr *som_ex; struct som_exec_auxhdr *hpuxhdr; - struct files_struct *files; /* Get the exec-header */ som_ex = (struct som_hdr *) bprm->buf; @@ -210,27 +208,15 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) size = som_ex->aux_header_size; if (size > SOM_PAGESIZE) goto out; - hpuxhdr = kmalloc(size, GFP_KERNEL); + hpuxhdr = (struct som_exec_auxhdr *) kmalloc(size, GFP_KERNEL); if (!hpuxhdr) goto out; retval = kernel_read(bprm->file, som_ex->aux_header_location, (char *) hpuxhdr, size); - if (retval != size) { - if (retval >= 0) - retval = -EIO; - goto out_free; - } - - files = current->files; /* Refcounted so ok */ - retval = unshare_files(); if (retval < 0) goto out_free; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } - +#error "Fix security hole before enabling me" retval = get_unused_fd(); if (retval < 0) goto out_free; diff --git a/trunk/fs/configfs/item.c b/trunk/fs/configfs/item.c index 24421209f854..e07485ac50ad 100644 --- a/trunk/fs/configfs/item.c +++ b/trunk/fs/configfs/item.c @@ -224,4 +224,4 @@ EXPORT_SYMBOL(config_item_init); EXPORT_SYMBOL(config_group_init); EXPORT_SYMBOL(config_item_get); EXPORT_SYMBOL(config_item_put); -EXPORT_SYMBOL(config_group_find_obj); + diff --git a/trunk/fs/dcache.c b/trunk/fs/dcache.c index 2355bddad8de..fc2faa44f8d1 100644 --- a/trunk/fs/dcache.c +++ b/trunk/fs/dcache.c @@ -291,9 +291,9 @@ struct dentry * dget_locked(struct dentry *dentry) * it can be unhashed only if it has no children, or if it is the root * of a filesystem. * - * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer + * If the inode has a DCACHE_DISCONNECTED alias, then prefer * any other hashed alias over that one unless @want_discon is set, - * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias. + * in which case only return a DCACHE_DISCONNECTED alias. */ static struct dentry * __d_find_alias(struct inode *inode, int want_discon) @@ -309,8 +309,7 @@ static struct dentry * __d_find_alias(struct inode *inode, int want_discon) prefetch(next); alias = list_entry(tmp, struct dentry, d_alias); if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) { - if (IS_ROOT(alias) && - (alias->d_flags & DCACHE_DISCONNECTED)) + if (alias->d_flags & DCACHE_DISCONNECTED) discon_alias = alias; else if (!want_discon) { __dget_locked(alias); @@ -1005,7 +1004,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) { struct dentry *new = NULL; - if (inode && S_ISDIR(inode->i_mode)) { + if (inode) { spin_lock(&dcache_lock); new = __d_find_alias(inode, 1); if (new) { diff --git a/trunk/fs/dlm/Kconfig b/trunk/fs/dlm/Kconfig deleted file mode 100644 index 490f85b3fa59..000000000000 --- a/trunk/fs/dlm/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -menu "Distributed Lock Manager" - depends on INET && EXPERIMENTAL - -config DLM - tristate "Distributed Lock Manager (DLM)" - depends on IPV6 || IPV6=n - depends on IP_SCTP - select CONFIGFS_FS - help - A general purpose distributed lock manager for kernel or userspace - applications. - -config DLM_DEBUG - bool "DLM debugging" - depends on DLM - help - Under the debugfs mount point, the name of each lockspace will - appear as a file in the "dlm" directory. The output is the - list of resource and locks the local node knows about. - -endmenu diff --git a/trunk/fs/dlm/Makefile b/trunk/fs/dlm/Makefile deleted file mode 100644 index 1832e0297f7d..000000000000 --- a/trunk/fs/dlm/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -obj-$(CONFIG_DLM) += dlm.o -dlm-y := ast.o \ - config.o \ - dir.o \ - lock.o \ - lockspace.o \ - lowcomms.o \ - main.o \ - member.o \ - memory.o \ - midcomms.o \ - rcom.o \ - recover.o \ - recoverd.o \ - requestqueue.o \ - user.o \ - util.o -dlm-$(CONFIG_DLM_DEBUG) += debug_fs.o - diff --git a/trunk/fs/dlm/ast.c b/trunk/fs/dlm/ast.c deleted file mode 100644 index f91d39cb1e0b..000000000000 --- a/trunk/fs/dlm/ast.c +++ /dev/null @@ -1,173 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lock.h" -#include "user.h" - -#define WAKE_ASTS 0 - -static struct list_head ast_queue; -static spinlock_t ast_queue_lock; -static struct task_struct * astd_task; -static unsigned long astd_wakeflags; -static struct mutex astd_running; - - -void dlm_del_ast(struct dlm_lkb *lkb) -{ - spin_lock(&ast_queue_lock); - if (lkb->lkb_ast_type & (AST_COMP | AST_BAST)) - list_del(&lkb->lkb_astqueue); - spin_unlock(&ast_queue_lock); -} - -void dlm_add_ast(struct dlm_lkb *lkb, int type) -{ - if (lkb->lkb_flags & DLM_IFL_USER) { - dlm_user_add_ast(lkb, type); - return; - } - DLM_ASSERT(lkb->lkb_astaddr != DLM_FAKE_USER_AST, dlm_print_lkb(lkb);); - - spin_lock(&ast_queue_lock); - if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) { - kref_get(&lkb->lkb_ref); - list_add_tail(&lkb->lkb_astqueue, &ast_queue); - } - lkb->lkb_ast_type |= type; - spin_unlock(&ast_queue_lock); - - set_bit(WAKE_ASTS, &astd_wakeflags); - wake_up_process(astd_task); -} - -static void process_asts(void) -{ - struct dlm_ls *ls = NULL; - struct dlm_rsb *r = NULL; - struct dlm_lkb *lkb; - void (*cast) (long param); - void (*bast) (long param, int mode); - int type = 0, found, bmode; - - for (;;) { - found = 0; - spin_lock(&ast_queue_lock); - list_for_each_entry(lkb, &ast_queue, lkb_astqueue) { - r = lkb->lkb_resource; - ls = r->res_ls; - - if (dlm_locking_stopped(ls)) - continue; - - list_del(&lkb->lkb_astqueue); - type = lkb->lkb_ast_type; - lkb->lkb_ast_type = 0; - found = 1; - break; - } - spin_unlock(&ast_queue_lock); - - if (!found) - break; - - cast = lkb->lkb_astaddr; - bast = lkb->lkb_bastaddr; - bmode = lkb->lkb_bastmode; - - if ((type & AST_COMP) && cast) - cast(lkb->lkb_astparam); - - /* FIXME: Is it safe to look at lkb_grmode here - without doing a lock_rsb() ? - Look at other checks in v1 to avoid basts. */ - - if ((type & AST_BAST) && bast) - if (!dlm_modes_compat(lkb->lkb_grmode, bmode)) - bast(lkb->lkb_astparam, bmode); - - /* this removes the reference added by dlm_add_ast - and may result in the lkb being freed */ - dlm_put_lkb(lkb); - - schedule(); - } -} - -static inline int no_asts(void) -{ - int ret; - - spin_lock(&ast_queue_lock); - ret = list_empty(&ast_queue); - spin_unlock(&ast_queue_lock); - return ret; -} - -static int dlm_astd(void *data) -{ - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - if (!test_bit(WAKE_ASTS, &astd_wakeflags)) - schedule(); - set_current_state(TASK_RUNNING); - - mutex_lock(&astd_running); - if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags)) - process_asts(); - mutex_unlock(&astd_running); - } - return 0; -} - -void dlm_astd_wake(void) -{ - if (!no_asts()) { - set_bit(WAKE_ASTS, &astd_wakeflags); - wake_up_process(astd_task); - } -} - -int dlm_astd_start(void) -{ - struct task_struct *p; - int error = 0; - - INIT_LIST_HEAD(&ast_queue); - spin_lock_init(&ast_queue_lock); - mutex_init(&astd_running); - - p = kthread_run(dlm_astd, NULL, "dlm_astd"); - if (IS_ERR(p)) - error = PTR_ERR(p); - else - astd_task = p; - return error; -} - -void dlm_astd_stop(void) -{ - kthread_stop(astd_task); -} - -void dlm_astd_suspend(void) -{ - mutex_lock(&astd_running); -} - -void dlm_astd_resume(void) -{ - mutex_unlock(&astd_running); -} - diff --git a/trunk/fs/dlm/ast.h b/trunk/fs/dlm/ast.h deleted file mode 100644 index 6ee276c74c52..000000000000 --- a/trunk/fs/dlm/ast.h +++ /dev/null @@ -1,26 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __ASTD_DOT_H__ -#define __ASTD_DOT_H__ - -void dlm_add_ast(struct dlm_lkb *lkb, int type); -void dlm_del_ast(struct dlm_lkb *lkb); - -void dlm_astd_wake(void); -int dlm_astd_start(void); -void dlm_astd_stop(void); -void dlm_astd_suspend(void); -void dlm_astd_resume(void); - -#endif - diff --git a/trunk/fs/dlm/config.c b/trunk/fs/dlm/config.c deleted file mode 100644 index 88553054bbfa..000000000000 --- a/trunk/fs/dlm/config.c +++ /dev/null @@ -1,789 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include -#include -#include -#include - -#include "config.h" -#include "lowcomms.h" - -/* - * /config/dlm//spaces//nodes//nodeid - * /config/dlm//spaces//nodes//weight - * /config/dlm//comms//nodeid - * /config/dlm//comms//local - * /config/dlm//comms//addr - * The level is useless, but I haven't figured out how to avoid it. - */ - -static struct config_group *space_list; -static struct config_group *comm_list; -static struct comm *local_comm; - -struct clusters; -struct cluster; -struct spaces; -struct space; -struct comms; -struct comm; -struct nodes; -struct node; - -static struct config_group *make_cluster(struct config_group *, const char *); -static void drop_cluster(struct config_group *, struct config_item *); -static void release_cluster(struct config_item *); -static struct config_group *make_space(struct config_group *, const char *); -static void drop_space(struct config_group *, struct config_item *); -static void release_space(struct config_item *); -static struct config_item *make_comm(struct config_group *, const char *); -static void drop_comm(struct config_group *, struct config_item *); -static void release_comm(struct config_item *); -static struct config_item *make_node(struct config_group *, const char *); -static void drop_node(struct config_group *, struct config_item *); -static void release_node(struct config_item *); - -static ssize_t show_comm(struct config_item *i, struct configfs_attribute *a, - char *buf); -static ssize_t store_comm(struct config_item *i, struct configfs_attribute *a, - const char *buf, size_t len); -static ssize_t show_node(struct config_item *i, struct configfs_attribute *a, - char *buf); -static ssize_t store_node(struct config_item *i, struct configfs_attribute *a, - const char *buf, size_t len); - -static ssize_t comm_nodeid_read(struct comm *cm, char *buf); -static ssize_t comm_nodeid_write(struct comm *cm, const char *buf, size_t len); -static ssize_t comm_local_read(struct comm *cm, char *buf); -static ssize_t comm_local_write(struct comm *cm, const char *buf, size_t len); -static ssize_t comm_addr_write(struct comm *cm, const char *buf, size_t len); -static ssize_t node_nodeid_read(struct node *nd, char *buf); -static ssize_t node_nodeid_write(struct node *nd, const char *buf, size_t len); -static ssize_t node_weight_read(struct node *nd, char *buf); -static ssize_t node_weight_write(struct node *nd, const char *buf, size_t len); - -enum { - COMM_ATTR_NODEID = 0, - COMM_ATTR_LOCAL, - COMM_ATTR_ADDR, -}; - -struct comm_attribute { - struct configfs_attribute attr; - ssize_t (*show)(struct comm *, char *); - ssize_t (*store)(struct comm *, const char *, size_t); -}; - -static struct comm_attribute comm_attr_nodeid = { - .attr = { .ca_owner = THIS_MODULE, - .ca_name = "nodeid", - .ca_mode = S_IRUGO | S_IWUSR }, - .show = comm_nodeid_read, - .store = comm_nodeid_write, -}; - -static struct comm_attribute comm_attr_local = { - .attr = { .ca_owner = THIS_MODULE, - .ca_name = "local", - .ca_mode = S_IRUGO | S_IWUSR }, - .show = comm_local_read, - .store = comm_local_write, -}; - -static struct comm_attribute comm_attr_addr = { - .attr = { .ca_owner = THIS_MODULE, - .ca_name = "addr", - .ca_mode = S_IRUGO | S_IWUSR }, - .store = comm_addr_write, -}; - -static struct configfs_attribute *comm_attrs[] = { - [COMM_ATTR_NODEID] = &comm_attr_nodeid.attr, - [COMM_ATTR_LOCAL] = &comm_attr_local.attr, - [COMM_ATTR_ADDR] = &comm_attr_addr.attr, - NULL, -}; - -enum { - NODE_ATTR_NODEID = 0, - NODE_ATTR_WEIGHT, -}; - -struct node_attribute { - struct configfs_attribute attr; - ssize_t (*show)(struct node *, char *); - ssize_t (*store)(struct node *, const char *, size_t); -}; - -static struct node_attribute node_attr_nodeid = { - .attr = { .ca_owner = THIS_MODULE, - .ca_name = "nodeid", - .ca_mode = S_IRUGO | S_IWUSR }, - .show = node_nodeid_read, - .store = node_nodeid_write, -}; - -static struct node_attribute node_attr_weight = { - .attr = { .ca_owner = THIS_MODULE, - .ca_name = "weight", - .ca_mode = S_IRUGO | S_IWUSR }, - .show = node_weight_read, - .store = node_weight_write, -}; - -static struct configfs_attribute *node_attrs[] = { - [NODE_ATTR_NODEID] = &node_attr_nodeid.attr, - [NODE_ATTR_WEIGHT] = &node_attr_weight.attr, - NULL, -}; - -struct clusters { - struct configfs_subsystem subsys; -}; - -struct cluster { - struct config_group group; -}; - -struct spaces { - struct config_group ss_group; -}; - -struct space { - struct config_group group; - struct list_head members; - struct mutex members_lock; - int members_count; -}; - -struct comms { - struct config_group cs_group; -}; - -struct comm { - struct config_item item; - int nodeid; - int local; - int addr_count; - struct sockaddr_storage *addr[DLM_MAX_ADDR_COUNT]; -}; - -struct nodes { - struct config_group ns_group; -}; - -struct node { - struct config_item item; - struct list_head list; /* space->members */ - int nodeid; - int weight; -}; - -static struct configfs_group_operations clusters_ops = { - .make_group = make_cluster, - .drop_item = drop_cluster, -}; - -static struct configfs_item_operations cluster_ops = { - .release = release_cluster, -}; - -static struct configfs_group_operations spaces_ops = { - .make_group = make_space, - .drop_item = drop_space, -}; - -static struct configfs_item_operations space_ops = { - .release = release_space, -}; - -static struct configfs_group_operations comms_ops = { - .make_item = make_comm, - .drop_item = drop_comm, -}; - -static struct configfs_item_operations comm_ops = { - .release = release_comm, - .show_attribute = show_comm, - .store_attribute = store_comm, -}; - -static struct configfs_group_operations nodes_ops = { - .make_item = make_node, - .drop_item = drop_node, -}; - -static struct configfs_item_operations node_ops = { - .release = release_node, - .show_attribute = show_node, - .store_attribute = store_node, -}; - -static struct config_item_type clusters_type = { - .ct_group_ops = &clusters_ops, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type cluster_type = { - .ct_item_ops = &cluster_ops, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type spaces_type = { - .ct_group_ops = &spaces_ops, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type space_type = { - .ct_item_ops = &space_ops, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type comms_type = { - .ct_group_ops = &comms_ops, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type comm_type = { - .ct_item_ops = &comm_ops, - .ct_attrs = comm_attrs, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type nodes_type = { - .ct_group_ops = &nodes_ops, - .ct_owner = THIS_MODULE, -}; - -static struct config_item_type node_type = { - .ct_item_ops = &node_ops, - .ct_attrs = node_attrs, - .ct_owner = THIS_MODULE, -}; - -static struct cluster *to_cluster(struct config_item *i) -{ - return i ? container_of(to_config_group(i), struct cluster, group):NULL; -} - -static struct space *to_space(struct config_item *i) -{ - return i ? container_of(to_config_group(i), struct space, group) : NULL; -} - -static struct comm *to_comm(struct config_item *i) -{ - return i ? container_of(i, struct comm, item) : NULL; -} - -static struct node *to_node(struct config_item *i) -{ - return i ? container_of(i, struct node, item) : NULL; -} - -static struct config_group *make_cluster(struct config_group *g, - const char *name) -{ - struct cluster *cl = NULL; - struct spaces *sps = NULL; - struct comms *cms = NULL; - void *gps = NULL; - - cl = kzalloc(sizeof(struct cluster), GFP_KERNEL); - gps = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL); - sps = kzalloc(sizeof(struct spaces), GFP_KERNEL); - cms = kzalloc(sizeof(struct comms), GFP_KERNEL); - - if (!cl || !gps || !sps || !cms) - goto fail; - - config_group_init_type_name(&cl->group, name, &cluster_type); - config_group_init_type_name(&sps->ss_group, "spaces", &spaces_type); - config_group_init_type_name(&cms->cs_group, "comms", &comms_type); - - cl->group.default_groups = gps; - cl->group.default_groups[0] = &sps->ss_group; - cl->group.default_groups[1] = &cms->cs_group; - cl->group.default_groups[2] = NULL; - - space_list = &sps->ss_group; - comm_list = &cms->cs_group; - return &cl->group; - - fail: - kfree(cl); - kfree(gps); - kfree(sps); - kfree(cms); - return NULL; -} - -static void drop_cluster(struct config_group *g, struct config_item *i) -{ - struct cluster *cl = to_cluster(i); - struct config_item *tmp; - int j; - - for (j = 0; cl->group.default_groups[j]; j++) { - tmp = &cl->group.default_groups[j]->cg_item; - cl->group.default_groups[j] = NULL; - config_item_put(tmp); - } - - space_list = NULL; - comm_list = NULL; - - config_item_put(i); -} - -static void release_cluster(struct config_item *i) -{ - struct cluster *cl = to_cluster(i); - kfree(cl->group.default_groups); - kfree(cl); -} - -static struct config_group *make_space(struct config_group *g, const char *name) -{ - struct space *sp = NULL; - struct nodes *nds = NULL; - void *gps = NULL; - - sp = kzalloc(sizeof(struct space), GFP_KERNEL); - gps = kcalloc(2, sizeof(struct config_group *), GFP_KERNEL); - nds = kzalloc(sizeof(struct nodes), GFP_KERNEL); - - if (!sp || !gps || !nds) - goto fail; - - config_group_init_type_name(&sp->group, name, &space_type); - config_group_init_type_name(&nds->ns_group, "nodes", &nodes_type); - - sp->group.default_groups = gps; - sp->group.default_groups[0] = &nds->ns_group; - sp->group.default_groups[1] = NULL; - - INIT_LIST_HEAD(&sp->members); - mutex_init(&sp->members_lock); - sp->members_count = 0; - return &sp->group; - - fail: - kfree(sp); - kfree(gps); - kfree(nds); - return NULL; -} - -static void drop_space(struct config_group *g, struct config_item *i) -{ - struct space *sp = to_space(i); - struct config_item *tmp; - int j; - - /* assert list_empty(&sp->members) */ - - for (j = 0; sp->group.default_groups[j]; j++) { - tmp = &sp->group.default_groups[j]->cg_item; - sp->group.default_groups[j] = NULL; - config_item_put(tmp); - } - - config_item_put(i); -} - -static void release_space(struct config_item *i) -{ - struct space *sp = to_space(i); - kfree(sp->group.default_groups); - kfree(sp); -} - -static struct config_item *make_comm(struct config_group *g, const char *name) -{ - struct comm *cm; - - cm = kzalloc(sizeof(struct comm), GFP_KERNEL); - if (!cm) - return NULL; - - config_item_init_type_name(&cm->item, name, &comm_type); - cm->nodeid = -1; - cm->local = 0; - cm->addr_count = 0; - return &cm->item; -} - -static void drop_comm(struct config_group *g, struct config_item *i) -{ - struct comm *cm = to_comm(i); - if (local_comm == cm) - local_comm = NULL; - dlm_lowcomms_close(cm->nodeid); - while (cm->addr_count--) - kfree(cm->addr[cm->addr_count]); - config_item_put(i); -} - -static void release_comm(struct config_item *i) -{ - struct comm *cm = to_comm(i); - kfree(cm); -} - -static struct config_item *make_node(struct config_group *g, const char *name) -{ - struct space *sp = to_space(g->cg_item.ci_parent); - struct node *nd; - - nd = kzalloc(sizeof(struct node), GFP_KERNEL); - if (!nd) - return NULL; - - config_item_init_type_name(&nd->item, name, &node_type); - nd->nodeid = -1; - nd->weight = 1; /* default weight of 1 if none is set */ - - mutex_lock(&sp->members_lock); - list_add(&nd->list, &sp->members); - sp->members_count++; - mutex_unlock(&sp->members_lock); - - return &nd->item; -} - -static void drop_node(struct config_group *g, struct config_item *i) -{ - struct space *sp = to_space(g->cg_item.ci_parent); - struct node *nd = to_node(i); - - mutex_lock(&sp->members_lock); - list_del(&nd->list); - sp->members_count--; - mutex_unlock(&sp->members_lock); - - config_item_put(i); -} - -static void release_node(struct config_item *i) -{ - struct node *nd = to_node(i); - kfree(nd); -} - -static struct clusters clusters_root = { - .subsys = { - .su_group = { - .cg_item = { - .ci_namebuf = "dlm", - .ci_type = &clusters_type, - }, - }, - }, -}; - -int dlm_config_init(void) -{ - config_group_init(&clusters_root.subsys.su_group); - init_MUTEX(&clusters_root.subsys.su_sem); - return configfs_register_subsystem(&clusters_root.subsys); -} - -void dlm_config_exit(void) -{ - configfs_unregister_subsystem(&clusters_root.subsys); -} - -/* - * Functions for user space to read/write attributes - */ - -static ssize_t show_comm(struct config_item *i, struct configfs_attribute *a, - char *buf) -{ - struct comm *cm = to_comm(i); - struct comm_attribute *cma = - container_of(a, struct comm_attribute, attr); - return cma->show ? cma->show(cm, buf) : 0; -} - -static ssize_t store_comm(struct config_item *i, struct configfs_attribute *a, - const char *buf, size_t len) -{ - struct comm *cm = to_comm(i); - struct comm_attribute *cma = - container_of(a, struct comm_attribute, attr); - return cma->store ? cma->store(cm, buf, len) : -EINVAL; -} - -static ssize_t comm_nodeid_read(struct comm *cm, char *buf) -{ - return sprintf(buf, "%d\n", cm->nodeid); -} - -static ssize_t comm_nodeid_write(struct comm *cm, const char *buf, size_t len) -{ - cm->nodeid = simple_strtol(buf, NULL, 0); - return len; -} - -static ssize_t comm_local_read(struct comm *cm, char *buf) -{ - return sprintf(buf, "%d\n", cm->local); -} - -static ssize_t comm_local_write(struct comm *cm, const char *buf, size_t len) -{ - cm->local= simple_strtol(buf, NULL, 0); - if (cm->local && !local_comm) - local_comm = cm; - return len; -} - -static ssize_t comm_addr_write(struct comm *cm, const char *buf, size_t len) -{ - struct sockaddr_storage *addr; - - if (len != sizeof(struct sockaddr_storage)) - return -EINVAL; - - if (cm->addr_count >= DLM_MAX_ADDR_COUNT) - return -ENOSPC; - - addr = kzalloc(sizeof(*addr), GFP_KERNEL); - if (!addr) - return -ENOMEM; - - memcpy(addr, buf, len); - cm->addr[cm->addr_count++] = addr; - return len; -} - -static ssize_t show_node(struct config_item *i, struct configfs_attribute *a, - char *buf) -{ - struct node *nd = to_node(i); - struct node_attribute *nda = - container_of(a, struct node_attribute, attr); - return nda->show ? nda->show(nd, buf) : 0; -} - -static ssize_t store_node(struct config_item *i, struct configfs_attribute *a, - const char *buf, size_t len) -{ - struct node *nd = to_node(i); - struct node_attribute *nda = - container_of(a, struct node_attribute, attr); - return nda->store ? nda->store(nd, buf, len) : -EINVAL; -} - -static ssize_t node_nodeid_read(struct node *nd, char *buf) -{ - return sprintf(buf, "%d\n", nd->nodeid); -} - -static ssize_t node_nodeid_write(struct node *nd, const char *buf, size_t len) -{ - nd->nodeid = simple_strtol(buf, NULL, 0); - return len; -} - -static ssize_t node_weight_read(struct node *nd, char *buf) -{ - return sprintf(buf, "%d\n", nd->weight); -} - -static ssize_t node_weight_write(struct node *nd, const char *buf, size_t len) -{ - nd->weight = simple_strtol(buf, NULL, 0); - return len; -} - -/* - * Functions for the dlm to get the info that's been configured - */ - -static struct space *get_space(char *name) -{ - if (!space_list) - return NULL; - return to_space(config_group_find_obj(space_list, name)); -} - -static void put_space(struct space *sp) -{ - config_item_put(&sp->group.cg_item); -} - -static struct comm *get_comm(int nodeid, struct sockaddr_storage *addr) -{ - struct config_item *i; - struct comm *cm = NULL; - int found = 0; - - if (!comm_list) - return NULL; - - down(&clusters_root.subsys.su_sem); - - list_for_each_entry(i, &comm_list->cg_children, ci_entry) { - cm = to_comm(i); - - if (nodeid) { - if (cm->nodeid != nodeid) - continue; - found = 1; - break; - } else { - if (!cm->addr_count || - memcmp(cm->addr[0], addr, sizeof(*addr))) - continue; - found = 1; - break; - } - } - up(&clusters_root.subsys.su_sem); - - if (found) - config_item_get(i); - else - cm = NULL; - return cm; -} - -static void put_comm(struct comm *cm) -{ - config_item_put(&cm->item); -} - -/* caller must free mem */ -int dlm_nodeid_list(char *lsname, int **ids_out) -{ - struct space *sp; - struct node *nd; - int i = 0, rv = 0; - int *ids; - - sp = get_space(lsname); - if (!sp) - return -EEXIST; - - mutex_lock(&sp->members_lock); - if (!sp->members_count) { - rv = 0; - goto out; - } - - ids = kcalloc(sp->members_count, sizeof(int), GFP_KERNEL); - if (!ids) { - rv = -ENOMEM; - goto out; - } - - rv = sp->members_count; - list_for_each_entry(nd, &sp->members, list) - ids[i++] = nd->nodeid; - - if (rv != i) - printk("bad nodeid count %d %d\n", rv, i); - - *ids_out = ids; - out: - mutex_unlock(&sp->members_lock); - put_space(sp); - return rv; -} - -int dlm_node_weight(char *lsname, int nodeid) -{ - struct space *sp; - struct node *nd; - int w = -EEXIST; - - sp = get_space(lsname); - if (!sp) - goto out; - - mutex_lock(&sp->members_lock); - list_for_each_entry(nd, &sp->members, list) { - if (nd->nodeid != nodeid) - continue; - w = nd->weight; - break; - } - mutex_unlock(&sp->members_lock); - put_space(sp); - out: - return w; -} - -int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr) -{ - struct comm *cm = get_comm(nodeid, NULL); - if (!cm) - return -EEXIST; - if (!cm->addr_count) - return -ENOENT; - memcpy(addr, cm->addr[0], sizeof(*addr)); - put_comm(cm); - return 0; -} - -int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid) -{ - struct comm *cm = get_comm(0, addr); - if (!cm) - return -EEXIST; - *nodeid = cm->nodeid; - put_comm(cm); - return 0; -} - -int dlm_our_nodeid(void) -{ - return local_comm ? local_comm->nodeid : 0; -} - -/* num 0 is first addr, num 1 is second addr */ -int dlm_our_addr(struct sockaddr_storage *addr, int num) -{ - if (!local_comm) - return -1; - if (num + 1 > local_comm->addr_count) - return -1; - memcpy(addr, local_comm->addr[num], sizeof(*addr)); - return 0; -} - -/* Config file defaults */ -#define DEFAULT_TCP_PORT 21064 -#define DEFAULT_BUFFER_SIZE 4096 -#define DEFAULT_RSBTBL_SIZE 256 -#define DEFAULT_LKBTBL_SIZE 1024 -#define DEFAULT_DIRTBL_SIZE 512 -#define DEFAULT_RECOVER_TIMER 5 -#define DEFAULT_TOSS_SECS 10 -#define DEFAULT_SCAN_SECS 5 - -struct dlm_config_info dlm_config = { - .tcp_port = DEFAULT_TCP_PORT, - .buffer_size = DEFAULT_BUFFER_SIZE, - .rsbtbl_size = DEFAULT_RSBTBL_SIZE, - .lkbtbl_size = DEFAULT_LKBTBL_SIZE, - .dirtbl_size = DEFAULT_DIRTBL_SIZE, - .recover_timer = DEFAULT_RECOVER_TIMER, - .toss_secs = DEFAULT_TOSS_SECS, - .scan_secs = DEFAULT_SCAN_SECS -}; - diff --git a/trunk/fs/dlm/config.h b/trunk/fs/dlm/config.h deleted file mode 100644 index 9da7839958a9..000000000000 --- a/trunk/fs/dlm/config.h +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __CONFIG_DOT_H__ -#define __CONFIG_DOT_H__ - -#define DLM_MAX_ADDR_COUNT 3 - -struct dlm_config_info { - int tcp_port; - int buffer_size; - int rsbtbl_size; - int lkbtbl_size; - int dirtbl_size; - int recover_timer; - int toss_secs; - int scan_secs; -}; - -extern struct dlm_config_info dlm_config; - -int dlm_config_init(void); -void dlm_config_exit(void); -int dlm_node_weight(char *lsname, int nodeid); -int dlm_nodeid_list(char *lsname, int **ids_out); -int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr); -int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid); -int dlm_our_nodeid(void); -int dlm_our_addr(struct sockaddr_storage *addr, int num); - -#endif /* __CONFIG_DOT_H__ */ - diff --git a/trunk/fs/dlm/debug_fs.c b/trunk/fs/dlm/debug_fs.c deleted file mode 100644 index ca94a837a5bb..000000000000 --- a/trunk/fs/dlm/debug_fs.c +++ /dev/null @@ -1,387 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include -#include -#include -#include -#include - -#include "dlm_internal.h" - -#define DLM_DEBUG_BUF_LEN 4096 -static char debug_buf[DLM_DEBUG_BUF_LEN]; -static struct mutex debug_buf_lock; - -static struct dentry *dlm_root; - -struct rsb_iter { - int entry; - struct dlm_ls *ls; - struct list_head *next; - struct dlm_rsb *rsb; -}; - -/* - * dump all rsb's in the lockspace hash table - */ - -static char *print_lockmode(int mode) -{ - switch (mode) { - case DLM_LOCK_IV: - return "--"; - case DLM_LOCK_NL: - return "NL"; - case DLM_LOCK_CR: - return "CR"; - case DLM_LOCK_CW: - return "CW"; - case DLM_LOCK_PR: - return "PR"; - case DLM_LOCK_PW: - return "PW"; - case DLM_LOCK_EX: - return "EX"; - default: - return "??"; - } -} - -static void print_lock(struct seq_file *s, struct dlm_lkb *lkb, - struct dlm_rsb *res) -{ - seq_printf(s, "%08x %s", lkb->lkb_id, print_lockmode(lkb->lkb_grmode)); - - if (lkb->lkb_status == DLM_LKSTS_CONVERT - || lkb->lkb_status == DLM_LKSTS_WAITING) - seq_printf(s, " (%s)", print_lockmode(lkb->lkb_rqmode)); - - if (lkb->lkb_nodeid) { - if (lkb->lkb_nodeid != res->res_nodeid) - seq_printf(s, " Remote: %3d %08x", lkb->lkb_nodeid, - lkb->lkb_remid); - else - seq_printf(s, " Master: %08x", lkb->lkb_remid); - } - - if (lkb->lkb_wait_type) - seq_printf(s, " wait_type: %d", lkb->lkb_wait_type); - - seq_printf(s, "\n"); -} - -static int print_resource(struct dlm_rsb *res, struct seq_file *s) -{ - struct dlm_lkb *lkb; - int i, lvblen = res->res_ls->ls_lvblen, recover_list, root_list; - - seq_printf(s, "\nResource %p Name (len=%d) \"", res, res->res_length); - for (i = 0; i < res->res_length; i++) { - if (isprint(res->res_name[i])) - seq_printf(s, "%c", res->res_name[i]); - else - seq_printf(s, "%c", '.'); - } - if (res->res_nodeid > 0) - seq_printf(s, "\" \nLocal Copy, Master is node %d\n", - res->res_nodeid); - else if (res->res_nodeid == 0) - seq_printf(s, "\" \nMaster Copy\n"); - else if (res->res_nodeid == -1) - seq_printf(s, "\" \nLooking up master (lkid %x)\n", - res->res_first_lkid); - else - seq_printf(s, "\" \nInvalid master %d\n", res->res_nodeid); - - /* Print the LVB: */ - if (res->res_lvbptr) { - seq_printf(s, "LVB: "); - for (i = 0; i < lvblen; i++) { - if (i == lvblen / 2) - seq_printf(s, "\n "); - seq_printf(s, "%02x ", - (unsigned char) res->res_lvbptr[i]); - } - if (rsb_flag(res, RSB_VALNOTVALID)) - seq_printf(s, " (INVALID)"); - seq_printf(s, "\n"); - } - - root_list = !list_empty(&res->res_root_list); - recover_list = !list_empty(&res->res_recover_list); - - if (root_list || recover_list) { - seq_printf(s, "Recovery: root %d recover %d flags %lx " - "count %d\n", root_list, recover_list, - res->res_flags, res->res_recover_locks_count); - } - - /* Print the locks attached to this resource */ - seq_printf(s, "Granted Queue\n"); - list_for_each_entry(lkb, &res->res_grantqueue, lkb_statequeue) - print_lock(s, lkb, res); - - seq_printf(s, "Conversion Queue\n"); - list_for_each_entry(lkb, &res->res_convertqueue, lkb_statequeue) - print_lock(s, lkb, res); - - seq_printf(s, "Waiting Queue\n"); - list_for_each_entry(lkb, &res->res_waitqueue, lkb_statequeue) - print_lock(s, lkb, res); - - if (list_empty(&res->res_lookup)) - goto out; - - seq_printf(s, "Lookup Queue\n"); - list_for_each_entry(lkb, &res->res_lookup, lkb_rsb_lookup) { - seq_printf(s, "%08x %s", lkb->lkb_id, - print_lockmode(lkb->lkb_rqmode)); - if (lkb->lkb_wait_type) - seq_printf(s, " wait_type: %d", lkb->lkb_wait_type); - seq_printf(s, "\n"); - } - out: - return 0; -} - -static int rsb_iter_next(struct rsb_iter *ri) -{ - struct dlm_ls *ls = ri->ls; - int i; - - if (!ri->next) { - top: - /* Find the next non-empty hash bucket */ - for (i = ri->entry; i < ls->ls_rsbtbl_size; i++) { - read_lock(&ls->ls_rsbtbl[i].lock); - if (!list_empty(&ls->ls_rsbtbl[i].list)) { - ri->next = ls->ls_rsbtbl[i].list.next; - read_unlock(&ls->ls_rsbtbl[i].lock); - break; - } - read_unlock(&ls->ls_rsbtbl[i].lock); - } - ri->entry = i; - - if (ri->entry >= ls->ls_rsbtbl_size) - return 1; - } else { - i = ri->entry; - read_lock(&ls->ls_rsbtbl[i].lock); - ri->next = ri->next->next; - if (ri->next->next == ls->ls_rsbtbl[i].list.next) { - /* End of list - move to next bucket */ - ri->next = NULL; - ri->entry++; - read_unlock(&ls->ls_rsbtbl[i].lock); - goto top; - } - read_unlock(&ls->ls_rsbtbl[i].lock); - } - ri->rsb = list_entry(ri->next, struct dlm_rsb, res_hashchain); - - return 0; -} - -static void rsb_iter_free(struct rsb_iter *ri) -{ - kfree(ri); -} - -static struct rsb_iter *rsb_iter_init(struct dlm_ls *ls) -{ - struct rsb_iter *ri; - - ri = kmalloc(sizeof *ri, GFP_KERNEL); - if (!ri) - return NULL; - - ri->ls = ls; - ri->entry = 0; - ri->next = NULL; - - if (rsb_iter_next(ri)) { - rsb_iter_free(ri); - return NULL; - } - - return ri; -} - -static void *rsb_seq_start(struct seq_file *file, loff_t *pos) -{ - struct rsb_iter *ri; - loff_t n = *pos; - - ri = rsb_iter_init(file->private); - if (!ri) - return NULL; - - while (n--) { - if (rsb_iter_next(ri)) { - rsb_iter_free(ri); - return NULL; - } - } - - return ri; -} - -static void *rsb_seq_next(struct seq_file *file, void *iter_ptr, loff_t *pos) -{ - struct rsb_iter *ri = iter_ptr; - - (*pos)++; - - if (rsb_iter_next(ri)) { - rsb_iter_free(ri); - return NULL; - } - - return ri; -} - -static void rsb_seq_stop(struct seq_file *file, void *iter_ptr) -{ - /* nothing for now */ -} - -static int rsb_seq_show(struct seq_file *file, void *iter_ptr) -{ - struct rsb_iter *ri = iter_ptr; - - print_resource(ri->rsb, file); - - return 0; -} - -static struct seq_operations rsb_seq_ops = { - .start = rsb_seq_start, - .next = rsb_seq_next, - .stop = rsb_seq_stop, - .show = rsb_seq_show, -}; - -static int rsb_open(struct inode *inode, struct file *file) -{ - struct seq_file *seq; - int ret; - - ret = seq_open(file, &rsb_seq_ops); - if (ret) - return ret; - - seq = file->private_data; - seq->private = inode->i_private; - - return 0; -} - -static struct file_operations rsb_fops = { - .owner = THIS_MODULE, - .open = rsb_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release -}; - -/* - * dump lkb's on the ls_waiters list - */ - -static int waiters_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t waiters_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct dlm_ls *ls = file->private_data; - struct dlm_lkb *lkb; - size_t len = DLM_DEBUG_BUF_LEN, pos = 0, ret, rv; - - mutex_lock(&debug_buf_lock); - mutex_lock(&ls->ls_waiters_mutex); - memset(debug_buf, 0, sizeof(debug_buf)); - - list_for_each_entry(lkb, &ls->ls_waiters, lkb_wait_reply) { - ret = snprintf(debug_buf + pos, len - pos, "%x %d %d %s\n", - lkb->lkb_id, lkb->lkb_wait_type, - lkb->lkb_nodeid, lkb->lkb_resource->res_name); - if (ret >= len - pos) - break; - pos += ret; - } - mutex_unlock(&ls->ls_waiters_mutex); - - rv = simple_read_from_buffer(userbuf, count, ppos, debug_buf, pos); - mutex_unlock(&debug_buf_lock); - return rv; -} - -static struct file_operations waiters_fops = { - .owner = THIS_MODULE, - .open = waiters_open, - .read = waiters_read -}; - -int dlm_create_debug_file(struct dlm_ls *ls) -{ - char name[DLM_LOCKSPACE_LEN+8]; - - ls->ls_debug_rsb_dentry = debugfs_create_file(ls->ls_name, - S_IFREG | S_IRUGO, - dlm_root, - ls, - &rsb_fops); - if (!ls->ls_debug_rsb_dentry) - return -ENOMEM; - - memset(name, 0, sizeof(name)); - snprintf(name, DLM_LOCKSPACE_LEN+8, "%s_waiters", ls->ls_name); - - ls->ls_debug_waiters_dentry = debugfs_create_file(name, - S_IFREG | S_IRUGO, - dlm_root, - ls, - &waiters_fops); - if (!ls->ls_debug_waiters_dentry) { - debugfs_remove(ls->ls_debug_rsb_dentry); - return -ENOMEM; - } - - return 0; -} - -void dlm_delete_debug_file(struct dlm_ls *ls) -{ - if (ls->ls_debug_rsb_dentry) - debugfs_remove(ls->ls_debug_rsb_dentry); - if (ls->ls_debug_waiters_dentry) - debugfs_remove(ls->ls_debug_waiters_dentry); -} - -int dlm_register_debugfs(void) -{ - mutex_init(&debug_buf_lock); - dlm_root = debugfs_create_dir("dlm", NULL); - return dlm_root ? 0 : -ENOMEM; -} - -void dlm_unregister_debugfs(void) -{ - debugfs_remove(dlm_root); -} - diff --git a/trunk/fs/dlm/dir.c b/trunk/fs/dlm/dir.c deleted file mode 100644 index 46754553fdcc..000000000000 --- a/trunk/fs/dlm/dir.c +++ /dev/null @@ -1,423 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "member.h" -#include "lowcomms.h" -#include "rcom.h" -#include "config.h" -#include "memory.h" -#include "recover.h" -#include "util.h" -#include "lock.h" -#include "dir.h" - - -static void put_free_de(struct dlm_ls *ls, struct dlm_direntry *de) -{ - spin_lock(&ls->ls_recover_list_lock); - list_add(&de->list, &ls->ls_recover_list); - spin_unlock(&ls->ls_recover_list_lock); -} - -static struct dlm_direntry *get_free_de(struct dlm_ls *ls, int len) -{ - int found = 0; - struct dlm_direntry *de; - - spin_lock(&ls->ls_recover_list_lock); - list_for_each_entry(de, &ls->ls_recover_list, list) { - if (de->length == len) { - list_del(&de->list); - de->master_nodeid = 0; - memset(de->name, 0, len); - found = 1; - break; - } - } - spin_unlock(&ls->ls_recover_list_lock); - - if (!found) - de = allocate_direntry(ls, len); - return de; -} - -void dlm_clear_free_entries(struct dlm_ls *ls) -{ - struct dlm_direntry *de; - - spin_lock(&ls->ls_recover_list_lock); - while (!list_empty(&ls->ls_recover_list)) { - de = list_entry(ls->ls_recover_list.next, struct dlm_direntry, - list); - list_del(&de->list); - free_direntry(de); - } - spin_unlock(&ls->ls_recover_list_lock); -} - -/* - * We use the upper 16 bits of the hash value to select the directory node. - * Low bits are used for distribution of rsb's among hash buckets on each node. - * - * To give the exact range wanted (0 to num_nodes-1), we apply a modulus of - * num_nodes to the hash value. This value in the desired range is used as an - * offset into the sorted list of nodeid's to give the particular nodeid. - */ - -int dlm_hash2nodeid(struct dlm_ls *ls, uint32_t hash) -{ - struct list_head *tmp; - struct dlm_member *memb = NULL; - uint32_t node, n = 0; - int nodeid; - - if (ls->ls_num_nodes == 1) { - nodeid = dlm_our_nodeid(); - goto out; - } - - if (ls->ls_node_array) { - node = (hash >> 16) % ls->ls_total_weight; - nodeid = ls->ls_node_array[node]; - goto out; - } - - /* make_member_array() failed to kmalloc ls_node_array... */ - - node = (hash >> 16) % ls->ls_num_nodes; - - list_for_each(tmp, &ls->ls_nodes) { - if (n++ != node) - continue; - memb = list_entry(tmp, struct dlm_member, list); - break; - } - - DLM_ASSERT(memb , printk("num_nodes=%u n=%u node=%u\n", - ls->ls_num_nodes, n, node);); - nodeid = memb->nodeid; - out: - return nodeid; -} - -int dlm_dir_nodeid(struct dlm_rsb *r) -{ - return dlm_hash2nodeid(r->res_ls, r->res_hash); -} - -static inline uint32_t dir_hash(struct dlm_ls *ls, char *name, int len) -{ - uint32_t val; - - val = jhash(name, len, 0); - val &= (ls->ls_dirtbl_size - 1); - - return val; -} - -static void add_entry_to_hash(struct dlm_ls *ls, struct dlm_direntry *de) -{ - uint32_t bucket; - - bucket = dir_hash(ls, de->name, de->length); - list_add_tail(&de->list, &ls->ls_dirtbl[bucket].list); -} - -static struct dlm_direntry *search_bucket(struct dlm_ls *ls, char *name, - int namelen, uint32_t bucket) -{ - struct dlm_direntry *de; - - list_for_each_entry(de, &ls->ls_dirtbl[bucket].list, list) { - if (de->length == namelen && !memcmp(name, de->name, namelen)) - goto out; - } - de = NULL; - out: - return de; -} - -void dlm_dir_remove_entry(struct dlm_ls *ls, int nodeid, char *name, int namelen) -{ - struct dlm_direntry *de; - uint32_t bucket; - - bucket = dir_hash(ls, name, namelen); - - write_lock(&ls->ls_dirtbl[bucket].lock); - - de = search_bucket(ls, name, namelen, bucket); - - if (!de) { - log_error(ls, "remove fr %u none", nodeid); - goto out; - } - - if (de->master_nodeid != nodeid) { - log_error(ls, "remove fr %u ID %u", nodeid, de->master_nodeid); - goto out; - } - - list_del(&de->list); - free_direntry(de); - out: - write_unlock(&ls->ls_dirtbl[bucket].lock); -} - -void dlm_dir_clear(struct dlm_ls *ls) -{ - struct list_head *head; - struct dlm_direntry *de; - int i; - - DLM_ASSERT(list_empty(&ls->ls_recover_list), ); - - for (i = 0; i < ls->ls_dirtbl_size; i++) { - write_lock(&ls->ls_dirtbl[i].lock); - head = &ls->ls_dirtbl[i].list; - while (!list_empty(head)) { - de = list_entry(head->next, struct dlm_direntry, list); - list_del(&de->list); - put_free_de(ls, de); - } - write_unlock(&ls->ls_dirtbl[i].lock); - } -} - -int dlm_recover_directory(struct dlm_ls *ls) -{ - struct dlm_member *memb; - struct dlm_direntry *de; - char *b, *last_name = NULL; - int error = -ENOMEM, last_len, count = 0; - uint16_t namelen; - - log_debug(ls, "dlm_recover_directory"); - - if (dlm_no_directory(ls)) - goto out_status; - - dlm_dir_clear(ls); - - last_name = kmalloc(DLM_RESNAME_MAXLEN, GFP_KERNEL); - if (!last_name) - goto out; - - list_for_each_entry(memb, &ls->ls_nodes, list) { - memset(last_name, 0, DLM_RESNAME_MAXLEN); - last_len = 0; - - for (;;) { - error = dlm_recovery_stopped(ls); - if (error) - goto out_free; - - error = dlm_rcom_names(ls, memb->nodeid, - last_name, last_len); - if (error) - goto out_free; - - schedule(); - - /* - * pick namelen/name pairs out of received buffer - */ - - b = ls->ls_recover_buf + sizeof(struct dlm_rcom); - - for (;;) { - memcpy(&namelen, b, sizeof(uint16_t)); - namelen = be16_to_cpu(namelen); - b += sizeof(uint16_t); - - /* namelen of 0xFFFFF marks end of names for - this node; namelen of 0 marks end of the - buffer */ - - if (namelen == 0xFFFF) - goto done; - if (!namelen) - break; - - error = -ENOMEM; - de = get_free_de(ls, namelen); - if (!de) - goto out_free; - - de->master_nodeid = memb->nodeid; - de->length = namelen; - last_len = namelen; - memcpy(de->name, b, namelen); - memcpy(last_name, b, namelen); - b += namelen; - - add_entry_to_hash(ls, de); - count++; - } - } - done: - ; - } - - out_status: - error = 0; - dlm_set_recover_status(ls, DLM_RS_DIR); - log_debug(ls, "dlm_recover_directory %d entries", count); - out_free: - kfree(last_name); - out: - dlm_clear_free_entries(ls); - return error; -} - -static int get_entry(struct dlm_ls *ls, int nodeid, char *name, - int namelen, int *r_nodeid) -{ - struct dlm_direntry *de, *tmp; - uint32_t bucket; - - bucket = dir_hash(ls, name, namelen); - - write_lock(&ls->ls_dirtbl[bucket].lock); - de = search_bucket(ls, name, namelen, bucket); - if (de) { - *r_nodeid = de->master_nodeid; - write_unlock(&ls->ls_dirtbl[bucket].lock); - if (*r_nodeid == nodeid) - return -EEXIST; - return 0; - } - - write_unlock(&ls->ls_dirtbl[bucket].lock); - - de = allocate_direntry(ls, namelen); - if (!de) - return -ENOMEM; - - de->master_nodeid = nodeid; - de->length = namelen; - memcpy(de->name, name, namelen); - - write_lock(&ls->ls_dirtbl[bucket].lock); - tmp = search_bucket(ls, name, namelen, bucket); - if (tmp) { - free_direntry(de); - de = tmp; - } else { - list_add_tail(&de->list, &ls->ls_dirtbl[bucket].list); - } - *r_nodeid = de->master_nodeid; - write_unlock(&ls->ls_dirtbl[bucket].lock); - return 0; -} - -int dlm_dir_lookup(struct dlm_ls *ls, int nodeid, char *name, int namelen, - int *r_nodeid) -{ - return get_entry(ls, nodeid, name, namelen, r_nodeid); -} - -/* Copy the names of master rsb's into the buffer provided. - Only select names whose dir node is the given nodeid. */ - -void dlm_copy_master_names(struct dlm_ls *ls, char *inbuf, int inlen, - char *outbuf, int outlen, int nodeid) -{ - struct list_head *list; - struct dlm_rsb *start_r = NULL, *r = NULL; - int offset = 0, start_namelen, error, dir_nodeid; - char *start_name; - uint16_t be_namelen; - - /* - * Find the rsb where we left off (or start again) - */ - - start_namelen = inlen; - start_name = inbuf; - - if (start_namelen > 1) { - /* - * We could also use a find_rsb_root() function here that - * searched the ls_root_list. - */ - error = dlm_find_rsb(ls, start_name, start_namelen, R_MASTER, - &start_r); - DLM_ASSERT(!error && start_r, - printk("error %d\n", error);); - DLM_ASSERT(!list_empty(&start_r->res_root_list), - dlm_print_rsb(start_r);); - dlm_put_rsb(start_r); - } - - /* - * Send rsb names for rsb's we're master of and whose directory node - * matches the requesting node. - */ - - down_read(&ls->ls_root_sem); - if (start_r) - list = start_r->res_root_list.next; - else - list = ls->ls_root_list.next; - - for (offset = 0; list != &ls->ls_root_list; list = list->next) { - r = list_entry(list, struct dlm_rsb, res_root_list); - if (r->res_nodeid) - continue; - - dir_nodeid = dlm_dir_nodeid(r); - if (dir_nodeid != nodeid) - continue; - - /* - * The block ends when we can't fit the following in the - * remaining buffer space: - * namelen (uint16_t) + - * name (r->res_length) + - * end-of-block record 0x0000 (uint16_t) - */ - - if (offset + sizeof(uint16_t)*2 + r->res_length > outlen) { - /* Write end-of-block record */ - be_namelen = 0; - memcpy(outbuf + offset, &be_namelen, sizeof(uint16_t)); - offset += sizeof(uint16_t); - goto out; - } - - be_namelen = cpu_to_be16(r->res_length); - memcpy(outbuf + offset, &be_namelen, sizeof(uint16_t)); - offset += sizeof(uint16_t); - memcpy(outbuf + offset, r->res_name, r->res_length); - offset += r->res_length; - } - - /* - * If we've reached the end of the list (and there's room) write a - * terminating record. - */ - - if ((list == &ls->ls_root_list) && - (offset + sizeof(uint16_t) <= outlen)) { - be_namelen = 0xFFFF; - memcpy(outbuf + offset, &be_namelen, sizeof(uint16_t)); - offset += sizeof(uint16_t); - } - - out: - up_read(&ls->ls_root_sem); -} - diff --git a/trunk/fs/dlm/dir.h b/trunk/fs/dlm/dir.h deleted file mode 100644 index 0b0eb1267b6e..000000000000 --- a/trunk/fs/dlm/dir.h +++ /dev/null @@ -1,30 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __DIR_DOT_H__ -#define __DIR_DOT_H__ - - -int dlm_dir_nodeid(struct dlm_rsb *rsb); -int dlm_hash2nodeid(struct dlm_ls *ls, uint32_t hash); -void dlm_dir_remove_entry(struct dlm_ls *ls, int nodeid, char *name, int len); -void dlm_dir_clear(struct dlm_ls *ls); -void dlm_clear_free_entries(struct dlm_ls *ls); -int dlm_recover_directory(struct dlm_ls *ls); -int dlm_dir_lookup(struct dlm_ls *ls, int nodeid, char *name, int namelen, - int *r_nodeid); -void dlm_copy_master_names(struct dlm_ls *ls, char *inbuf, int inlen, - char *outbuf, int outlen, int nodeid); - -#endif /* __DIR_DOT_H__ */ - diff --git a/trunk/fs/dlm/dlm_internal.h b/trunk/fs/dlm/dlm_internal.h deleted file mode 100644 index 1e5cd67e1b7a..000000000000 --- a/trunk/fs/dlm/dlm_internal.h +++ /dev/null @@ -1,543 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __DLM_INTERNAL_DOT_H__ -#define __DLM_INTERNAL_DOT_H__ - -/* - * This is the main header file to be included in each DLM source file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DLM_LOCKSPACE_LEN 64 - -/* Size of the temp buffer midcomms allocates on the stack. - We try to make this large enough so most messages fit. - FIXME: should sctp make this unnecessary? */ - -#define DLM_INBUF_LEN 148 - -struct dlm_ls; -struct dlm_lkb; -struct dlm_rsb; -struct dlm_member; -struct dlm_lkbtable; -struct dlm_rsbtable; -struct dlm_dirtable; -struct dlm_direntry; -struct dlm_recover; -struct dlm_header; -struct dlm_message; -struct dlm_rcom; -struct dlm_mhandle; - -#define log_print(fmt, args...) \ - printk(KERN_ERR "dlm: "fmt"\n" , ##args) -#define log_error(ls, fmt, args...) \ - printk(KERN_ERR "dlm: %s: " fmt "\n", (ls)->ls_name , ##args) - -#define DLM_LOG_DEBUG -#ifdef DLM_LOG_DEBUG -#define log_debug(ls, fmt, args...) log_error(ls, fmt, ##args) -#else -#define log_debug(ls, fmt, args...) -#endif - -#define DLM_ASSERT(x, do) \ -{ \ - if (!(x)) \ - { \ - printk(KERN_ERR "\nDLM: Assertion failed on line %d of file %s\n" \ - "DLM: assertion: \"%s\"\n" \ - "DLM: time = %lu\n", \ - __LINE__, __FILE__, #x, jiffies); \ - {do} \ - printk("\n"); \ - BUG(); \ - panic("DLM: Record message above and reboot.\n"); \ - } \ -} - -#define DLM_FAKE_USER_AST ERR_PTR(-EINVAL) - - -struct dlm_direntry { - struct list_head list; - uint32_t master_nodeid; - uint16_t length; - char name[1]; -}; - -struct dlm_dirtable { - struct list_head list; - rwlock_t lock; -}; - -struct dlm_rsbtable { - struct list_head list; - struct list_head toss; - rwlock_t lock; -}; - -struct dlm_lkbtable { - struct list_head list; - rwlock_t lock; - uint16_t counter; -}; - -/* - * Lockspace member (per node in a ls) - */ - -struct dlm_member { - struct list_head list; - int nodeid; - int weight; -}; - -/* - * Save and manage recovery state for a lockspace. - */ - -struct dlm_recover { - struct list_head list; - int *nodeids; - int node_count; - uint64_t seq; -}; - -/* - * Pass input args to second stage locking function. - */ - -struct dlm_args { - uint32_t flags; - void *astaddr; - long astparam; - void *bastaddr; - int mode; - struct dlm_lksb *lksb; -}; - - -/* - * Lock block - * - * A lock can be one of three types: - * - * local copy lock is mastered locally - * (lkb_nodeid is zero and DLM_LKF_MSTCPY is not set) - * process copy lock is mastered on a remote node - * (lkb_nodeid is non-zero and DLM_LKF_MSTCPY is not set) - * master copy master node's copy of a lock owned by remote node - * (lkb_nodeid is non-zero and DLM_LKF_MSTCPY is set) - * - * lkb_exflags: a copy of the most recent flags arg provided to dlm_lock or - * dlm_unlock. The dlm does not modify these or use any private flags in - * this field; it only contains DLM_LKF_ flags from dlm.h. These flags - * are sent as-is to the remote master when the lock is remote. - * - * lkb_flags: internal dlm flags (DLM_IFL_ prefix) from dlm_internal.h. - * Some internal flags are shared between the master and process nodes; - * these shared flags are kept in the lower two bytes. One of these - * flags set on the master copy will be propagated to the process copy - * and v.v. Other internal flags are private to the master or process - * node (e.g. DLM_IFL_MSTCPY). These are kept in the high two bytes. - * - * lkb_sbflags: status block flags. These flags are copied directly into - * the caller's lksb.sb_flags prior to the dlm_lock/dlm_unlock completion - * ast. All defined in dlm.h with DLM_SBF_ prefix. - * - * lkb_status: the lock status indicates which rsb queue the lock is - * on, grant, convert, or wait. DLM_LKSTS_ WAITING/GRANTED/CONVERT - * - * lkb_wait_type: the dlm message type (DLM_MSG_ prefix) for which a - * reply is needed. Only set when the lkb is on the lockspace waiters - * list awaiting a reply from a remote node. - * - * lkb_nodeid: when the lkb is a local copy, nodeid is 0; when the lkb - * is a master copy, nodeid specifies the remote lock holder, when the - * lkb is a process copy, the nodeid specifies the lock master. - */ - -/* lkb_ast_type */ - -#define AST_COMP 1 -#define AST_BAST 2 - -/* lkb_status */ - -#define DLM_LKSTS_WAITING 1 -#define DLM_LKSTS_GRANTED 2 -#define DLM_LKSTS_CONVERT 3 - -/* lkb_flags */ - -#define DLM_IFL_MSTCPY 0x00010000 -#define DLM_IFL_RESEND 0x00020000 -#define DLM_IFL_DEAD 0x00040000 -#define DLM_IFL_USER 0x00000001 -#define DLM_IFL_ORPHAN 0x00000002 - -struct dlm_lkb { - struct dlm_rsb *lkb_resource; /* the rsb */ - struct kref lkb_ref; - int lkb_nodeid; /* copied from rsb */ - int lkb_ownpid; /* pid of lock owner */ - uint32_t lkb_id; /* our lock ID */ - uint32_t lkb_remid; /* lock ID on remote partner */ - uint32_t lkb_exflags; /* external flags from caller */ - uint32_t lkb_sbflags; /* lksb flags */ - uint32_t lkb_flags; /* internal flags */ - uint32_t lkb_lvbseq; /* lvb sequence number */ - - int8_t lkb_status; /* granted, waiting, convert */ - int8_t lkb_rqmode; /* requested lock mode */ - int8_t lkb_grmode; /* granted lock mode */ - int8_t lkb_bastmode; /* requested mode */ - int8_t lkb_highbast; /* highest mode bast sent for */ - - int8_t lkb_wait_type; /* type of reply waiting for */ - int8_t lkb_ast_type; /* type of ast queued for */ - - struct list_head lkb_idtbl_list; /* lockspace lkbtbl */ - struct list_head lkb_statequeue; /* rsb g/c/w list */ - struct list_head lkb_rsb_lookup; /* waiting for rsb lookup */ - struct list_head lkb_wait_reply; /* waiting for remote reply */ - struct list_head lkb_astqueue; /* need ast to be sent */ - struct list_head lkb_ownqueue; /* list of locks for a process */ - - char *lkb_lvbptr; - struct dlm_lksb *lkb_lksb; /* caller's status block */ - void *lkb_astaddr; /* caller's ast function */ - void *lkb_bastaddr; /* caller's bast function */ - long lkb_astparam; /* caller's ast arg */ -}; - - -struct dlm_rsb { - struct dlm_ls *res_ls; /* the lockspace */ - struct kref res_ref; - struct mutex res_mutex; - unsigned long res_flags; - int res_length; /* length of rsb name */ - int res_nodeid; - uint32_t res_lvbseq; - uint32_t res_hash; - uint32_t res_bucket; /* rsbtbl */ - unsigned long res_toss_time; - uint32_t res_first_lkid; - struct list_head res_lookup; /* lkbs waiting on first */ - struct list_head res_hashchain; /* rsbtbl */ - struct list_head res_grantqueue; - struct list_head res_convertqueue; - struct list_head res_waitqueue; - - struct list_head res_root_list; /* used for recovery */ - struct list_head res_recover_list; /* used for recovery */ - int res_recover_locks_count; - - char *res_lvbptr; - char res_name[1]; -}; - -/* find_rsb() flags */ - -#define R_MASTER 1 /* only return rsb if it's a master */ -#define R_CREATE 2 /* create/add rsb if not found */ - -/* rsb_flags */ - -enum rsb_flags { - RSB_MASTER_UNCERTAIN, - RSB_VALNOTVALID, - RSB_VALNOTVALID_PREV, - RSB_NEW_MASTER, - RSB_NEW_MASTER2, - RSB_RECOVER_CONVERT, - RSB_LOCKS_PURGED, -}; - -static inline void rsb_set_flag(struct dlm_rsb *r, enum rsb_flags flag) -{ - __set_bit(flag, &r->res_flags); -} - -static inline void rsb_clear_flag(struct dlm_rsb *r, enum rsb_flags flag) -{ - __clear_bit(flag, &r->res_flags); -} - -static inline int rsb_flag(struct dlm_rsb *r, enum rsb_flags flag) -{ - return test_bit(flag, &r->res_flags); -} - - -/* dlm_header is first element of all structs sent between nodes */ - -#define DLM_HEADER_MAJOR 0x00020000 -#define DLM_HEADER_MINOR 0x00000001 - -#define DLM_MSG 1 -#define DLM_RCOM 2 - -struct dlm_header { - uint32_t h_version; - uint32_t h_lockspace; - uint32_t h_nodeid; /* nodeid of sender */ - uint16_t h_length; - uint8_t h_cmd; /* DLM_MSG, DLM_RCOM */ - uint8_t h_pad; -}; - - -#define DLM_MSG_REQUEST 1 -#define DLM_MSG_CONVERT 2 -#define DLM_MSG_UNLOCK 3 -#define DLM_MSG_CANCEL 4 -#define DLM_MSG_REQUEST_REPLY 5 -#define DLM_MSG_CONVERT_REPLY 6 -#define DLM_MSG_UNLOCK_REPLY 7 -#define DLM_MSG_CANCEL_REPLY 8 -#define DLM_MSG_GRANT 9 -#define DLM_MSG_BAST 10 -#define DLM_MSG_LOOKUP 11 -#define DLM_MSG_REMOVE 12 -#define DLM_MSG_LOOKUP_REPLY 13 - -struct dlm_message { - struct dlm_header m_header; - uint32_t m_type; /* DLM_MSG_ */ - uint32_t m_nodeid; - uint32_t m_pid; - uint32_t m_lkid; /* lkid on sender */ - uint32_t m_remid; /* lkid on receiver */ - uint32_t m_parent_lkid; - uint32_t m_parent_remid; - uint32_t m_exflags; - uint32_t m_sbflags; - uint32_t m_flags; - uint32_t m_lvbseq; - uint32_t m_hash; - int m_status; - int m_grmode; - int m_rqmode; - int m_bastmode; - int m_asts; - int m_result; /* 0 or -EXXX */ - char m_extra[0]; /* name or lvb */ -}; - - -#define DLM_RS_NODES 0x00000001 -#define DLM_RS_NODES_ALL 0x00000002 -#define DLM_RS_DIR 0x00000004 -#define DLM_RS_DIR_ALL 0x00000008 -#define DLM_RS_LOCKS 0x00000010 -#define DLM_RS_LOCKS_ALL 0x00000020 -#define DLM_RS_DONE 0x00000040 -#define DLM_RS_DONE_ALL 0x00000080 - -#define DLM_RCOM_STATUS 1 -#define DLM_RCOM_NAMES 2 -#define DLM_RCOM_LOOKUP 3 -#define DLM_RCOM_LOCK 4 -#define DLM_RCOM_STATUS_REPLY 5 -#define DLM_RCOM_NAMES_REPLY 6 -#define DLM_RCOM_LOOKUP_REPLY 7 -#define DLM_RCOM_LOCK_REPLY 8 - -struct dlm_rcom { - struct dlm_header rc_header; - uint32_t rc_type; /* DLM_RCOM_ */ - int rc_result; /* multi-purpose */ - uint64_t rc_id; /* match reply with request */ - char rc_buf[0]; -}; - -struct rcom_config { - uint32_t rf_lvblen; - uint32_t rf_lsflags; - uint64_t rf_unused; -}; - -struct rcom_lock { - uint32_t rl_ownpid; - uint32_t rl_lkid; - uint32_t rl_remid; - uint32_t rl_parent_lkid; - uint32_t rl_parent_remid; - uint32_t rl_exflags; - uint32_t rl_flags; - uint32_t rl_lvbseq; - int rl_result; - int8_t rl_rqmode; - int8_t rl_grmode; - int8_t rl_status; - int8_t rl_asts; - uint16_t rl_wait_type; - uint16_t rl_namelen; - char rl_name[DLM_RESNAME_MAXLEN]; - char rl_lvb[0]; -}; - -struct dlm_ls { - struct list_head ls_list; /* list of lockspaces */ - dlm_lockspace_t *ls_local_handle; - uint32_t ls_global_id; /* global unique lockspace ID */ - uint32_t ls_exflags; - int ls_lvblen; - int ls_count; /* reference count */ - unsigned long ls_flags; /* LSFL_ */ - struct kobject ls_kobj; - - struct dlm_rsbtable *ls_rsbtbl; - uint32_t ls_rsbtbl_size; - - struct dlm_lkbtable *ls_lkbtbl; - uint32_t ls_lkbtbl_size; - - struct dlm_dirtable *ls_dirtbl; - uint32_t ls_dirtbl_size; - - struct mutex ls_waiters_mutex; - struct list_head ls_waiters; /* lkbs needing a reply */ - - struct list_head ls_nodes; /* current nodes in ls */ - struct list_head ls_nodes_gone; /* dead node list, recovery */ - int ls_num_nodes; /* number of nodes in ls */ - int ls_low_nodeid; - int ls_total_weight; - int *ls_node_array; - - struct dlm_rsb ls_stub_rsb; /* for returning errors */ - struct dlm_lkb ls_stub_lkb; /* for returning errors */ - struct dlm_message ls_stub_ms; /* for faking a reply */ - - struct dentry *ls_debug_rsb_dentry; /* debugfs */ - struct dentry *ls_debug_waiters_dentry; /* debugfs */ - - wait_queue_head_t ls_uevent_wait; /* user part of join/leave */ - int ls_uevent_result; - - struct miscdevice ls_device; - - /* recovery related */ - - struct timer_list ls_timer; - struct task_struct *ls_recoverd_task; - struct mutex ls_recoverd_active; - spinlock_t ls_recover_lock; - uint32_t ls_recover_status; /* DLM_RS_ */ - uint64_t ls_recover_seq; - struct dlm_recover *ls_recover_args; - struct rw_semaphore ls_in_recovery; /* block local requests */ - struct list_head ls_requestqueue;/* queue remote requests */ - struct mutex ls_requestqueue_mutex; - char *ls_recover_buf; - int ls_recover_nodeid; /* for debugging */ - uint64_t ls_rcom_seq; - struct list_head ls_recover_list; - spinlock_t ls_recover_list_lock; - int ls_recover_list_count; - wait_queue_head_t ls_wait_general; - struct mutex ls_clear_proc_locks; - - struct list_head ls_root_list; /* root resources */ - struct rw_semaphore ls_root_sem; /* protect root_list */ - - int ls_namelen; - char ls_name[1]; -}; - -#define LSFL_WORK 0 -#define LSFL_RUNNING 1 -#define LSFL_RECOVERY_STOP 2 -#define LSFL_RCOM_READY 3 -#define LSFL_UEVENT_WAIT 4 - -/* much of this is just saving user space pointers associated with the - lock that we pass back to the user lib with an ast */ - -struct dlm_user_args { - struct dlm_user_proc *proc; /* each process that opens the lockspace - device has private data - (dlm_user_proc) on the struct file, - the process's locks point back to it*/ - struct dlm_lksb lksb; - int old_mode; - int update_user_lvb; - struct dlm_lksb __user *user_lksb; - void __user *castparam; - void __user *castaddr; - void __user *bastparam; - void __user *bastaddr; -}; - -#define DLM_PROC_FLAGS_CLOSING 1 -#define DLM_PROC_FLAGS_COMPAT 2 - -/* locks list is kept so we can remove all a process's locks when it - exits (or orphan those that are persistent) */ - -struct dlm_user_proc { - dlm_lockspace_t *lockspace; - unsigned long flags; /* DLM_PROC_FLAGS */ - struct list_head asts; - spinlock_t asts_spin; - struct list_head locks; - spinlock_t locks_spin; - wait_queue_head_t wait; -}; - -static inline int dlm_locking_stopped(struct dlm_ls *ls) -{ - return !test_bit(LSFL_RUNNING, &ls->ls_flags); -} - -static inline int dlm_recovery_stopped(struct dlm_ls *ls) -{ - return test_bit(LSFL_RECOVERY_STOP, &ls->ls_flags); -} - -static inline int dlm_no_directory(struct dlm_ls *ls) -{ - return (ls->ls_exflags & DLM_LSFL_NODIR) ? 1 : 0; -} - -#endif /* __DLM_INTERNAL_DOT_H__ */ - diff --git a/trunk/fs/dlm/lock.c b/trunk/fs/dlm/lock.c deleted file mode 100644 index 3f2befa4797b..000000000000 --- a/trunk/fs/dlm/lock.c +++ /dev/null @@ -1,3871 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -/* Central locking logic has four stages: - - dlm_lock() - dlm_unlock() - - request_lock(ls, lkb) - convert_lock(ls, lkb) - unlock_lock(ls, lkb) - cancel_lock(ls, lkb) - - _request_lock(r, lkb) - _convert_lock(r, lkb) - _unlock_lock(r, lkb) - _cancel_lock(r, lkb) - - do_request(r, lkb) - do_convert(r, lkb) - do_unlock(r, lkb) - do_cancel(r, lkb) - - Stage 1 (lock, unlock) is mainly about checking input args and - splitting into one of the four main operations: - - dlm_lock = request_lock - dlm_lock+CONVERT = convert_lock - dlm_unlock = unlock_lock - dlm_unlock+CANCEL = cancel_lock - - Stage 2, xxxx_lock(), just finds and locks the relevant rsb which is - provided to the next stage. - - Stage 3, _xxxx_lock(), determines if the operation is local or remote. - When remote, it calls send_xxxx(), when local it calls do_xxxx(). - - Stage 4, do_xxxx(), is the guts of the operation. It manipulates the - given rsb and lkb and queues callbacks. - - For remote operations, send_xxxx() results in the corresponding do_xxxx() - function being executed on the remote node. The connecting send/receive - calls on local (L) and remote (R) nodes: - - L: send_xxxx() -> R: receive_xxxx() - R: do_xxxx() - L: receive_xxxx_reply() <- R: send_xxxx_reply() -*/ -#include -#include "dlm_internal.h" -#include -#include "memory.h" -#include "lowcomms.h" -#include "requestqueue.h" -#include "util.h" -#include "dir.h" -#include "member.h" -#include "lockspace.h" -#include "ast.h" -#include "lock.h" -#include "rcom.h" -#include "recover.h" -#include "lvb_table.h" -#include "user.h" -#include "config.h" - -static int send_request(struct dlm_rsb *r, struct dlm_lkb *lkb); -static int send_convert(struct dlm_rsb *r, struct dlm_lkb *lkb); -static int send_unlock(struct dlm_rsb *r, struct dlm_lkb *lkb); -static int send_cancel(struct dlm_rsb *r, struct dlm_lkb *lkb); -static int send_grant(struct dlm_rsb *r, struct dlm_lkb *lkb); -static int send_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int mode); -static int send_lookup(struct dlm_rsb *r, struct dlm_lkb *lkb); -static int send_remove(struct dlm_rsb *r); -static int _request_lock(struct dlm_rsb *r, struct dlm_lkb *lkb); -static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, - struct dlm_message *ms); -static int receive_extralen(struct dlm_message *ms); - -/* - * Lock compatibilty matrix - thanks Steve - * UN = Unlocked state. Not really a state, used as a flag - * PD = Padding. Used to make the matrix a nice power of two in size - * Other states are the same as the VMS DLM. - * Usage: matrix[grmode+1][rqmode+1] (although m[rq+1][gr+1] is the same) - */ - -static const int __dlm_compat_matrix[8][8] = { - /* UN NL CR CW PR PW EX PD */ - {1, 1, 1, 1, 1, 1, 1, 0}, /* UN */ - {1, 1, 1, 1, 1, 1, 1, 0}, /* NL */ - {1, 1, 1, 1, 1, 1, 0, 0}, /* CR */ - {1, 1, 1, 1, 0, 0, 0, 0}, /* CW */ - {1, 1, 1, 0, 1, 0, 0, 0}, /* PR */ - {1, 1, 1, 0, 0, 0, 0, 0}, /* PW */ - {1, 1, 0, 0, 0, 0, 0, 0}, /* EX */ - {0, 0, 0, 0, 0, 0, 0, 0} /* PD */ -}; - -/* - * This defines the direction of transfer of LVB data. - * Granted mode is the row; requested mode is the column. - * Usage: matrix[grmode+1][rqmode+1] - * 1 = LVB is returned to the caller - * 0 = LVB is written to the resource - * -1 = nothing happens to the LVB - */ - -const int dlm_lvb_operations[8][8] = { - /* UN NL CR CW PR PW EX PD*/ - { -1, 1, 1, 1, 1, 1, 1, -1 }, /* UN */ - { -1, 1, 1, 1, 1, 1, 1, 0 }, /* NL */ - { -1, -1, 1, 1, 1, 1, 1, 0 }, /* CR */ - { -1, -1, -1, 1, 1, 1, 1, 0 }, /* CW */ - { -1, -1, -1, -1, 1, 1, 1, 0 }, /* PR */ - { -1, 0, 0, 0, 0, 0, 1, 0 }, /* PW */ - { -1, 0, 0, 0, 0, 0, 0, 0 }, /* EX */ - { -1, 0, 0, 0, 0, 0, 0, 0 } /* PD */ -}; - -#define modes_compat(gr, rq) \ - __dlm_compat_matrix[(gr)->lkb_grmode + 1][(rq)->lkb_rqmode + 1] - -int dlm_modes_compat(int mode1, int mode2) -{ - return __dlm_compat_matrix[mode1 + 1][mode2 + 1]; -} - -/* - * Compatibility matrix for conversions with QUECVT set. - * Granted mode is the row; requested mode is the column. - * Usage: matrix[grmode+1][rqmode+1] - */ - -static const int __quecvt_compat_matrix[8][8] = { - /* UN NL CR CW PR PW EX PD */ - {0, 0, 0, 0, 0, 0, 0, 0}, /* UN */ - {0, 0, 1, 1, 1, 1, 1, 0}, /* NL */ - {0, 0, 0, 1, 1, 1, 1, 0}, /* CR */ - {0, 0, 0, 0, 1, 1, 1, 0}, /* CW */ - {0, 0, 0, 1, 0, 1, 1, 0}, /* PR */ - {0, 0, 0, 0, 0, 0, 1, 0}, /* PW */ - {0, 0, 0, 0, 0, 0, 0, 0}, /* EX */ - {0, 0, 0, 0, 0, 0, 0, 0} /* PD */ -}; - -void dlm_print_lkb(struct dlm_lkb *lkb) -{ - printk(KERN_ERR "lkb: nodeid %d id %x remid %x exflags %x flags %x\n" - " status %d rqmode %d grmode %d wait_type %d ast_type %d\n", - lkb->lkb_nodeid, lkb->lkb_id, lkb->lkb_remid, lkb->lkb_exflags, - lkb->lkb_flags, lkb->lkb_status, lkb->lkb_rqmode, - lkb->lkb_grmode, lkb->lkb_wait_type, lkb->lkb_ast_type); -} - -void dlm_print_rsb(struct dlm_rsb *r) -{ - printk(KERN_ERR "rsb: nodeid %d flags %lx first %x rlc %d name %s\n", - r->res_nodeid, r->res_flags, r->res_first_lkid, - r->res_recover_locks_count, r->res_name); -} - -void dlm_dump_rsb(struct dlm_rsb *r) -{ - struct dlm_lkb *lkb; - - dlm_print_rsb(r); - - printk(KERN_ERR "rsb: root_list empty %d recover_list empty %d\n", - list_empty(&r->res_root_list), list_empty(&r->res_recover_list)); - printk(KERN_ERR "rsb lookup list\n"); - list_for_each_entry(lkb, &r->res_lookup, lkb_rsb_lookup) - dlm_print_lkb(lkb); - printk(KERN_ERR "rsb grant queue:\n"); - list_for_each_entry(lkb, &r->res_grantqueue, lkb_statequeue) - dlm_print_lkb(lkb); - printk(KERN_ERR "rsb convert queue:\n"); - list_for_each_entry(lkb, &r->res_convertqueue, lkb_statequeue) - dlm_print_lkb(lkb); - printk(KERN_ERR "rsb wait queue:\n"); - list_for_each_entry(lkb, &r->res_waitqueue, lkb_statequeue) - dlm_print_lkb(lkb); -} - -/* Threads cannot use the lockspace while it's being recovered */ - -static inline void lock_recovery(struct dlm_ls *ls) -{ - down_read(&ls->ls_in_recovery); -} - -static inline void unlock_recovery(struct dlm_ls *ls) -{ - up_read(&ls->ls_in_recovery); -} - -static inline int lock_recovery_try(struct dlm_ls *ls) -{ - return down_read_trylock(&ls->ls_in_recovery); -} - -static inline int can_be_queued(struct dlm_lkb *lkb) -{ - return !(lkb->lkb_exflags & DLM_LKF_NOQUEUE); -} - -static inline int force_blocking_asts(struct dlm_lkb *lkb) -{ - return (lkb->lkb_exflags & DLM_LKF_NOQUEUEBAST); -} - -static inline int is_demoted(struct dlm_lkb *lkb) -{ - return (lkb->lkb_sbflags & DLM_SBF_DEMOTED); -} - -static inline int is_remote(struct dlm_rsb *r) -{ - DLM_ASSERT(r->res_nodeid >= 0, dlm_print_rsb(r);); - return !!r->res_nodeid; -} - -static inline int is_process_copy(struct dlm_lkb *lkb) -{ - return (lkb->lkb_nodeid && !(lkb->lkb_flags & DLM_IFL_MSTCPY)); -} - -static inline int is_master_copy(struct dlm_lkb *lkb) -{ - if (lkb->lkb_flags & DLM_IFL_MSTCPY) - DLM_ASSERT(lkb->lkb_nodeid, dlm_print_lkb(lkb);); - return (lkb->lkb_flags & DLM_IFL_MSTCPY) ? 1 : 0; -} - -static inline int middle_conversion(struct dlm_lkb *lkb) -{ - if ((lkb->lkb_grmode==DLM_LOCK_PR && lkb->lkb_rqmode==DLM_LOCK_CW) || - (lkb->lkb_rqmode==DLM_LOCK_PR && lkb->lkb_grmode==DLM_LOCK_CW)) - return 1; - return 0; -} - -static inline int down_conversion(struct dlm_lkb *lkb) -{ - return (!middle_conversion(lkb) && lkb->lkb_rqmode < lkb->lkb_grmode); -} - -static void queue_cast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv) -{ - if (is_master_copy(lkb)) - return; - - DLM_ASSERT(lkb->lkb_lksb, dlm_print_lkb(lkb);); - - lkb->lkb_lksb->sb_status = rv; - lkb->lkb_lksb->sb_flags = lkb->lkb_sbflags; - - dlm_add_ast(lkb, AST_COMP); -} - -static void queue_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rqmode) -{ - if (is_master_copy(lkb)) - send_bast(r, lkb, rqmode); - else { - lkb->lkb_bastmode = rqmode; - dlm_add_ast(lkb, AST_BAST); - } -} - -/* - * Basic operations on rsb's and lkb's - */ - -static struct dlm_rsb *create_rsb(struct dlm_ls *ls, char *name, int len) -{ - struct dlm_rsb *r; - - r = allocate_rsb(ls, len); - if (!r) - return NULL; - - r->res_ls = ls; - r->res_length = len; - memcpy(r->res_name, name, len); - mutex_init(&r->res_mutex); - - INIT_LIST_HEAD(&r->res_lookup); - INIT_LIST_HEAD(&r->res_grantqueue); - INIT_LIST_HEAD(&r->res_convertqueue); - INIT_LIST_HEAD(&r->res_waitqueue); - INIT_LIST_HEAD(&r->res_root_list); - INIT_LIST_HEAD(&r->res_recover_list); - - return r; -} - -static int search_rsb_list(struct list_head *head, char *name, int len, - unsigned int flags, struct dlm_rsb **r_ret) -{ - struct dlm_rsb *r; - int error = 0; - - list_for_each_entry(r, head, res_hashchain) { - if (len == r->res_length && !memcmp(name, r->res_name, len)) - goto found; - } - return -EBADR; - - found: - if (r->res_nodeid && (flags & R_MASTER)) - error = -ENOTBLK; - *r_ret = r; - return error; -} - -static int _search_rsb(struct dlm_ls *ls, char *name, int len, int b, - unsigned int flags, struct dlm_rsb **r_ret) -{ - struct dlm_rsb *r; - int error; - - error = search_rsb_list(&ls->ls_rsbtbl[b].list, name, len, flags, &r); - if (!error) { - kref_get(&r->res_ref); - goto out; - } - error = search_rsb_list(&ls->ls_rsbtbl[b].toss, name, len, flags, &r); - if (error) - goto out; - - list_move(&r->res_hashchain, &ls->ls_rsbtbl[b].list); - - if (dlm_no_directory(ls)) - goto out; - - if (r->res_nodeid == -1) { - rsb_clear_flag(r, RSB_MASTER_UNCERTAIN); - r->res_first_lkid = 0; - } else if (r->res_nodeid > 0) { - rsb_set_flag(r, RSB_MASTER_UNCERTAIN); - r->res_first_lkid = 0; - } else { - DLM_ASSERT(r->res_nodeid == 0, dlm_print_rsb(r);); - DLM_ASSERT(!rsb_flag(r, RSB_MASTER_UNCERTAIN),); - } - out: - *r_ret = r; - return error; -} - -static int search_rsb(struct dlm_ls *ls, char *name, int len, int b, - unsigned int flags, struct dlm_rsb **r_ret) -{ - int error; - write_lock(&ls->ls_rsbtbl[b].lock); - error = _search_rsb(ls, name, len, b, flags, r_ret); - write_unlock(&ls->ls_rsbtbl[b].lock); - return error; -} - -/* - * Find rsb in rsbtbl and potentially create/add one - * - * Delaying the release of rsb's has a similar benefit to applications keeping - * NL locks on an rsb, but without the guarantee that the cached master value - * will still be valid when the rsb is reused. Apps aren't always smart enough - * to keep NL locks on an rsb that they may lock again shortly; this can lead - * to excessive master lookups and removals if we don't delay the release. - * - * Searching for an rsb means looking through both the normal list and toss - * list. When found on the toss list the rsb is moved to the normal list with - * ref count of 1; when found on normal list the ref count is incremented. - */ - -static int find_rsb(struct dlm_ls *ls, char *name, int namelen, - unsigned int flags, struct dlm_rsb **r_ret) -{ - struct dlm_rsb *r, *tmp; - uint32_t hash, bucket; - int error = 0; - - if (dlm_no_directory(ls)) - flags |= R_CREATE; - - hash = jhash(name, namelen, 0); - bucket = hash & (ls->ls_rsbtbl_size - 1); - - error = search_rsb(ls, name, namelen, bucket, flags, &r); - if (!error) - goto out; - - if (error == -EBADR && !(flags & R_CREATE)) - goto out; - - /* the rsb was found but wasn't a master copy */ - if (error == -ENOTBLK) - goto out; - - error = -ENOMEM; - r = create_rsb(ls, name, namelen); - if (!r) - goto out; - - r->res_hash = hash; - r->res_bucket = bucket; - r->res_nodeid = -1; - kref_init(&r->res_ref); - - /* With no directory, the master can be set immediately */ - if (dlm_no_directory(ls)) { - int nodeid = dlm_dir_nodeid(r); - if (nodeid == dlm_our_nodeid()) - nodeid = 0; - r->res_nodeid = nodeid; - } - - write_lock(&ls->ls_rsbtbl[bucket].lock); - error = _search_rsb(ls, name, namelen, bucket, 0, &tmp); - if (!error) { - write_unlock(&ls->ls_rsbtbl[bucket].lock); - free_rsb(r); - r = tmp; - goto out; - } - list_add(&r->res_hashchain, &ls->ls_rsbtbl[bucket].list); - write_unlock(&ls->ls_rsbtbl[bucket].lock); - error = 0; - out: - *r_ret = r; - return error; -} - -int dlm_find_rsb(struct dlm_ls *ls, char *name, int namelen, - unsigned int flags, struct dlm_rsb **r_ret) -{ - return find_rsb(ls, name, namelen, flags, r_ret); -} - -/* This is only called to add a reference when the code already holds - a valid reference to the rsb, so there's no need for locking. */ - -static inline void hold_rsb(struct dlm_rsb *r) -{ - kref_get(&r->res_ref); -} - -void dlm_hold_rsb(struct dlm_rsb *r) -{ - hold_rsb(r); -} - -static void toss_rsb(struct kref *kref) -{ - struct dlm_rsb *r = container_of(kref, struct dlm_rsb, res_ref); - struct dlm_ls *ls = r->res_ls; - - DLM_ASSERT(list_empty(&r->res_root_list), dlm_print_rsb(r);); - kref_init(&r->res_ref); - list_move(&r->res_hashchain, &ls->ls_rsbtbl[r->res_bucket].toss); - r->res_toss_time = jiffies; - if (r->res_lvbptr) { - free_lvb(r->res_lvbptr); - r->res_lvbptr = NULL; - } -} - -/* When all references to the rsb are gone it's transfered to - the tossed list for later disposal. */ - -static void put_rsb(struct dlm_rsb *r) -{ - struct dlm_ls *ls = r->res_ls; - uint32_t bucket = r->res_bucket; - - write_lock(&ls->ls_rsbtbl[bucket].lock); - kref_put(&r->res_ref, toss_rsb); - write_unlock(&ls->ls_rsbtbl[bucket].lock); -} - -void dlm_put_rsb(struct dlm_rsb *r) -{ - put_rsb(r); -} - -/* See comment for unhold_lkb */ - -static void unhold_rsb(struct dlm_rsb *r) -{ - int rv; - rv = kref_put(&r->res_ref, toss_rsb); - DLM_ASSERT(!rv, dlm_dump_rsb(r);); -} - -static void kill_rsb(struct kref *kref) -{ - struct dlm_rsb *r = container_of(kref, struct dlm_rsb, res_ref); - - /* All work is done after the return from kref_put() so we - can release the write_lock before the remove and free. */ - - DLM_ASSERT(list_empty(&r->res_lookup), dlm_dump_rsb(r);); - DLM_ASSERT(list_empty(&r->res_grantqueue), dlm_dump_rsb(r);); - DLM_ASSERT(list_empty(&r->res_convertqueue), dlm_dump_rsb(r);); - DLM_ASSERT(list_empty(&r->res_waitqueue), dlm_dump_rsb(r);); - DLM_ASSERT(list_empty(&r->res_root_list), dlm_dump_rsb(r);); - DLM_ASSERT(list_empty(&r->res_recover_list), dlm_dump_rsb(r);); -} - -/* Attaching/detaching lkb's from rsb's is for rsb reference counting. - The rsb must exist as long as any lkb's for it do. */ - -static void attach_lkb(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - hold_rsb(r); - lkb->lkb_resource = r; -} - -static void detach_lkb(struct dlm_lkb *lkb) -{ - if (lkb->lkb_resource) { - put_rsb(lkb->lkb_resource); - lkb->lkb_resource = NULL; - } -} - -static int create_lkb(struct dlm_ls *ls, struct dlm_lkb **lkb_ret) -{ - struct dlm_lkb *lkb, *tmp; - uint32_t lkid = 0; - uint16_t bucket; - - lkb = allocate_lkb(ls); - if (!lkb) - return -ENOMEM; - - lkb->lkb_nodeid = -1; - lkb->lkb_grmode = DLM_LOCK_IV; - kref_init(&lkb->lkb_ref); - INIT_LIST_HEAD(&lkb->lkb_ownqueue); - - get_random_bytes(&bucket, sizeof(bucket)); - bucket &= (ls->ls_lkbtbl_size - 1); - - write_lock(&ls->ls_lkbtbl[bucket].lock); - - /* counter can roll over so we must verify lkid is not in use */ - - while (lkid == 0) { - lkid = bucket | (ls->ls_lkbtbl[bucket].counter++ << 16); - - list_for_each_entry(tmp, &ls->ls_lkbtbl[bucket].list, - lkb_idtbl_list) { - if (tmp->lkb_id != lkid) - continue; - lkid = 0; - break; - } - } - - lkb->lkb_id = lkid; - list_add(&lkb->lkb_idtbl_list, &ls->ls_lkbtbl[bucket].list); - write_unlock(&ls->ls_lkbtbl[bucket].lock); - - *lkb_ret = lkb; - return 0; -} - -static struct dlm_lkb *__find_lkb(struct dlm_ls *ls, uint32_t lkid) -{ - uint16_t bucket = lkid & 0xFFFF; - struct dlm_lkb *lkb; - - list_for_each_entry(lkb, &ls->ls_lkbtbl[bucket].list, lkb_idtbl_list) { - if (lkb->lkb_id == lkid) - return lkb; - } - return NULL; -} - -static int find_lkb(struct dlm_ls *ls, uint32_t lkid, struct dlm_lkb **lkb_ret) -{ - struct dlm_lkb *lkb; - uint16_t bucket = lkid & 0xFFFF; - - if (bucket >= ls->ls_lkbtbl_size) - return -EBADSLT; - - read_lock(&ls->ls_lkbtbl[bucket].lock); - lkb = __find_lkb(ls, lkid); - if (lkb) - kref_get(&lkb->lkb_ref); - read_unlock(&ls->ls_lkbtbl[bucket].lock); - - *lkb_ret = lkb; - return lkb ? 0 : -ENOENT; -} - -static void kill_lkb(struct kref *kref) -{ - struct dlm_lkb *lkb = container_of(kref, struct dlm_lkb, lkb_ref); - - /* All work is done after the return from kref_put() so we - can release the write_lock before the detach_lkb */ - - DLM_ASSERT(!lkb->lkb_status, dlm_print_lkb(lkb);); -} - -/* __put_lkb() is used when an lkb may not have an rsb attached to - it so we need to provide the lockspace explicitly */ - -static int __put_lkb(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - uint16_t bucket = lkb->lkb_id & 0xFFFF; - - write_lock(&ls->ls_lkbtbl[bucket].lock); - if (kref_put(&lkb->lkb_ref, kill_lkb)) { - list_del(&lkb->lkb_idtbl_list); - write_unlock(&ls->ls_lkbtbl[bucket].lock); - - detach_lkb(lkb); - - /* for local/process lkbs, lvbptr points to caller's lksb */ - if (lkb->lkb_lvbptr && is_master_copy(lkb)) - free_lvb(lkb->lkb_lvbptr); - free_lkb(lkb); - return 1; - } else { - write_unlock(&ls->ls_lkbtbl[bucket].lock); - return 0; - } -} - -int dlm_put_lkb(struct dlm_lkb *lkb) -{ - struct dlm_ls *ls; - - DLM_ASSERT(lkb->lkb_resource, dlm_print_lkb(lkb);); - DLM_ASSERT(lkb->lkb_resource->res_ls, dlm_print_lkb(lkb);); - - ls = lkb->lkb_resource->res_ls; - return __put_lkb(ls, lkb); -} - -/* This is only called to add a reference when the code already holds - a valid reference to the lkb, so there's no need for locking. */ - -static inline void hold_lkb(struct dlm_lkb *lkb) -{ - kref_get(&lkb->lkb_ref); -} - -/* This is called when we need to remove a reference and are certain - it's not the last ref. e.g. del_lkb is always called between a - find_lkb/put_lkb and is always the inverse of a previous add_lkb. - put_lkb would work fine, but would involve unnecessary locking */ - -static inline void unhold_lkb(struct dlm_lkb *lkb) -{ - int rv; - rv = kref_put(&lkb->lkb_ref, kill_lkb); - DLM_ASSERT(!rv, dlm_print_lkb(lkb);); -} - -static void lkb_add_ordered(struct list_head *new, struct list_head *head, - int mode) -{ - struct dlm_lkb *lkb = NULL; - - list_for_each_entry(lkb, head, lkb_statequeue) - if (lkb->lkb_rqmode < mode) - break; - - if (!lkb) - list_add_tail(new, head); - else - __list_add(new, lkb->lkb_statequeue.prev, &lkb->lkb_statequeue); -} - -/* add/remove lkb to rsb's grant/convert/wait queue */ - -static void add_lkb(struct dlm_rsb *r, struct dlm_lkb *lkb, int status) -{ - kref_get(&lkb->lkb_ref); - - DLM_ASSERT(!lkb->lkb_status, dlm_print_lkb(lkb);); - - lkb->lkb_status = status; - - switch (status) { - case DLM_LKSTS_WAITING: - if (lkb->lkb_exflags & DLM_LKF_HEADQUE) - list_add(&lkb->lkb_statequeue, &r->res_waitqueue); - else - list_add_tail(&lkb->lkb_statequeue, &r->res_waitqueue); - break; - case DLM_LKSTS_GRANTED: - /* convention says granted locks kept in order of grmode */ - lkb_add_ordered(&lkb->lkb_statequeue, &r->res_grantqueue, - lkb->lkb_grmode); - break; - case DLM_LKSTS_CONVERT: - if (lkb->lkb_exflags & DLM_LKF_HEADQUE) - list_add(&lkb->lkb_statequeue, &r->res_convertqueue); - else - list_add_tail(&lkb->lkb_statequeue, - &r->res_convertqueue); - break; - default: - DLM_ASSERT(0, dlm_print_lkb(lkb); printk("sts=%d\n", status);); - } -} - -static void del_lkb(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - lkb->lkb_status = 0; - list_del(&lkb->lkb_statequeue); - unhold_lkb(lkb); -} - -static void move_lkb(struct dlm_rsb *r, struct dlm_lkb *lkb, int sts) -{ - hold_lkb(lkb); - del_lkb(r, lkb); - add_lkb(r, lkb, sts); - unhold_lkb(lkb); -} - -/* add/remove lkb from global waiters list of lkb's waiting for - a reply from a remote node */ - -static void add_to_waiters(struct dlm_lkb *lkb, int mstype) -{ - struct dlm_ls *ls = lkb->lkb_resource->res_ls; - - mutex_lock(&ls->ls_waiters_mutex); - if (lkb->lkb_wait_type) { - log_print("add_to_waiters error %d", lkb->lkb_wait_type); - goto out; - } - lkb->lkb_wait_type = mstype; - kref_get(&lkb->lkb_ref); - list_add(&lkb->lkb_wait_reply, &ls->ls_waiters); - out: - mutex_unlock(&ls->ls_waiters_mutex); -} - -static int _remove_from_waiters(struct dlm_lkb *lkb) -{ - int error = 0; - - if (!lkb->lkb_wait_type) { - log_print("remove_from_waiters error"); - error = -EINVAL; - goto out; - } - lkb->lkb_wait_type = 0; - list_del(&lkb->lkb_wait_reply); - unhold_lkb(lkb); - out: - return error; -} - -static int remove_from_waiters(struct dlm_lkb *lkb) -{ - struct dlm_ls *ls = lkb->lkb_resource->res_ls; - int error; - - mutex_lock(&ls->ls_waiters_mutex); - error = _remove_from_waiters(lkb); - mutex_unlock(&ls->ls_waiters_mutex); - return error; -} - -static void dir_remove(struct dlm_rsb *r) -{ - int to_nodeid; - - if (dlm_no_directory(r->res_ls)) - return; - - to_nodeid = dlm_dir_nodeid(r); - if (to_nodeid != dlm_our_nodeid()) - send_remove(r); - else - dlm_dir_remove_entry(r->res_ls, to_nodeid, - r->res_name, r->res_length); -} - -/* FIXME: shouldn't this be able to exit as soon as one non-due rsb is - found since they are in order of newest to oldest? */ - -static int shrink_bucket(struct dlm_ls *ls, int b) -{ - struct dlm_rsb *r; - int count = 0, found; - - for (;;) { - found = 0; - write_lock(&ls->ls_rsbtbl[b].lock); - list_for_each_entry_reverse(r, &ls->ls_rsbtbl[b].toss, - res_hashchain) { - if (!time_after_eq(jiffies, r->res_toss_time + - dlm_config.toss_secs * HZ)) - continue; - found = 1; - break; - } - - if (!found) { - write_unlock(&ls->ls_rsbtbl[b].lock); - break; - } - - if (kref_put(&r->res_ref, kill_rsb)) { - list_del(&r->res_hashchain); - write_unlock(&ls->ls_rsbtbl[b].lock); - - if (is_master(r)) - dir_remove(r); - free_rsb(r); - count++; - } else { - write_unlock(&ls->ls_rsbtbl[b].lock); - log_error(ls, "tossed rsb in use %s", r->res_name); - } - } - - return count; -} - -void dlm_scan_rsbs(struct dlm_ls *ls) -{ - int i; - - if (dlm_locking_stopped(ls)) - return; - - for (i = 0; i < ls->ls_rsbtbl_size; i++) { - shrink_bucket(ls, i); - cond_resched(); - } -} - -/* lkb is master or local copy */ - -static void set_lvb_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int b, len = r->res_ls->ls_lvblen; - - /* b=1 lvb returned to caller - b=0 lvb written to rsb or invalidated - b=-1 do nothing */ - - b = dlm_lvb_operations[lkb->lkb_grmode + 1][lkb->lkb_rqmode + 1]; - - if (b == 1) { - if (!lkb->lkb_lvbptr) - return; - - if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) - return; - - if (!r->res_lvbptr) - return; - - memcpy(lkb->lkb_lvbptr, r->res_lvbptr, len); - lkb->lkb_lvbseq = r->res_lvbseq; - - } else if (b == 0) { - if (lkb->lkb_exflags & DLM_LKF_IVVALBLK) { - rsb_set_flag(r, RSB_VALNOTVALID); - return; - } - - if (!lkb->lkb_lvbptr) - return; - - if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) - return; - - if (!r->res_lvbptr) - r->res_lvbptr = allocate_lvb(r->res_ls); - - if (!r->res_lvbptr) - return; - - memcpy(r->res_lvbptr, lkb->lkb_lvbptr, len); - r->res_lvbseq++; - lkb->lkb_lvbseq = r->res_lvbseq; - rsb_clear_flag(r, RSB_VALNOTVALID); - } - - if (rsb_flag(r, RSB_VALNOTVALID)) - lkb->lkb_sbflags |= DLM_SBF_VALNOTVALID; -} - -static void set_lvb_unlock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - if (lkb->lkb_grmode < DLM_LOCK_PW) - return; - - if (lkb->lkb_exflags & DLM_LKF_IVVALBLK) { - rsb_set_flag(r, RSB_VALNOTVALID); - return; - } - - if (!lkb->lkb_lvbptr) - return; - - if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) - return; - - if (!r->res_lvbptr) - r->res_lvbptr = allocate_lvb(r->res_ls); - - if (!r->res_lvbptr) - return; - - memcpy(r->res_lvbptr, lkb->lkb_lvbptr, r->res_ls->ls_lvblen); - r->res_lvbseq++; - rsb_clear_flag(r, RSB_VALNOTVALID); -} - -/* lkb is process copy (pc) */ - -static void set_lvb_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - int b; - - if (!lkb->lkb_lvbptr) - return; - - if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) - return; - - b = dlm_lvb_operations[lkb->lkb_grmode + 1][lkb->lkb_rqmode + 1]; - if (b == 1) { - int len = receive_extralen(ms); - memcpy(lkb->lkb_lvbptr, ms->m_extra, len); - lkb->lkb_lvbseq = ms->m_lvbseq; - } -} - -/* Manipulate lkb's on rsb's convert/granted/waiting queues - remove_lock -- used for unlock, removes lkb from granted - revert_lock -- used for cancel, moves lkb from convert to granted - grant_lock -- used for request and convert, adds lkb to granted or - moves lkb from convert or waiting to granted - - Each of these is used for master or local copy lkb's. There is - also a _pc() variation used to make the corresponding change on - a process copy (pc) lkb. */ - -static void _remove_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - del_lkb(r, lkb); - lkb->lkb_grmode = DLM_LOCK_IV; - /* this unhold undoes the original ref from create_lkb() - so this leads to the lkb being freed */ - unhold_lkb(lkb); -} - -static void remove_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - set_lvb_unlock(r, lkb); - _remove_lock(r, lkb); -} - -static void remove_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - _remove_lock(r, lkb); -} - -static void revert_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - lkb->lkb_rqmode = DLM_LOCK_IV; - - switch (lkb->lkb_status) { - case DLM_LKSTS_GRANTED: - break; - case DLM_LKSTS_CONVERT: - move_lkb(r, lkb, DLM_LKSTS_GRANTED); - break; - case DLM_LKSTS_WAITING: - del_lkb(r, lkb); - lkb->lkb_grmode = DLM_LOCK_IV; - /* this unhold undoes the original ref from create_lkb() - so this leads to the lkb being freed */ - unhold_lkb(lkb); - break; - default: - log_print("invalid status for revert %d", lkb->lkb_status); - } -} - -static void revert_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - revert_lock(r, lkb); -} - -static void _grant_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - if (lkb->lkb_grmode != lkb->lkb_rqmode) { - lkb->lkb_grmode = lkb->lkb_rqmode; - if (lkb->lkb_status) - move_lkb(r, lkb, DLM_LKSTS_GRANTED); - else - add_lkb(r, lkb, DLM_LKSTS_GRANTED); - } - - lkb->lkb_rqmode = DLM_LOCK_IV; -} - -static void grant_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - set_lvb_lock(r, lkb); - _grant_lock(r, lkb); - lkb->lkb_highbast = 0; -} - -static void grant_lock_pc(struct dlm_rsb *r, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - set_lvb_lock_pc(r, lkb, ms); - _grant_lock(r, lkb); -} - -/* called by grant_pending_locks() which means an async grant message must - be sent to the requesting node in addition to granting the lock if the - lkb belongs to a remote node. */ - -static void grant_lock_pending(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - grant_lock(r, lkb); - if (is_master_copy(lkb)) - send_grant(r, lkb); - else - queue_cast(r, lkb, 0); -} - -static inline int first_in_list(struct dlm_lkb *lkb, struct list_head *head) -{ - struct dlm_lkb *first = list_entry(head->next, struct dlm_lkb, - lkb_statequeue); - if (lkb->lkb_id == first->lkb_id) - return 1; - - return 0; -} - -/* Check if the given lkb conflicts with another lkb on the queue. */ - -static int queue_conflict(struct list_head *head, struct dlm_lkb *lkb) -{ - struct dlm_lkb *this; - - list_for_each_entry(this, head, lkb_statequeue) { - if (this == lkb) - continue; - if (!modes_compat(this, lkb)) - return 1; - } - return 0; -} - -/* - * "A conversion deadlock arises with a pair of lock requests in the converting - * queue for one resource. The granted mode of each lock blocks the requested - * mode of the other lock." - * - * Part 2: if the granted mode of lkb is preventing the first lkb in the - * convert queue from being granted, then demote lkb (set grmode to NL). - * This second form requires that we check for conv-deadlk even when - * now == 0 in _can_be_granted(). - * - * Example: - * Granted Queue: empty - * Convert Queue: NL->EX (first lock) - * PR->EX (second lock) - * - * The first lock can't be granted because of the granted mode of the second - * lock and the second lock can't be granted because it's not first in the - * list. We demote the granted mode of the second lock (the lkb passed to this - * function). - * - * After the resolution, the "grant pending" function needs to go back and try - * to grant locks on the convert queue again since the first lock can now be - * granted. - */ - -static int conversion_deadlock_detect(struct dlm_rsb *rsb, struct dlm_lkb *lkb) -{ - struct dlm_lkb *this, *first = NULL, *self = NULL; - - list_for_each_entry(this, &rsb->res_convertqueue, lkb_statequeue) { - if (!first) - first = this; - if (this == lkb) { - self = lkb; - continue; - } - - if (!modes_compat(this, lkb) && !modes_compat(lkb, this)) - return 1; - } - - /* if lkb is on the convert queue and is preventing the first - from being granted, then there's deadlock and we demote lkb. - multiple converting locks may need to do this before the first - converting lock can be granted. */ - - if (self && self != first) { - if (!modes_compat(lkb, first) && - !queue_conflict(&rsb->res_grantqueue, first)) - return 1; - } - - return 0; -} - -/* - * Return 1 if the lock can be granted, 0 otherwise. - * Also detect and resolve conversion deadlocks. - * - * lkb is the lock to be granted - * - * now is 1 if the function is being called in the context of the - * immediate request, it is 0 if called later, after the lock has been - * queued. - * - * References are from chapter 6 of "VAXcluster Principles" by Roy Davis - */ - -static int _can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) -{ - int8_t conv = (lkb->lkb_grmode != DLM_LOCK_IV); - - /* - * 6-10: Version 5.4 introduced an option to address the phenomenon of - * a new request for a NL mode lock being blocked. - * - * 6-11: If the optional EXPEDITE flag is used with the new NL mode - * request, then it would be granted. In essence, the use of this flag - * tells the Lock Manager to expedite theis request by not considering - * what may be in the CONVERTING or WAITING queues... As of this - * writing, the EXPEDITE flag can be used only with new requests for NL - * mode locks. This flag is not valid for conversion requests. - * - * A shortcut. Earlier checks return an error if EXPEDITE is used in a - * conversion or used with a non-NL requested mode. We also know an - * EXPEDITE request is always granted immediately, so now must always - * be 1. The full condition to grant an expedite request: (now && - * !conv && lkb->rqmode == DLM_LOCK_NL && (flags & EXPEDITE)) can - * therefore be shortened to just checking the flag. - */ - - if (lkb->lkb_exflags & DLM_LKF_EXPEDITE) - return 1; - - /* - * A shortcut. Without this, !queue_conflict(grantqueue, lkb) would be - * added to the remaining conditions. - */ - - if (queue_conflict(&r->res_grantqueue, lkb)) - goto out; - - /* - * 6-3: By default, a conversion request is immediately granted if the - * requested mode is compatible with the modes of all other granted - * locks - */ - - if (queue_conflict(&r->res_convertqueue, lkb)) - goto out; - - /* - * 6-5: But the default algorithm for deciding whether to grant or - * queue conversion requests does not by itself guarantee that such - * requests are serviced on a "first come first serve" basis. This, in - * turn, can lead to a phenomenon known as "indefinate postponement". - * - * 6-7: This issue is dealt with by using the optional QUECVT flag with - * the system service employed to request a lock conversion. This flag - * forces certain conversion requests to be queued, even if they are - * compatible with the granted modes of other locks on the same - * resource. Thus, the use of this flag results in conversion requests - * being ordered on a "first come first servce" basis. - * - * DCT: This condition is all about new conversions being able to occur - * "in place" while the lock remains on the granted queue (assuming - * nothing else conflicts.) IOW if QUECVT isn't set, a conversion - * doesn't _have_ to go onto the convert queue where it's processed in - * order. The "now" variable is necessary to distinguish converts - * being received and processed for the first time now, because once a - * convert is moved to the conversion queue the condition below applies - * requiring fifo granting. - */ - - if (now && conv && !(lkb->lkb_exflags & DLM_LKF_QUECVT)) - return 1; - - /* - * The NOORDER flag is set to avoid the standard vms rules on grant - * order. - */ - - if (lkb->lkb_exflags & DLM_LKF_NOORDER) - return 1; - - /* - * 6-3: Once in that queue [CONVERTING], a conversion request cannot be - * granted until all other conversion requests ahead of it are granted - * and/or canceled. - */ - - if (!now && conv && first_in_list(lkb, &r->res_convertqueue)) - return 1; - - /* - * 6-4: By default, a new request is immediately granted only if all - * three of the following conditions are satisfied when the request is - * issued: - * - The queue of ungranted conversion requests for the resource is - * empty. - * - The queue of ungranted new requests for the resource is empty. - * - The mode of the new request is compatible with the most - * restrictive mode of all granted locks on the resource. - */ - - if (now && !conv && list_empty(&r->res_convertqueue) && - list_empty(&r->res_waitqueue)) - return 1; - - /* - * 6-4: Once a lock request is in the queue of ungranted new requests, - * it cannot be granted until the queue of ungranted conversion - * requests is empty, all ungranted new requests ahead of it are - * granted and/or canceled, and it is compatible with the granted mode - * of the most restrictive lock granted on the resource. - */ - - if (!now && !conv && list_empty(&r->res_convertqueue) && - first_in_list(lkb, &r->res_waitqueue)) - return 1; - - out: - /* - * The following, enabled by CONVDEADLK, departs from VMS. - */ - - if (conv && (lkb->lkb_exflags & DLM_LKF_CONVDEADLK) && - conversion_deadlock_detect(r, lkb)) { - lkb->lkb_grmode = DLM_LOCK_NL; - lkb->lkb_sbflags |= DLM_SBF_DEMOTED; - } - - return 0; -} - -/* - * The ALTPR and ALTCW flags aren't traditional lock manager flags, but are a - * simple way to provide a big optimization to applications that can use them. - */ - -static int can_be_granted(struct dlm_rsb *r, struct dlm_lkb *lkb, int now) -{ - uint32_t flags = lkb->lkb_exflags; - int rv; - int8_t alt = 0, rqmode = lkb->lkb_rqmode; - - rv = _can_be_granted(r, lkb, now); - if (rv) - goto out; - - if (lkb->lkb_sbflags & DLM_SBF_DEMOTED) - goto out; - - if (rqmode != DLM_LOCK_PR && flags & DLM_LKF_ALTPR) - alt = DLM_LOCK_PR; - else if (rqmode != DLM_LOCK_CW && flags & DLM_LKF_ALTCW) - alt = DLM_LOCK_CW; - - if (alt) { - lkb->lkb_rqmode = alt; - rv = _can_be_granted(r, lkb, now); - if (rv) - lkb->lkb_sbflags |= DLM_SBF_ALTMODE; - else - lkb->lkb_rqmode = rqmode; - } - out: - return rv; -} - -static int grant_pending_convert(struct dlm_rsb *r, int high) -{ - struct dlm_lkb *lkb, *s; - int hi, demoted, quit, grant_restart, demote_restart; - - quit = 0; - restart: - grant_restart = 0; - demote_restart = 0; - hi = DLM_LOCK_IV; - - list_for_each_entry_safe(lkb, s, &r->res_convertqueue, lkb_statequeue) { - demoted = is_demoted(lkb); - if (can_be_granted(r, lkb, 0)) { - grant_lock_pending(r, lkb); - grant_restart = 1; - } else { - hi = max_t(int, lkb->lkb_rqmode, hi); - if (!demoted && is_demoted(lkb)) - demote_restart = 1; - } - } - - if (grant_restart) - goto restart; - if (demote_restart && !quit) { - quit = 1; - goto restart; - } - - return max_t(int, high, hi); -} - -static int grant_pending_wait(struct dlm_rsb *r, int high) -{ - struct dlm_lkb *lkb, *s; - - list_for_each_entry_safe(lkb, s, &r->res_waitqueue, lkb_statequeue) { - if (can_be_granted(r, lkb, 0)) - grant_lock_pending(r, lkb); - else - high = max_t(int, lkb->lkb_rqmode, high); - } - - return high; -} - -static void grant_pending_locks(struct dlm_rsb *r) -{ - struct dlm_lkb *lkb, *s; - int high = DLM_LOCK_IV; - - DLM_ASSERT(is_master(r), dlm_dump_rsb(r);); - - high = grant_pending_convert(r, high); - high = grant_pending_wait(r, high); - - if (high == DLM_LOCK_IV) - return; - - /* - * If there are locks left on the wait/convert queue then send blocking - * ASTs to granted locks based on the largest requested mode (high) - * found above. FIXME: highbast < high comparison not valid for PR/CW. - */ - - list_for_each_entry_safe(lkb, s, &r->res_grantqueue, lkb_statequeue) { - if (lkb->lkb_bastaddr && (lkb->lkb_highbast < high) && - !__dlm_compat_matrix[lkb->lkb_grmode+1][high+1]) { - queue_bast(r, lkb, high); - lkb->lkb_highbast = high; - } - } -} - -static void send_bast_queue(struct dlm_rsb *r, struct list_head *head, - struct dlm_lkb *lkb) -{ - struct dlm_lkb *gr; - - list_for_each_entry(gr, head, lkb_statequeue) { - if (gr->lkb_bastaddr && - gr->lkb_highbast < lkb->lkb_rqmode && - !modes_compat(gr, lkb)) { - queue_bast(r, gr, lkb->lkb_rqmode); - gr->lkb_highbast = lkb->lkb_rqmode; - } - } -} - -static void send_blocking_asts(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - send_bast_queue(r, &r->res_grantqueue, lkb); -} - -static void send_blocking_asts_all(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - send_bast_queue(r, &r->res_grantqueue, lkb); - send_bast_queue(r, &r->res_convertqueue, lkb); -} - -/* set_master(r, lkb) -- set the master nodeid of a resource - - The purpose of this function is to set the nodeid field in the given - lkb using the nodeid field in the given rsb. If the rsb's nodeid is - known, it can just be copied to the lkb and the function will return - 0. If the rsb's nodeid is _not_ known, it needs to be looked up - before it can be copied to the lkb. - - When the rsb nodeid is being looked up remotely, the initial lkb - causing the lookup is kept on the ls_waiters list waiting for the - lookup reply. Other lkb's waiting for the same rsb lookup are kept - on the rsb's res_lookup list until the master is verified. - - Return values: - 0: nodeid is set in rsb/lkb and the caller should go ahead and use it - 1: the rsb master is not available and the lkb has been placed on - a wait queue -*/ - -static int set_master(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - struct dlm_ls *ls = r->res_ls; - int error, dir_nodeid, ret_nodeid, our_nodeid = dlm_our_nodeid(); - - if (rsb_flag(r, RSB_MASTER_UNCERTAIN)) { - rsb_clear_flag(r, RSB_MASTER_UNCERTAIN); - r->res_first_lkid = lkb->lkb_id; - lkb->lkb_nodeid = r->res_nodeid; - return 0; - } - - if (r->res_first_lkid && r->res_first_lkid != lkb->lkb_id) { - list_add_tail(&lkb->lkb_rsb_lookup, &r->res_lookup); - return 1; - } - - if (r->res_nodeid == 0) { - lkb->lkb_nodeid = 0; - return 0; - } - - if (r->res_nodeid > 0) { - lkb->lkb_nodeid = r->res_nodeid; - return 0; - } - - DLM_ASSERT(r->res_nodeid == -1, dlm_dump_rsb(r);); - - dir_nodeid = dlm_dir_nodeid(r); - - if (dir_nodeid != our_nodeid) { - r->res_first_lkid = lkb->lkb_id; - send_lookup(r, lkb); - return 1; - } - - for (;;) { - /* It's possible for dlm_scand to remove an old rsb for - this same resource from the toss list, us to create - a new one, look up the master locally, and find it - already exists just before dlm_scand does the - dir_remove() on the previous rsb. */ - - error = dlm_dir_lookup(ls, our_nodeid, r->res_name, - r->res_length, &ret_nodeid); - if (!error) - break; - log_debug(ls, "dir_lookup error %d %s", error, r->res_name); - schedule(); - } - - if (ret_nodeid == our_nodeid) { - r->res_first_lkid = 0; - r->res_nodeid = 0; - lkb->lkb_nodeid = 0; - } else { - r->res_first_lkid = lkb->lkb_id; - r->res_nodeid = ret_nodeid; - lkb->lkb_nodeid = ret_nodeid; - } - return 0; -} - -static void process_lookup_list(struct dlm_rsb *r) -{ - struct dlm_lkb *lkb, *safe; - - list_for_each_entry_safe(lkb, safe, &r->res_lookup, lkb_rsb_lookup) { - list_del(&lkb->lkb_rsb_lookup); - _request_lock(r, lkb); - schedule(); - } -} - -/* confirm_master -- confirm (or deny) an rsb's master nodeid */ - -static void confirm_master(struct dlm_rsb *r, int error) -{ - struct dlm_lkb *lkb; - - if (!r->res_first_lkid) - return; - - switch (error) { - case 0: - case -EINPROGRESS: - r->res_first_lkid = 0; - process_lookup_list(r); - break; - - case -EAGAIN: - /* the remote master didn't queue our NOQUEUE request; - make a waiting lkb the first_lkid */ - - r->res_first_lkid = 0; - - if (!list_empty(&r->res_lookup)) { - lkb = list_entry(r->res_lookup.next, struct dlm_lkb, - lkb_rsb_lookup); - list_del(&lkb->lkb_rsb_lookup); - r->res_first_lkid = lkb->lkb_id; - _request_lock(r, lkb); - } else - r->res_nodeid = -1; - break; - - default: - log_error(r->res_ls, "confirm_master unknown error %d", error); - } -} - -static int set_lock_args(int mode, struct dlm_lksb *lksb, uint32_t flags, - int namelen, uint32_t parent_lkid, void *ast, - void *astarg, void *bast, struct dlm_args *args) -{ - int rv = -EINVAL; - - /* check for invalid arg usage */ - - if (mode < 0 || mode > DLM_LOCK_EX) - goto out; - - if (!(flags & DLM_LKF_CONVERT) && (namelen > DLM_RESNAME_MAXLEN)) - goto out; - - if (flags & DLM_LKF_CANCEL) - goto out; - - if (flags & DLM_LKF_QUECVT && !(flags & DLM_LKF_CONVERT)) - goto out; - - if (flags & DLM_LKF_CONVDEADLK && !(flags & DLM_LKF_CONVERT)) - goto out; - - if (flags & DLM_LKF_CONVDEADLK && flags & DLM_LKF_NOQUEUE) - goto out; - - if (flags & DLM_LKF_EXPEDITE && flags & DLM_LKF_CONVERT) - goto out; - - if (flags & DLM_LKF_EXPEDITE && flags & DLM_LKF_QUECVT) - goto out; - - if (flags & DLM_LKF_EXPEDITE && flags & DLM_LKF_NOQUEUE) - goto out; - - if (flags & DLM_LKF_EXPEDITE && mode != DLM_LOCK_NL) - goto out; - - if (!ast || !lksb) - goto out; - - if (flags & DLM_LKF_VALBLK && !lksb->sb_lvbptr) - goto out; - - /* parent/child locks not yet supported */ - if (parent_lkid) - goto out; - - if (flags & DLM_LKF_CONVERT && !lksb->sb_lkid) - goto out; - - /* these args will be copied to the lkb in validate_lock_args, - it cannot be done now because when converting locks, fields in - an active lkb cannot be modified before locking the rsb */ - - args->flags = flags; - args->astaddr = ast; - args->astparam = (long) astarg; - args->bastaddr = bast; - args->mode = mode; - args->lksb = lksb; - rv = 0; - out: - return rv; -} - -static int set_unlock_args(uint32_t flags, void *astarg, struct dlm_args *args) -{ - if (flags & ~(DLM_LKF_CANCEL | DLM_LKF_VALBLK | DLM_LKF_IVVALBLK | - DLM_LKF_FORCEUNLOCK)) - return -EINVAL; - - args->flags = flags; - args->astparam = (long) astarg; - return 0; -} - -static int validate_lock_args(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_args *args) -{ - int rv = -EINVAL; - - if (args->flags & DLM_LKF_CONVERT) { - if (lkb->lkb_flags & DLM_IFL_MSTCPY) - goto out; - - if (args->flags & DLM_LKF_QUECVT && - !__quecvt_compat_matrix[lkb->lkb_grmode+1][args->mode+1]) - goto out; - - rv = -EBUSY; - if (lkb->lkb_status != DLM_LKSTS_GRANTED) - goto out; - - if (lkb->lkb_wait_type) - goto out; - } - - lkb->lkb_exflags = args->flags; - lkb->lkb_sbflags = 0; - lkb->lkb_astaddr = args->astaddr; - lkb->lkb_astparam = args->astparam; - lkb->lkb_bastaddr = args->bastaddr; - lkb->lkb_rqmode = args->mode; - lkb->lkb_lksb = args->lksb; - lkb->lkb_lvbptr = args->lksb->sb_lvbptr; - lkb->lkb_ownpid = (int) current->pid; - rv = 0; - out: - return rv; -} - -static int validate_unlock_args(struct dlm_lkb *lkb, struct dlm_args *args) -{ - int rv = -EINVAL; - - if (lkb->lkb_flags & DLM_IFL_MSTCPY) - goto out; - - if (args->flags & DLM_LKF_FORCEUNLOCK) - goto out_ok; - - if (args->flags & DLM_LKF_CANCEL && - lkb->lkb_status == DLM_LKSTS_GRANTED) - goto out; - - if (!(args->flags & DLM_LKF_CANCEL) && - lkb->lkb_status != DLM_LKSTS_GRANTED) - goto out; - - rv = -EBUSY; - if (lkb->lkb_wait_type) - goto out; - - out_ok: - lkb->lkb_exflags = args->flags; - lkb->lkb_sbflags = 0; - lkb->lkb_astparam = args->astparam; - - rv = 0; - out: - return rv; -} - -/* - * Four stage 4 varieties: - * do_request(), do_convert(), do_unlock(), do_cancel() - * These are called on the master node for the given lock and - * from the central locking logic. - */ - -static int do_request(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error = 0; - - if (can_be_granted(r, lkb, 1)) { - grant_lock(r, lkb); - queue_cast(r, lkb, 0); - goto out; - } - - if (can_be_queued(lkb)) { - error = -EINPROGRESS; - add_lkb(r, lkb, DLM_LKSTS_WAITING); - send_blocking_asts(r, lkb); - goto out; - } - - error = -EAGAIN; - if (force_blocking_asts(lkb)) - send_blocking_asts_all(r, lkb); - queue_cast(r, lkb, -EAGAIN); - - out: - return error; -} - -static int do_convert(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error = 0; - - /* changing an existing lock may allow others to be granted */ - - if (can_be_granted(r, lkb, 1)) { - grant_lock(r, lkb); - queue_cast(r, lkb, 0); - grant_pending_locks(r); - goto out; - } - - if (can_be_queued(lkb)) { - if (is_demoted(lkb)) - grant_pending_locks(r); - error = -EINPROGRESS; - del_lkb(r, lkb); - add_lkb(r, lkb, DLM_LKSTS_CONVERT); - send_blocking_asts(r, lkb); - goto out; - } - - error = -EAGAIN; - if (force_blocking_asts(lkb)) - send_blocking_asts_all(r, lkb); - queue_cast(r, lkb, -EAGAIN); - - out: - return error; -} - -static int do_unlock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - remove_lock(r, lkb); - queue_cast(r, lkb, -DLM_EUNLOCK); - grant_pending_locks(r); - return -DLM_EUNLOCK; -} - -/* FIXME: if revert_lock() finds that the lkb is granted, we should - skip the queue_cast(ECANCEL). It indicates that the request/convert - completed (and queued a normal ast) just before the cancel; we don't - want to clobber the sb_result for the normal ast with ECANCEL. */ - -static int do_cancel(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - revert_lock(r, lkb); - queue_cast(r, lkb, -DLM_ECANCEL); - grant_pending_locks(r); - return -DLM_ECANCEL; -} - -/* - * Four stage 3 varieties: - * _request_lock(), _convert_lock(), _unlock_lock(), _cancel_lock() - */ - -/* add a new lkb to a possibly new rsb, called by requesting process */ - -static int _request_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error; - - /* set_master: sets lkb nodeid from r */ - - error = set_master(r, lkb); - if (error < 0) - goto out; - if (error) { - error = 0; - goto out; - } - - if (is_remote(r)) - /* receive_request() calls do_request() on remote node */ - error = send_request(r, lkb); - else - error = do_request(r, lkb); - out: - return error; -} - -/* change some property of an existing lkb, e.g. mode */ - -static int _convert_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error; - - if (is_remote(r)) - /* receive_convert() calls do_convert() on remote node */ - error = send_convert(r, lkb); - else - error = do_convert(r, lkb); - - return error; -} - -/* remove an existing lkb from the granted queue */ - -static int _unlock_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error; - - if (is_remote(r)) - /* receive_unlock() calls do_unlock() on remote node */ - error = send_unlock(r, lkb); - else - error = do_unlock(r, lkb); - - return error; -} - -/* remove an existing lkb from the convert or wait queue */ - -static int _cancel_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error; - - if (is_remote(r)) - /* receive_cancel() calls do_cancel() on remote node */ - error = send_cancel(r, lkb); - else - error = do_cancel(r, lkb); - - return error; -} - -/* - * Four stage 2 varieties: - * request_lock(), convert_lock(), unlock_lock(), cancel_lock() - */ - -static int request_lock(struct dlm_ls *ls, struct dlm_lkb *lkb, char *name, - int len, struct dlm_args *args) -{ - struct dlm_rsb *r; - int error; - - error = validate_lock_args(ls, lkb, args); - if (error) - goto out; - - error = find_rsb(ls, name, len, R_CREATE, &r); - if (error) - goto out; - - lock_rsb(r); - - attach_lkb(r, lkb); - lkb->lkb_lksb->sb_lkid = lkb->lkb_id; - - error = _request_lock(r, lkb); - - unlock_rsb(r); - put_rsb(r); - - out: - return error; -} - -static int convert_lock(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_args *args) -{ - struct dlm_rsb *r; - int error; - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - error = validate_lock_args(ls, lkb, args); - if (error) - goto out; - - error = _convert_lock(r, lkb); - out: - unlock_rsb(r); - put_rsb(r); - return error; -} - -static int unlock_lock(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_args *args) -{ - struct dlm_rsb *r; - int error; - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - error = validate_unlock_args(lkb, args); - if (error) - goto out; - - error = _unlock_lock(r, lkb); - out: - unlock_rsb(r); - put_rsb(r); - return error; -} - -static int cancel_lock(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_args *args) -{ - struct dlm_rsb *r; - int error; - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - error = validate_unlock_args(lkb, args); - if (error) - goto out; - - error = _cancel_lock(r, lkb); - out: - unlock_rsb(r); - put_rsb(r); - return error; -} - -/* - * Two stage 1 varieties: dlm_lock() and dlm_unlock() - */ - -int dlm_lock(dlm_lockspace_t *lockspace, - int mode, - struct dlm_lksb *lksb, - uint32_t flags, - void *name, - unsigned int namelen, - uint32_t parent_lkid, - void (*ast) (void *astarg), - void *astarg, - void (*bast) (void *astarg, int mode)) -{ - struct dlm_ls *ls; - struct dlm_lkb *lkb; - struct dlm_args args; - int error, convert = flags & DLM_LKF_CONVERT; - - ls = dlm_find_lockspace_local(lockspace); - if (!ls) - return -EINVAL; - - lock_recovery(ls); - - if (convert) - error = find_lkb(ls, lksb->sb_lkid, &lkb); - else - error = create_lkb(ls, &lkb); - - if (error) - goto out; - - error = set_lock_args(mode, lksb, flags, namelen, parent_lkid, ast, - astarg, bast, &args); - if (error) - goto out_put; - - if (convert) - error = convert_lock(ls, lkb, &args); - else - error = request_lock(ls, lkb, name, namelen, &args); - - if (error == -EINPROGRESS) - error = 0; - out_put: - if (convert || error) - __put_lkb(ls, lkb); - if (error == -EAGAIN) - error = 0; - out: - unlock_recovery(ls); - dlm_put_lockspace(ls); - return error; -} - -int dlm_unlock(dlm_lockspace_t *lockspace, - uint32_t lkid, - uint32_t flags, - struct dlm_lksb *lksb, - void *astarg) -{ - struct dlm_ls *ls; - struct dlm_lkb *lkb; - struct dlm_args args; - int error; - - ls = dlm_find_lockspace_local(lockspace); - if (!ls) - return -EINVAL; - - lock_recovery(ls); - - error = find_lkb(ls, lkid, &lkb); - if (error) - goto out; - - error = set_unlock_args(flags, astarg, &args); - if (error) - goto out_put; - - if (flags & DLM_LKF_CANCEL) - error = cancel_lock(ls, lkb, &args); - else - error = unlock_lock(ls, lkb, &args); - - if (error == -DLM_EUNLOCK || error == -DLM_ECANCEL) - error = 0; - out_put: - dlm_put_lkb(lkb); - out: - unlock_recovery(ls); - dlm_put_lockspace(ls); - return error; -} - -/* - * send/receive routines for remote operations and replies - * - * send_args - * send_common - * send_request receive_request - * send_convert receive_convert - * send_unlock receive_unlock - * send_cancel receive_cancel - * send_grant receive_grant - * send_bast receive_bast - * send_lookup receive_lookup - * send_remove receive_remove - * - * send_common_reply - * receive_request_reply send_request_reply - * receive_convert_reply send_convert_reply - * receive_unlock_reply send_unlock_reply - * receive_cancel_reply send_cancel_reply - * receive_lookup_reply send_lookup_reply - */ - -static int create_message(struct dlm_rsb *r, struct dlm_lkb *lkb, - int to_nodeid, int mstype, - struct dlm_message **ms_ret, - struct dlm_mhandle **mh_ret) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - char *mb; - int mb_len = sizeof(struct dlm_message); - - switch (mstype) { - case DLM_MSG_REQUEST: - case DLM_MSG_LOOKUP: - case DLM_MSG_REMOVE: - mb_len += r->res_length; - break; - case DLM_MSG_CONVERT: - case DLM_MSG_UNLOCK: - case DLM_MSG_REQUEST_REPLY: - case DLM_MSG_CONVERT_REPLY: - case DLM_MSG_GRANT: - if (lkb && lkb->lkb_lvbptr) - mb_len += r->res_ls->ls_lvblen; - break; - } - - /* get_buffer gives us a message handle (mh) that we need to - pass into lowcomms_commit and a message buffer (mb) that we - write our data into */ - - mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, GFP_KERNEL, &mb); - if (!mh) - return -ENOBUFS; - - memset(mb, 0, mb_len); - - ms = (struct dlm_message *) mb; - - ms->m_header.h_version = (DLM_HEADER_MAJOR | DLM_HEADER_MINOR); - ms->m_header.h_lockspace = r->res_ls->ls_global_id; - ms->m_header.h_nodeid = dlm_our_nodeid(); - ms->m_header.h_length = mb_len; - ms->m_header.h_cmd = DLM_MSG; - - ms->m_type = mstype; - - *mh_ret = mh; - *ms_ret = ms; - return 0; -} - -/* further lowcomms enhancements or alternate implementations may make - the return value from this function useful at some point */ - -static int send_message(struct dlm_mhandle *mh, struct dlm_message *ms) -{ - dlm_message_out(ms); - dlm_lowcomms_commit_buffer(mh); - return 0; -} - -static void send_args(struct dlm_rsb *r, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - ms->m_nodeid = lkb->lkb_nodeid; - ms->m_pid = lkb->lkb_ownpid; - ms->m_lkid = lkb->lkb_id; - ms->m_remid = lkb->lkb_remid; - ms->m_exflags = lkb->lkb_exflags; - ms->m_sbflags = lkb->lkb_sbflags; - ms->m_flags = lkb->lkb_flags; - ms->m_lvbseq = lkb->lkb_lvbseq; - ms->m_status = lkb->lkb_status; - ms->m_grmode = lkb->lkb_grmode; - ms->m_rqmode = lkb->lkb_rqmode; - ms->m_hash = r->res_hash; - - /* m_result and m_bastmode are set from function args, - not from lkb fields */ - - if (lkb->lkb_bastaddr) - ms->m_asts |= AST_BAST; - if (lkb->lkb_astaddr) - ms->m_asts |= AST_COMP; - - if (ms->m_type == DLM_MSG_REQUEST || ms->m_type == DLM_MSG_LOOKUP) - memcpy(ms->m_extra, r->res_name, r->res_length); - - else if (lkb->lkb_lvbptr) - memcpy(ms->m_extra, lkb->lkb_lvbptr, r->res_ls->ls_lvblen); - -} - -static int send_common(struct dlm_rsb *r, struct dlm_lkb *lkb, int mstype) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - int to_nodeid, error; - - add_to_waiters(lkb, mstype); - - to_nodeid = r->res_nodeid; - - error = create_message(r, lkb, to_nodeid, mstype, &ms, &mh); - if (error) - goto fail; - - send_args(r, lkb, ms); - - error = send_message(mh, ms); - if (error) - goto fail; - return 0; - - fail: - remove_from_waiters(lkb); - return error; -} - -static int send_request(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - return send_common(r, lkb, DLM_MSG_REQUEST); -} - -static int send_convert(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - int error; - - error = send_common(r, lkb, DLM_MSG_CONVERT); - - /* down conversions go without a reply from the master */ - if (!error && down_conversion(lkb)) { - remove_from_waiters(lkb); - r->res_ls->ls_stub_ms.m_result = 0; - r->res_ls->ls_stub_ms.m_flags = lkb->lkb_flags; - __receive_convert_reply(r, lkb, &r->res_ls->ls_stub_ms); - } - - return error; -} - -/* FIXME: if this lkb is the only lock we hold on the rsb, then set - MASTER_UNCERTAIN to force the next request on the rsb to confirm - that the master is still correct. */ - -static int send_unlock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - return send_common(r, lkb, DLM_MSG_UNLOCK); -} - -static int send_cancel(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - return send_common(r, lkb, DLM_MSG_CANCEL); -} - -static int send_grant(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - int to_nodeid, error; - - to_nodeid = lkb->lkb_nodeid; - - error = create_message(r, lkb, to_nodeid, DLM_MSG_GRANT, &ms, &mh); - if (error) - goto out; - - send_args(r, lkb, ms); - - ms->m_result = 0; - - error = send_message(mh, ms); - out: - return error; -} - -static int send_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int mode) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - int to_nodeid, error; - - to_nodeid = lkb->lkb_nodeid; - - error = create_message(r, NULL, to_nodeid, DLM_MSG_BAST, &ms, &mh); - if (error) - goto out; - - send_args(r, lkb, ms); - - ms->m_bastmode = mode; - - error = send_message(mh, ms); - out: - return error; -} - -static int send_lookup(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - int to_nodeid, error; - - add_to_waiters(lkb, DLM_MSG_LOOKUP); - - to_nodeid = dlm_dir_nodeid(r); - - error = create_message(r, NULL, to_nodeid, DLM_MSG_LOOKUP, &ms, &mh); - if (error) - goto fail; - - send_args(r, lkb, ms); - - error = send_message(mh, ms); - if (error) - goto fail; - return 0; - - fail: - remove_from_waiters(lkb); - return error; -} - -static int send_remove(struct dlm_rsb *r) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - int to_nodeid, error; - - to_nodeid = dlm_dir_nodeid(r); - - error = create_message(r, NULL, to_nodeid, DLM_MSG_REMOVE, &ms, &mh); - if (error) - goto out; - - memcpy(ms->m_extra, r->res_name, r->res_length); - ms->m_hash = r->res_hash; - - error = send_message(mh, ms); - out: - return error; -} - -static int send_common_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, - int mstype, int rv) -{ - struct dlm_message *ms; - struct dlm_mhandle *mh; - int to_nodeid, error; - - to_nodeid = lkb->lkb_nodeid; - - error = create_message(r, lkb, to_nodeid, mstype, &ms, &mh); - if (error) - goto out; - - send_args(r, lkb, ms); - - ms->m_result = rv; - - error = send_message(mh, ms); - out: - return error; -} - -static int send_request_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv) -{ - return send_common_reply(r, lkb, DLM_MSG_REQUEST_REPLY, rv); -} - -static int send_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv) -{ - return send_common_reply(r, lkb, DLM_MSG_CONVERT_REPLY, rv); -} - -static int send_unlock_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv) -{ - return send_common_reply(r, lkb, DLM_MSG_UNLOCK_REPLY, rv); -} - -static int send_cancel_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, int rv) -{ - return send_common_reply(r, lkb, DLM_MSG_CANCEL_REPLY, rv); -} - -static int send_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms_in, - int ret_nodeid, int rv) -{ - struct dlm_rsb *r = &ls->ls_stub_rsb; - struct dlm_message *ms; - struct dlm_mhandle *mh; - int error, nodeid = ms_in->m_header.h_nodeid; - - error = create_message(r, NULL, nodeid, DLM_MSG_LOOKUP_REPLY, &ms, &mh); - if (error) - goto out; - - ms->m_lkid = ms_in->m_lkid; - ms->m_result = rv; - ms->m_nodeid = ret_nodeid; - - error = send_message(mh, ms); - out: - return error; -} - -/* which args we save from a received message depends heavily on the type - of message, unlike the send side where we can safely send everything about - the lkb for any type of message */ - -static void receive_flags(struct dlm_lkb *lkb, struct dlm_message *ms) -{ - lkb->lkb_exflags = ms->m_exflags; - lkb->lkb_flags = (lkb->lkb_flags & 0xFFFF0000) | - (ms->m_flags & 0x0000FFFF); -} - -static void receive_flags_reply(struct dlm_lkb *lkb, struct dlm_message *ms) -{ - lkb->lkb_sbflags = ms->m_sbflags; - lkb->lkb_flags = (lkb->lkb_flags & 0xFFFF0000) | - (ms->m_flags & 0x0000FFFF); -} - -static int receive_extralen(struct dlm_message *ms) -{ - return (ms->m_header.h_length - sizeof(struct dlm_message)); -} - -static int receive_lvb(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - int len; - - if (lkb->lkb_exflags & DLM_LKF_VALBLK) { - if (!lkb->lkb_lvbptr) - lkb->lkb_lvbptr = allocate_lvb(ls); - if (!lkb->lkb_lvbptr) - return -ENOMEM; - len = receive_extralen(ms); - memcpy(lkb->lkb_lvbptr, ms->m_extra, len); - } - return 0; -} - -static int receive_request_args(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - lkb->lkb_nodeid = ms->m_header.h_nodeid; - lkb->lkb_ownpid = ms->m_pid; - lkb->lkb_remid = ms->m_lkid; - lkb->lkb_grmode = DLM_LOCK_IV; - lkb->lkb_rqmode = ms->m_rqmode; - lkb->lkb_bastaddr = (void *) (long) (ms->m_asts & AST_BAST); - lkb->lkb_astaddr = (void *) (long) (ms->m_asts & AST_COMP); - - DLM_ASSERT(is_master_copy(lkb), dlm_print_lkb(lkb);); - - if (receive_lvb(ls, lkb, ms)) - return -ENOMEM; - - return 0; -} - -static int receive_convert_args(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - if (lkb->lkb_nodeid != ms->m_header.h_nodeid) { - log_error(ls, "convert_args nodeid %d %d lkid %x %x", - lkb->lkb_nodeid, ms->m_header.h_nodeid, - lkb->lkb_id, lkb->lkb_remid); - return -EINVAL; - } - - if (!is_master_copy(lkb)) - return -EINVAL; - - if (lkb->lkb_status != DLM_LKSTS_GRANTED) - return -EBUSY; - - if (receive_lvb(ls, lkb, ms)) - return -ENOMEM; - - lkb->lkb_rqmode = ms->m_rqmode; - lkb->lkb_lvbseq = ms->m_lvbseq; - - return 0; -} - -static int receive_unlock_args(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - if (!is_master_copy(lkb)) - return -EINVAL; - if (receive_lvb(ls, lkb, ms)) - return -ENOMEM; - return 0; -} - -/* We fill in the stub-lkb fields with the info that send_xxxx_reply() - uses to send a reply and that the remote end uses to process the reply. */ - -static void setup_stub_lkb(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb = &ls->ls_stub_lkb; - lkb->lkb_nodeid = ms->m_header.h_nodeid; - lkb->lkb_remid = ms->m_lkid; -} - -static void receive_request(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error, namelen; - - error = create_lkb(ls, &lkb); - if (error) - goto fail; - - receive_flags(lkb, ms); - lkb->lkb_flags |= DLM_IFL_MSTCPY; - error = receive_request_args(ls, lkb, ms); - if (error) { - __put_lkb(ls, lkb); - goto fail; - } - - namelen = receive_extralen(ms); - - error = find_rsb(ls, ms->m_extra, namelen, R_MASTER, &r); - if (error) { - __put_lkb(ls, lkb); - goto fail; - } - - lock_rsb(r); - - attach_lkb(r, lkb); - error = do_request(r, lkb); - send_request_reply(r, lkb, error); - - unlock_rsb(r); - put_rsb(r); - - if (error == -EINPROGRESS) - error = 0; - if (error) - dlm_put_lkb(lkb); - return; - - fail: - setup_stub_lkb(ls, ms); - send_request_reply(&ls->ls_stub_rsb, &ls->ls_stub_lkb, error); -} - -static void receive_convert(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error, reply = 1; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) - goto fail; - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - receive_flags(lkb, ms); - error = receive_convert_args(ls, lkb, ms); - if (error) - goto out; - reply = !down_conversion(lkb); - - error = do_convert(r, lkb); - out: - if (reply) - send_convert_reply(r, lkb, error); - - unlock_rsb(r); - put_rsb(r); - dlm_put_lkb(lkb); - return; - - fail: - setup_stub_lkb(ls, ms); - send_convert_reply(&ls->ls_stub_rsb, &ls->ls_stub_lkb, error); -} - -static void receive_unlock(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) - goto fail; - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - receive_flags(lkb, ms); - error = receive_unlock_args(ls, lkb, ms); - if (error) - goto out; - - error = do_unlock(r, lkb); - out: - send_unlock_reply(r, lkb, error); - - unlock_rsb(r); - put_rsb(r); - dlm_put_lkb(lkb); - return; - - fail: - setup_stub_lkb(ls, ms); - send_unlock_reply(&ls->ls_stub_rsb, &ls->ls_stub_lkb, error); -} - -static void receive_cancel(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) - goto fail; - - receive_flags(lkb, ms); - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - error = do_cancel(r, lkb); - send_cancel_reply(r, lkb, error); - - unlock_rsb(r); - put_rsb(r); - dlm_put_lkb(lkb); - return; - - fail: - setup_stub_lkb(ls, ms); - send_cancel_reply(&ls->ls_stub_rsb, &ls->ls_stub_lkb, error); -} - -static void receive_grant(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) { - log_error(ls, "receive_grant no lkb"); - return; - } - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - receive_flags_reply(lkb, ms); - grant_lock_pc(r, lkb, ms); - queue_cast(r, lkb, 0); - - unlock_rsb(r); - put_rsb(r); - dlm_put_lkb(lkb); -} - -static void receive_bast(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) { - log_error(ls, "receive_bast no lkb"); - return; - } - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - queue_bast(r, lkb, ms->m_bastmode); - - unlock_rsb(r); - put_rsb(r); - dlm_put_lkb(lkb); -} - -static void receive_lookup(struct dlm_ls *ls, struct dlm_message *ms) -{ - int len, error, ret_nodeid, dir_nodeid, from_nodeid, our_nodeid; - - from_nodeid = ms->m_header.h_nodeid; - our_nodeid = dlm_our_nodeid(); - - len = receive_extralen(ms); - - dir_nodeid = dlm_hash2nodeid(ls, ms->m_hash); - if (dir_nodeid != our_nodeid) { - log_error(ls, "lookup dir_nodeid %d from %d", - dir_nodeid, from_nodeid); - error = -EINVAL; - ret_nodeid = -1; - goto out; - } - - error = dlm_dir_lookup(ls, from_nodeid, ms->m_extra, len, &ret_nodeid); - - /* Optimization: we're master so treat lookup as a request */ - if (!error && ret_nodeid == our_nodeid) { - receive_request(ls, ms); - return; - } - out: - send_lookup_reply(ls, ms, ret_nodeid, error); -} - -static void receive_remove(struct dlm_ls *ls, struct dlm_message *ms) -{ - int len, dir_nodeid, from_nodeid; - - from_nodeid = ms->m_header.h_nodeid; - - len = receive_extralen(ms); - - dir_nodeid = dlm_hash2nodeid(ls, ms->m_hash); - if (dir_nodeid != dlm_our_nodeid()) { - log_error(ls, "remove dir entry dir_nodeid %d from %d", - dir_nodeid, from_nodeid); - return; - } - - dlm_dir_remove_entry(ls, from_nodeid, ms->m_extra, len); -} - -static void receive_request_reply(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error, mstype; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) { - log_error(ls, "receive_request_reply no lkb"); - return; - } - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - mstype = lkb->lkb_wait_type; - error = remove_from_waiters(lkb); - if (error) { - log_error(ls, "receive_request_reply not on waiters"); - goto out; - } - - /* this is the value returned from do_request() on the master */ - error = ms->m_result; - - r = lkb->lkb_resource; - hold_rsb(r); - lock_rsb(r); - - /* Optimization: the dir node was also the master, so it took our - lookup as a request and sent request reply instead of lookup reply */ - if (mstype == DLM_MSG_LOOKUP) { - r->res_nodeid = ms->m_header.h_nodeid; - lkb->lkb_nodeid = r->res_nodeid; - } - - switch (error) { - case -EAGAIN: - /* request would block (be queued) on remote master; - the unhold undoes the original ref from create_lkb() - so it leads to the lkb being freed */ - queue_cast(r, lkb, -EAGAIN); - confirm_master(r, -EAGAIN); - unhold_lkb(lkb); - break; - - case -EINPROGRESS: - case 0: - /* request was queued or granted on remote master */ - receive_flags_reply(lkb, ms); - lkb->lkb_remid = ms->m_lkid; - if (error) - add_lkb(r, lkb, DLM_LKSTS_WAITING); - else { - grant_lock_pc(r, lkb, ms); - queue_cast(r, lkb, 0); - } - confirm_master(r, error); - break; - - case -EBADR: - case -ENOTBLK: - /* find_rsb failed to find rsb or rsb wasn't master */ - r->res_nodeid = -1; - lkb->lkb_nodeid = -1; - _request_lock(r, lkb); - break; - - default: - log_error(ls, "receive_request_reply error %d", error); - } - - unlock_rsb(r); - put_rsb(r); - out: - dlm_put_lkb(lkb); -} - -static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb, - struct dlm_message *ms) -{ - int error = ms->m_result; - - /* this is the value returned from do_convert() on the master */ - - switch (error) { - case -EAGAIN: - /* convert would block (be queued) on remote master */ - queue_cast(r, lkb, -EAGAIN); - break; - - case -EINPROGRESS: - /* convert was queued on remote master */ - del_lkb(r, lkb); - add_lkb(r, lkb, DLM_LKSTS_CONVERT); - break; - - case 0: - /* convert was granted on remote master */ - receive_flags_reply(lkb, ms); - grant_lock_pc(r, lkb, ms); - queue_cast(r, lkb, 0); - break; - - default: - log_error(r->res_ls, "receive_convert_reply error %d", error); - } -} - -static void _receive_convert_reply(struct dlm_lkb *lkb, struct dlm_message *ms) -{ - struct dlm_rsb *r = lkb->lkb_resource; - - hold_rsb(r); - lock_rsb(r); - - __receive_convert_reply(r, lkb, ms); - - unlock_rsb(r); - put_rsb(r); -} - -static void receive_convert_reply(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) { - log_error(ls, "receive_convert_reply no lkb"); - return; - } - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - error = remove_from_waiters(lkb); - if (error) { - log_error(ls, "receive_convert_reply not on waiters"); - goto out; - } - - _receive_convert_reply(lkb, ms); - out: - dlm_put_lkb(lkb); -} - -static void _receive_unlock_reply(struct dlm_lkb *lkb, struct dlm_message *ms) -{ - struct dlm_rsb *r = lkb->lkb_resource; - int error = ms->m_result; - - hold_rsb(r); - lock_rsb(r); - - /* this is the value returned from do_unlock() on the master */ - - switch (error) { - case -DLM_EUNLOCK: - receive_flags_reply(lkb, ms); - remove_lock_pc(r, lkb); - queue_cast(r, lkb, -DLM_EUNLOCK); - break; - default: - log_error(r->res_ls, "receive_unlock_reply error %d", error); - } - - unlock_rsb(r); - put_rsb(r); -} - -static void receive_unlock_reply(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) { - log_error(ls, "receive_unlock_reply no lkb"); - return; - } - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - error = remove_from_waiters(lkb); - if (error) { - log_error(ls, "receive_unlock_reply not on waiters"); - goto out; - } - - _receive_unlock_reply(lkb, ms); - out: - dlm_put_lkb(lkb); -} - -static void _receive_cancel_reply(struct dlm_lkb *lkb, struct dlm_message *ms) -{ - struct dlm_rsb *r = lkb->lkb_resource; - int error = ms->m_result; - - hold_rsb(r); - lock_rsb(r); - - /* this is the value returned from do_cancel() on the master */ - - switch (error) { - case -DLM_ECANCEL: - receive_flags_reply(lkb, ms); - revert_lock_pc(r, lkb); - queue_cast(r, lkb, -DLM_ECANCEL); - break; - default: - log_error(r->res_ls, "receive_cancel_reply error %d", error); - } - - unlock_rsb(r); - put_rsb(r); -} - -static void receive_cancel_reply(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - int error; - - error = find_lkb(ls, ms->m_remid, &lkb); - if (error) { - log_error(ls, "receive_cancel_reply no lkb"); - return; - } - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - error = remove_from_waiters(lkb); - if (error) { - log_error(ls, "receive_cancel_reply not on waiters"); - goto out; - } - - _receive_cancel_reply(lkb, ms); - out: - dlm_put_lkb(lkb); -} - -static void receive_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error, ret_nodeid; - - error = find_lkb(ls, ms->m_lkid, &lkb); - if (error) { - log_error(ls, "receive_lookup_reply no lkb"); - return; - } - - error = remove_from_waiters(lkb); - if (error) { - log_error(ls, "receive_lookup_reply not on waiters"); - goto out; - } - - /* this is the value returned by dlm_dir_lookup on dir node - FIXME: will a non-zero error ever be returned? */ - error = ms->m_result; - - r = lkb->lkb_resource; - hold_rsb(r); - lock_rsb(r); - - ret_nodeid = ms->m_nodeid; - if (ret_nodeid == dlm_our_nodeid()) { - r->res_nodeid = 0; - ret_nodeid = 0; - r->res_first_lkid = 0; - } else { - /* set_master() will copy res_nodeid to lkb_nodeid */ - r->res_nodeid = ret_nodeid; - } - - _request_lock(r, lkb); - - if (!ret_nodeid) - process_lookup_list(r); - - unlock_rsb(r); - put_rsb(r); - out: - dlm_put_lkb(lkb); -} - -int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery) -{ - struct dlm_message *ms = (struct dlm_message *) hd; - struct dlm_ls *ls; - int error; - - if (!recovery) - dlm_message_in(ms); - - ls = dlm_find_lockspace_global(hd->h_lockspace); - if (!ls) { - log_print("drop message %d from %d for unknown lockspace %d", - ms->m_type, nodeid, hd->h_lockspace); - return -EINVAL; - } - - /* recovery may have just ended leaving a bunch of backed-up requests - in the requestqueue; wait while dlm_recoverd clears them */ - - if (!recovery) - dlm_wait_requestqueue(ls); - - /* recovery may have just started while there were a bunch of - in-flight requests -- save them in requestqueue to be processed - after recovery. we can't let dlm_recvd block on the recovery - lock. if dlm_recoverd is calling this function to clear the - requestqueue, it needs to be interrupted (-EINTR) if another - recovery operation is starting. */ - - while (1) { - if (dlm_locking_stopped(ls)) { - if (!recovery) - dlm_add_requestqueue(ls, nodeid, hd); - error = -EINTR; - goto out; - } - - if (lock_recovery_try(ls)) - break; - schedule(); - } - - switch (ms->m_type) { - - /* messages sent to a master node */ - - case DLM_MSG_REQUEST: - receive_request(ls, ms); - break; - - case DLM_MSG_CONVERT: - receive_convert(ls, ms); - break; - - case DLM_MSG_UNLOCK: - receive_unlock(ls, ms); - break; - - case DLM_MSG_CANCEL: - receive_cancel(ls, ms); - break; - - /* messages sent from a master node (replies to above) */ - - case DLM_MSG_REQUEST_REPLY: - receive_request_reply(ls, ms); - break; - - case DLM_MSG_CONVERT_REPLY: - receive_convert_reply(ls, ms); - break; - - case DLM_MSG_UNLOCK_REPLY: - receive_unlock_reply(ls, ms); - break; - - case DLM_MSG_CANCEL_REPLY: - receive_cancel_reply(ls, ms); - break; - - /* messages sent from a master node (only two types of async msg) */ - - case DLM_MSG_GRANT: - receive_grant(ls, ms); - break; - - case DLM_MSG_BAST: - receive_bast(ls, ms); - break; - - /* messages sent to a dir node */ - - case DLM_MSG_LOOKUP: - receive_lookup(ls, ms); - break; - - case DLM_MSG_REMOVE: - receive_remove(ls, ms); - break; - - /* messages sent from a dir node (remove has no reply) */ - - case DLM_MSG_LOOKUP_REPLY: - receive_lookup_reply(ls, ms); - break; - - default: - log_error(ls, "unknown message type %d", ms->m_type); - } - - unlock_recovery(ls); - out: - dlm_put_lockspace(ls); - dlm_astd_wake(); - return 0; -} - - -/* - * Recovery related - */ - -static void recover_convert_waiter(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - if (middle_conversion(lkb)) { - hold_lkb(lkb); - ls->ls_stub_ms.m_result = -EINPROGRESS; - _remove_from_waiters(lkb); - _receive_convert_reply(lkb, &ls->ls_stub_ms); - - /* Same special case as in receive_rcom_lock_args() */ - lkb->lkb_grmode = DLM_LOCK_IV; - rsb_set_flag(lkb->lkb_resource, RSB_RECOVER_CONVERT); - unhold_lkb(lkb); - - } else if (lkb->lkb_rqmode >= lkb->lkb_grmode) { - lkb->lkb_flags |= DLM_IFL_RESEND; - } - - /* lkb->lkb_rqmode < lkb->lkb_grmode shouldn't happen since down - conversions are async; there's no reply from the remote master */ -} - -/* A waiting lkb needs recovery if the master node has failed, or - the master node is changing (only when no directory is used) */ - -static int waiter_needs_recovery(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - if (dlm_is_removed(ls, lkb->lkb_nodeid)) - return 1; - - if (!dlm_no_directory(ls)) - return 0; - - if (dlm_dir_nodeid(lkb->lkb_resource) != lkb->lkb_nodeid) - return 1; - - return 0; -} - -/* Recovery for locks that are waiting for replies from nodes that are now - gone. We can just complete unlocks and cancels by faking a reply from the - dead node. Requests and up-conversions we flag to be resent after - recovery. Down-conversions can just be completed with a fake reply like - unlocks. Conversions between PR and CW need special attention. */ - -void dlm_recover_waiters_pre(struct dlm_ls *ls) -{ - struct dlm_lkb *lkb, *safe; - - mutex_lock(&ls->ls_waiters_mutex); - - list_for_each_entry_safe(lkb, safe, &ls->ls_waiters, lkb_wait_reply) { - log_debug(ls, "pre recover waiter lkid %x type %d flags %x", - lkb->lkb_id, lkb->lkb_wait_type, lkb->lkb_flags); - - /* all outstanding lookups, regardless of destination will be - resent after recovery is done */ - - if (lkb->lkb_wait_type == DLM_MSG_LOOKUP) { - lkb->lkb_flags |= DLM_IFL_RESEND; - continue; - } - - if (!waiter_needs_recovery(ls, lkb)) - continue; - - switch (lkb->lkb_wait_type) { - - case DLM_MSG_REQUEST: - lkb->lkb_flags |= DLM_IFL_RESEND; - break; - - case DLM_MSG_CONVERT: - recover_convert_waiter(ls, lkb); - break; - - case DLM_MSG_UNLOCK: - hold_lkb(lkb); - ls->ls_stub_ms.m_result = -DLM_EUNLOCK; - _remove_from_waiters(lkb); - _receive_unlock_reply(lkb, &ls->ls_stub_ms); - dlm_put_lkb(lkb); - break; - - case DLM_MSG_CANCEL: - hold_lkb(lkb); - ls->ls_stub_ms.m_result = -DLM_ECANCEL; - _remove_from_waiters(lkb); - _receive_cancel_reply(lkb, &ls->ls_stub_ms); - dlm_put_lkb(lkb); - break; - - default: - log_error(ls, "invalid lkb wait_type %d", - lkb->lkb_wait_type); - } - schedule(); - } - mutex_unlock(&ls->ls_waiters_mutex); -} - -static int remove_resend_waiter(struct dlm_ls *ls, struct dlm_lkb **lkb_ret) -{ - struct dlm_lkb *lkb; - int rv = 0; - - mutex_lock(&ls->ls_waiters_mutex); - list_for_each_entry(lkb, &ls->ls_waiters, lkb_wait_reply) { - if (lkb->lkb_flags & DLM_IFL_RESEND) { - rv = lkb->lkb_wait_type; - _remove_from_waiters(lkb); - lkb->lkb_flags &= ~DLM_IFL_RESEND; - break; - } - } - mutex_unlock(&ls->ls_waiters_mutex); - - if (!rv) - lkb = NULL; - *lkb_ret = lkb; - return rv; -} - -/* Deal with lookups and lkb's marked RESEND from _pre. We may now be the - master or dir-node for r. Processing the lkb may result in it being placed - back on waiters. */ - -int dlm_recover_waiters_post(struct dlm_ls *ls) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *r; - int error = 0, mstype; - - while (1) { - if (dlm_locking_stopped(ls)) { - log_debug(ls, "recover_waiters_post aborted"); - error = -EINTR; - break; - } - - mstype = remove_resend_waiter(ls, &lkb); - if (!mstype) - break; - - r = lkb->lkb_resource; - - log_debug(ls, "recover_waiters_post %x type %d flags %x %s", - lkb->lkb_id, mstype, lkb->lkb_flags, r->res_name); - - switch (mstype) { - - case DLM_MSG_LOOKUP: - hold_rsb(r); - lock_rsb(r); - _request_lock(r, lkb); - if (is_master(r)) - confirm_master(r, 0); - unlock_rsb(r); - put_rsb(r); - break; - - case DLM_MSG_REQUEST: - hold_rsb(r); - lock_rsb(r); - _request_lock(r, lkb); - if (is_master(r)) - confirm_master(r, 0); - unlock_rsb(r); - put_rsb(r); - break; - - case DLM_MSG_CONVERT: - hold_rsb(r); - lock_rsb(r); - _convert_lock(r, lkb); - unlock_rsb(r); - put_rsb(r); - break; - - default: - log_error(ls, "recover_waiters_post type %d", mstype); - } - } - - return error; -} - -static void purge_queue(struct dlm_rsb *r, struct list_head *queue, - int (*test)(struct dlm_ls *ls, struct dlm_lkb *lkb)) -{ - struct dlm_ls *ls = r->res_ls; - struct dlm_lkb *lkb, *safe; - - list_for_each_entry_safe(lkb, safe, queue, lkb_statequeue) { - if (test(ls, lkb)) { - rsb_set_flag(r, RSB_LOCKS_PURGED); - del_lkb(r, lkb); - /* this put should free the lkb */ - if (!dlm_put_lkb(lkb)) - log_error(ls, "purged lkb not released"); - } - } -} - -static int purge_dead_test(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - return (is_master_copy(lkb) && dlm_is_removed(ls, lkb->lkb_nodeid)); -} - -static int purge_mstcpy_test(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - return is_master_copy(lkb); -} - -static void purge_dead_locks(struct dlm_rsb *r) -{ - purge_queue(r, &r->res_grantqueue, &purge_dead_test); - purge_queue(r, &r->res_convertqueue, &purge_dead_test); - purge_queue(r, &r->res_waitqueue, &purge_dead_test); -} - -void dlm_purge_mstcpy_locks(struct dlm_rsb *r) -{ - purge_queue(r, &r->res_grantqueue, &purge_mstcpy_test); - purge_queue(r, &r->res_convertqueue, &purge_mstcpy_test); - purge_queue(r, &r->res_waitqueue, &purge_mstcpy_test); -} - -/* Get rid of locks held by nodes that are gone. */ - -int dlm_purge_locks(struct dlm_ls *ls) -{ - struct dlm_rsb *r; - - log_debug(ls, "dlm_purge_locks"); - - down_write(&ls->ls_root_sem); - list_for_each_entry(r, &ls->ls_root_list, res_root_list) { - hold_rsb(r); - lock_rsb(r); - if (is_master(r)) - purge_dead_locks(r); - unlock_rsb(r); - unhold_rsb(r); - - schedule(); - } - up_write(&ls->ls_root_sem); - - return 0; -} - -static struct dlm_rsb *find_purged_rsb(struct dlm_ls *ls, int bucket) -{ - struct dlm_rsb *r, *r_ret = NULL; - - read_lock(&ls->ls_rsbtbl[bucket].lock); - list_for_each_entry(r, &ls->ls_rsbtbl[bucket].list, res_hashchain) { - if (!rsb_flag(r, RSB_LOCKS_PURGED)) - continue; - hold_rsb(r); - rsb_clear_flag(r, RSB_LOCKS_PURGED); - r_ret = r; - break; - } - read_unlock(&ls->ls_rsbtbl[bucket].lock); - return r_ret; -} - -void dlm_grant_after_purge(struct dlm_ls *ls) -{ - struct dlm_rsb *r; - int bucket = 0; - - while (1) { - r = find_purged_rsb(ls, bucket); - if (!r) { - if (bucket == ls->ls_rsbtbl_size - 1) - break; - bucket++; - continue; - } - lock_rsb(r); - if (is_master(r)) { - grant_pending_locks(r); - confirm_master(r, 0); - } - unlock_rsb(r); - put_rsb(r); - schedule(); - } -} - -static struct dlm_lkb *search_remid_list(struct list_head *head, int nodeid, - uint32_t remid) -{ - struct dlm_lkb *lkb; - - list_for_each_entry(lkb, head, lkb_statequeue) { - if (lkb->lkb_nodeid == nodeid && lkb->lkb_remid == remid) - return lkb; - } - return NULL; -} - -static struct dlm_lkb *search_remid(struct dlm_rsb *r, int nodeid, - uint32_t remid) -{ - struct dlm_lkb *lkb; - - lkb = search_remid_list(&r->res_grantqueue, nodeid, remid); - if (lkb) - return lkb; - lkb = search_remid_list(&r->res_convertqueue, nodeid, remid); - if (lkb) - return lkb; - lkb = search_remid_list(&r->res_waitqueue, nodeid, remid); - if (lkb) - return lkb; - return NULL; -} - -static int receive_rcom_lock_args(struct dlm_ls *ls, struct dlm_lkb *lkb, - struct dlm_rsb *r, struct dlm_rcom *rc) -{ - struct rcom_lock *rl = (struct rcom_lock *) rc->rc_buf; - int lvblen; - - lkb->lkb_nodeid = rc->rc_header.h_nodeid; - lkb->lkb_ownpid = rl->rl_ownpid; - lkb->lkb_remid = rl->rl_lkid; - lkb->lkb_exflags = rl->rl_exflags; - lkb->lkb_flags = rl->rl_flags & 0x0000FFFF; - lkb->lkb_flags |= DLM_IFL_MSTCPY; - lkb->lkb_lvbseq = rl->rl_lvbseq; - lkb->lkb_rqmode = rl->rl_rqmode; - lkb->lkb_grmode = rl->rl_grmode; - /* don't set lkb_status because add_lkb wants to itself */ - - lkb->lkb_bastaddr = (void *) (long) (rl->rl_asts & AST_BAST); - lkb->lkb_astaddr = (void *) (long) (rl->rl_asts & AST_COMP); - - if (lkb->lkb_exflags & DLM_LKF_VALBLK) { - lkb->lkb_lvbptr = allocate_lvb(ls); - if (!lkb->lkb_lvbptr) - return -ENOMEM; - lvblen = rc->rc_header.h_length - sizeof(struct dlm_rcom) - - sizeof(struct rcom_lock); - memcpy(lkb->lkb_lvbptr, rl->rl_lvb, lvblen); - } - - /* Conversions between PR and CW (middle modes) need special handling. - The real granted mode of these converting locks cannot be determined - until all locks have been rebuilt on the rsb (recover_conversion) */ - - if (rl->rl_wait_type == DLM_MSG_CONVERT && middle_conversion(lkb)) { - rl->rl_status = DLM_LKSTS_CONVERT; - lkb->lkb_grmode = DLM_LOCK_IV; - rsb_set_flag(r, RSB_RECOVER_CONVERT); - } - - return 0; -} - -/* This lkb may have been recovered in a previous aborted recovery so we need - to check if the rsb already has an lkb with the given remote nodeid/lkid. - If so we just send back a standard reply. If not, we create a new lkb with - the given values and send back our lkid. We send back our lkid by sending - back the rcom_lock struct we got but with the remid field filled in. */ - -int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc) -{ - struct rcom_lock *rl = (struct rcom_lock *) rc->rc_buf; - struct dlm_rsb *r; - struct dlm_lkb *lkb; - int error; - - if (rl->rl_parent_lkid) { - error = -EOPNOTSUPP; - goto out; - } - - error = find_rsb(ls, rl->rl_name, rl->rl_namelen, R_MASTER, &r); - if (error) - goto out; - - lock_rsb(r); - - lkb = search_remid(r, rc->rc_header.h_nodeid, rl->rl_lkid); - if (lkb) { - error = -EEXIST; - goto out_remid; - } - - error = create_lkb(ls, &lkb); - if (error) - goto out_unlock; - - error = receive_rcom_lock_args(ls, lkb, r, rc); - if (error) { - __put_lkb(ls, lkb); - goto out_unlock; - } - - attach_lkb(r, lkb); - add_lkb(r, lkb, rl->rl_status); - error = 0; - - out_remid: - /* this is the new value returned to the lock holder for - saving in its process-copy lkb */ - rl->rl_remid = lkb->lkb_id; - - out_unlock: - unlock_rsb(r); - put_rsb(r); - out: - if (error) - log_print("recover_master_copy %d %x", error, rl->rl_lkid); - rl->rl_result = error; - return error; -} - -int dlm_recover_process_copy(struct dlm_ls *ls, struct dlm_rcom *rc) -{ - struct rcom_lock *rl = (struct rcom_lock *) rc->rc_buf; - struct dlm_rsb *r; - struct dlm_lkb *lkb; - int error; - - error = find_lkb(ls, rl->rl_lkid, &lkb); - if (error) { - log_error(ls, "recover_process_copy no lkid %x", rl->rl_lkid); - return error; - } - - DLM_ASSERT(is_process_copy(lkb), dlm_print_lkb(lkb);); - - error = rl->rl_result; - - r = lkb->lkb_resource; - hold_rsb(r); - lock_rsb(r); - - switch (error) { - case -EEXIST: - log_debug(ls, "master copy exists %x", lkb->lkb_id); - /* fall through */ - case 0: - lkb->lkb_remid = rl->rl_remid; - break; - default: - log_error(ls, "dlm_recover_process_copy unknown error %d %x", - error, lkb->lkb_id); - } - - /* an ack for dlm_recover_locks() which waits for replies from - all the locks it sends to new masters */ - dlm_recovered_lock(r); - - unlock_rsb(r); - put_rsb(r); - dlm_put_lkb(lkb); - - return 0; -} - -int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua, - int mode, uint32_t flags, void *name, unsigned int namelen, - uint32_t parent_lkid) -{ - struct dlm_lkb *lkb; - struct dlm_args args; - int error; - - lock_recovery(ls); - - error = create_lkb(ls, &lkb); - if (error) { - kfree(ua); - goto out; - } - - if (flags & DLM_LKF_VALBLK) { - ua->lksb.sb_lvbptr = kmalloc(DLM_USER_LVB_LEN, GFP_KERNEL); - if (!ua->lksb.sb_lvbptr) { - kfree(ua); - __put_lkb(ls, lkb); - error = -ENOMEM; - goto out; - } - } - - /* After ua is attached to lkb it will be freed by free_lkb(). - When DLM_IFL_USER is set, the dlm knows that this is a userspace - lock and that lkb_astparam is the dlm_user_args structure. */ - - error = set_lock_args(mode, &ua->lksb, flags, namelen, parent_lkid, - DLM_FAKE_USER_AST, ua, DLM_FAKE_USER_AST, &args); - lkb->lkb_flags |= DLM_IFL_USER; - ua->old_mode = DLM_LOCK_IV; - - if (error) { - __put_lkb(ls, lkb); - goto out; - } - - error = request_lock(ls, lkb, name, namelen, &args); - - switch (error) { - case 0: - break; - case -EINPROGRESS: - error = 0; - break; - case -EAGAIN: - error = 0; - /* fall through */ - default: - __put_lkb(ls, lkb); - goto out; - } - - /* add this new lkb to the per-process list of locks */ - spin_lock(&ua->proc->locks_spin); - kref_get(&lkb->lkb_ref); - list_add_tail(&lkb->lkb_ownqueue, &ua->proc->locks); - spin_unlock(&ua->proc->locks_spin); - out: - unlock_recovery(ls); - return error; -} - -int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - int mode, uint32_t flags, uint32_t lkid, char *lvb_in) -{ - struct dlm_lkb *lkb; - struct dlm_args args; - struct dlm_user_args *ua; - int error; - - lock_recovery(ls); - - error = find_lkb(ls, lkid, &lkb); - if (error) - goto out; - - /* user can change the params on its lock when it converts it, or - add an lvb that didn't exist before */ - - ua = (struct dlm_user_args *)lkb->lkb_astparam; - - if (flags & DLM_LKF_VALBLK && !ua->lksb.sb_lvbptr) { - ua->lksb.sb_lvbptr = kmalloc(DLM_USER_LVB_LEN, GFP_KERNEL); - if (!ua->lksb.sb_lvbptr) { - error = -ENOMEM; - goto out_put; - } - } - if (lvb_in && ua->lksb.sb_lvbptr) - memcpy(ua->lksb.sb_lvbptr, lvb_in, DLM_USER_LVB_LEN); - - ua->castparam = ua_tmp->castparam; - ua->castaddr = ua_tmp->castaddr; - ua->bastparam = ua_tmp->bastparam; - ua->bastaddr = ua_tmp->bastaddr; - ua->user_lksb = ua_tmp->user_lksb; - ua->old_mode = lkb->lkb_grmode; - - error = set_lock_args(mode, &ua->lksb, flags, 0, 0, DLM_FAKE_USER_AST, - ua, DLM_FAKE_USER_AST, &args); - if (error) - goto out_put; - - error = convert_lock(ls, lkb, &args); - - if (error == -EINPROGRESS || error == -EAGAIN) - error = 0; - out_put: - dlm_put_lkb(lkb); - out: - unlock_recovery(ls); - kfree(ua_tmp); - return error; -} - -int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - uint32_t flags, uint32_t lkid, char *lvb_in) -{ - struct dlm_lkb *lkb; - struct dlm_args args; - struct dlm_user_args *ua; - int error; - - lock_recovery(ls); - - error = find_lkb(ls, lkid, &lkb); - if (error) - goto out; - - ua = (struct dlm_user_args *)lkb->lkb_astparam; - - if (lvb_in && ua->lksb.sb_lvbptr) - memcpy(ua->lksb.sb_lvbptr, lvb_in, DLM_USER_LVB_LEN); - ua->castparam = ua_tmp->castparam; - ua->user_lksb = ua_tmp->user_lksb; - - error = set_unlock_args(flags, ua, &args); - if (error) - goto out_put; - - error = unlock_lock(ls, lkb, &args); - - if (error == -DLM_EUNLOCK) - error = 0; - if (error) - goto out_put; - - spin_lock(&ua->proc->locks_spin); - list_del_init(&lkb->lkb_ownqueue); - spin_unlock(&ua->proc->locks_spin); - - /* this removes the reference for the proc->locks list added by - dlm_user_request */ - unhold_lkb(lkb); - out_put: - dlm_put_lkb(lkb); - out: - unlock_recovery(ls); - return error; -} - -int dlm_user_cancel(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - uint32_t flags, uint32_t lkid) -{ - struct dlm_lkb *lkb; - struct dlm_args args; - struct dlm_user_args *ua; - int error; - - lock_recovery(ls); - - error = find_lkb(ls, lkid, &lkb); - if (error) - goto out; - - ua = (struct dlm_user_args *)lkb->lkb_astparam; - ua->castparam = ua_tmp->castparam; - ua->user_lksb = ua_tmp->user_lksb; - - error = set_unlock_args(flags, ua, &args); - if (error) - goto out_put; - - error = cancel_lock(ls, lkb, &args); - - if (error == -DLM_ECANCEL) - error = 0; - if (error) - goto out_put; - - /* this lkb was removed from the WAITING queue */ - if (lkb->lkb_grmode == DLM_LOCK_IV) { - spin_lock(&ua->proc->locks_spin); - list_del_init(&lkb->lkb_ownqueue); - spin_unlock(&ua->proc->locks_spin); - unhold_lkb(lkb); - } - out_put: - dlm_put_lkb(lkb); - out: - unlock_recovery(ls); - return error; -} - -static int orphan_proc_lock(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - struct dlm_user_args *ua = (struct dlm_user_args *)lkb->lkb_astparam; - - if (ua->lksb.sb_lvbptr) - kfree(ua->lksb.sb_lvbptr); - kfree(ua); - lkb->lkb_astparam = (long)NULL; - - /* TODO: propogate to master if needed */ - return 0; -} - -/* The force flag allows the unlock to go ahead even if the lkb isn't granted. - Regardless of what rsb queue the lock is on, it's removed and freed. */ - -static int unlock_proc_lock(struct dlm_ls *ls, struct dlm_lkb *lkb) -{ - struct dlm_user_args *ua = (struct dlm_user_args *)lkb->lkb_astparam; - struct dlm_args args; - int error; - - /* FIXME: we need to handle the case where the lkb is in limbo - while the rsb is being looked up, currently we assert in - _unlock_lock/is_remote because rsb nodeid is -1. */ - - set_unlock_args(DLM_LKF_FORCEUNLOCK, ua, &args); - - error = unlock_lock(ls, lkb, &args); - if (error == -DLM_EUNLOCK) - error = 0; - return error; -} - -/* The ls_clear_proc_locks mutex protects against dlm_user_add_asts() which - 1) references lkb->ua which we free here and 2) adds lkbs to proc->asts, - which we clear here. */ - -/* proc CLOSING flag is set so no more device_reads should look at proc->asts - list, and no more device_writes should add lkb's to proc->locks list; so we - shouldn't need to take asts_spin or locks_spin here. this assumes that - device reads/writes/closes are serialized -- FIXME: we may need to serialize - them ourself. */ - -void dlm_clear_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc) -{ - struct dlm_lkb *lkb, *safe; - - lock_recovery(ls); - mutex_lock(&ls->ls_clear_proc_locks); - - list_for_each_entry_safe(lkb, safe, &proc->locks, lkb_ownqueue) { - if (lkb->lkb_ast_type) { - list_del(&lkb->lkb_astqueue); - unhold_lkb(lkb); - } - - list_del_init(&lkb->lkb_ownqueue); - - if (lkb->lkb_exflags & DLM_LKF_PERSISTENT) { - lkb->lkb_flags |= DLM_IFL_ORPHAN; - orphan_proc_lock(ls, lkb); - } else { - lkb->lkb_flags |= DLM_IFL_DEAD; - unlock_proc_lock(ls, lkb); - } - - /* this removes the reference for the proc->locks list - added by dlm_user_request, it may result in the lkb - being freed */ - - dlm_put_lkb(lkb); - } - mutex_unlock(&ls->ls_clear_proc_locks); - unlock_recovery(ls); -} diff --git a/trunk/fs/dlm/lock.h b/trunk/fs/dlm/lock.h deleted file mode 100644 index 0843a3073ec3..000000000000 --- a/trunk/fs/dlm/lock.h +++ /dev/null @@ -1,62 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __LOCK_DOT_H__ -#define __LOCK_DOT_H__ - -void dlm_print_rsb(struct dlm_rsb *r); -void dlm_dump_rsb(struct dlm_rsb *r); -void dlm_print_lkb(struct dlm_lkb *lkb); -int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery); -int dlm_modes_compat(int mode1, int mode2); -int dlm_find_rsb(struct dlm_ls *ls, char *name, int namelen, - unsigned int flags, struct dlm_rsb **r_ret); -void dlm_put_rsb(struct dlm_rsb *r); -void dlm_hold_rsb(struct dlm_rsb *r); -int dlm_put_lkb(struct dlm_lkb *lkb); -void dlm_scan_rsbs(struct dlm_ls *ls); - -int dlm_purge_locks(struct dlm_ls *ls); -void dlm_purge_mstcpy_locks(struct dlm_rsb *r); -void dlm_grant_after_purge(struct dlm_ls *ls); -int dlm_recover_waiters_post(struct dlm_ls *ls); -void dlm_recover_waiters_pre(struct dlm_ls *ls); -int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc); -int dlm_recover_process_copy(struct dlm_ls *ls, struct dlm_rcom *rc); - -int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua, int mode, - uint32_t flags, void *name, unsigned int namelen, uint32_t parent_lkid); -int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - int mode, uint32_t flags, uint32_t lkid, char *lvb_in); -int dlm_user_unlock(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - uint32_t flags, uint32_t lkid, char *lvb_in); -int dlm_user_cancel(struct dlm_ls *ls, struct dlm_user_args *ua_tmp, - uint32_t flags, uint32_t lkid); -void dlm_clear_proc_locks(struct dlm_ls *ls, struct dlm_user_proc *proc); - -static inline int is_master(struct dlm_rsb *r) -{ - return !r->res_nodeid; -} - -static inline void lock_rsb(struct dlm_rsb *r) -{ - mutex_lock(&r->res_mutex); -} - -static inline void unlock_rsb(struct dlm_rsb *r) -{ - mutex_unlock(&r->res_mutex); -} - -#endif - diff --git a/trunk/fs/dlm/lockspace.c b/trunk/fs/dlm/lockspace.c deleted file mode 100644 index 109333c8ecb9..000000000000 --- a/trunk/fs/dlm/lockspace.c +++ /dev/null @@ -1,717 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "member.h" -#include "recoverd.h" -#include "ast.h" -#include "dir.h" -#include "lowcomms.h" -#include "config.h" -#include "memory.h" -#include "lock.h" -#include "recover.h" - -#ifdef CONFIG_DLM_DEBUG -int dlm_create_debug_file(struct dlm_ls *ls); -void dlm_delete_debug_file(struct dlm_ls *ls); -#else -static inline int dlm_create_debug_file(struct dlm_ls *ls) { return 0; } -static inline void dlm_delete_debug_file(struct dlm_ls *ls) { } -#endif - -static int ls_count; -static struct mutex ls_lock; -static struct list_head lslist; -static spinlock_t lslist_lock; -static struct task_struct * scand_task; - - -static ssize_t dlm_control_store(struct dlm_ls *ls, const char *buf, size_t len) -{ - ssize_t ret = len; - int n = simple_strtol(buf, NULL, 0); - - switch (n) { - case 0: - dlm_ls_stop(ls); - break; - case 1: - dlm_ls_start(ls); - break; - default: - ret = -EINVAL; - } - return ret; -} - -static ssize_t dlm_event_store(struct dlm_ls *ls, const char *buf, size_t len) -{ - ls->ls_uevent_result = simple_strtol(buf, NULL, 0); - set_bit(LSFL_UEVENT_WAIT, &ls->ls_flags); - wake_up(&ls->ls_uevent_wait); - return len; -} - -static ssize_t dlm_id_show(struct dlm_ls *ls, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%u\n", ls->ls_global_id); -} - -static ssize_t dlm_id_store(struct dlm_ls *ls, const char *buf, size_t len) -{ - ls->ls_global_id = simple_strtoul(buf, NULL, 0); - return len; -} - -static ssize_t dlm_recover_status_show(struct dlm_ls *ls, char *buf) -{ - uint32_t status = dlm_recover_status(ls); - return snprintf(buf, PAGE_SIZE, "%x\n", status); -} - -static ssize_t dlm_recover_nodeid_show(struct dlm_ls *ls, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", ls->ls_recover_nodeid); -} - -struct dlm_attr { - struct attribute attr; - ssize_t (*show)(struct dlm_ls *, char *); - ssize_t (*store)(struct dlm_ls *, const char *, size_t); -}; - -static struct dlm_attr dlm_attr_control = { - .attr = {.name = "control", .mode = S_IWUSR}, - .store = dlm_control_store -}; - -static struct dlm_attr dlm_attr_event = { - .attr = {.name = "event_done", .mode = S_IWUSR}, - .store = dlm_event_store -}; - -static struct dlm_attr dlm_attr_id = { - .attr = {.name = "id", .mode = S_IRUGO | S_IWUSR}, - .show = dlm_id_show, - .store = dlm_id_store -}; - -static struct dlm_attr dlm_attr_recover_status = { - .attr = {.name = "recover_status", .mode = S_IRUGO}, - .show = dlm_recover_status_show -}; - -static struct dlm_attr dlm_attr_recover_nodeid = { - .attr = {.name = "recover_nodeid", .mode = S_IRUGO}, - .show = dlm_recover_nodeid_show -}; - -static struct attribute *dlm_attrs[] = { - &dlm_attr_control.attr, - &dlm_attr_event.attr, - &dlm_attr_id.attr, - &dlm_attr_recover_status.attr, - &dlm_attr_recover_nodeid.attr, - NULL, -}; - -static ssize_t dlm_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct dlm_ls *ls = container_of(kobj, struct dlm_ls, ls_kobj); - struct dlm_attr *a = container_of(attr, struct dlm_attr, attr); - return a->show ? a->show(ls, buf) : 0; -} - -static ssize_t dlm_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t len) -{ - struct dlm_ls *ls = container_of(kobj, struct dlm_ls, ls_kobj); - struct dlm_attr *a = container_of(attr, struct dlm_attr, attr); - return a->store ? a->store(ls, buf, len) : len; -} - -static struct sysfs_ops dlm_attr_ops = { - .show = dlm_attr_show, - .store = dlm_attr_store, -}; - -static struct kobj_type dlm_ktype = { - .default_attrs = dlm_attrs, - .sysfs_ops = &dlm_attr_ops, -}; - -static struct kset dlm_kset = { - .subsys = &kernel_subsys, - .kobj = {.name = "dlm",}, - .ktype = &dlm_ktype, -}; - -static int kobject_setup(struct dlm_ls *ls) -{ - char lsname[DLM_LOCKSPACE_LEN]; - int error; - - memset(lsname, 0, DLM_LOCKSPACE_LEN); - snprintf(lsname, DLM_LOCKSPACE_LEN, "%s", ls->ls_name); - - error = kobject_set_name(&ls->ls_kobj, "%s", lsname); - if (error) - return error; - - ls->ls_kobj.kset = &dlm_kset; - ls->ls_kobj.ktype = &dlm_ktype; - return 0; -} - -static int do_uevent(struct dlm_ls *ls, int in) -{ - int error; - - if (in) - kobject_uevent(&ls->ls_kobj, KOBJ_ONLINE); - else - kobject_uevent(&ls->ls_kobj, KOBJ_OFFLINE); - - error = wait_event_interruptible(ls->ls_uevent_wait, - test_and_clear_bit(LSFL_UEVENT_WAIT, &ls->ls_flags)); - if (error) - goto out; - - error = ls->ls_uevent_result; - out: - return error; -} - - -int dlm_lockspace_init(void) -{ - int error; - - ls_count = 0; - mutex_init(&ls_lock); - INIT_LIST_HEAD(&lslist); - spin_lock_init(&lslist_lock); - - error = kset_register(&dlm_kset); - if (error) - printk("dlm_lockspace_init: cannot register kset %d\n", error); - return error; -} - -void dlm_lockspace_exit(void) -{ - kset_unregister(&dlm_kset); -} - -static int dlm_scand(void *data) -{ - struct dlm_ls *ls; - - while (!kthread_should_stop()) { - list_for_each_entry(ls, &lslist, ls_list) - dlm_scan_rsbs(ls); - schedule_timeout_interruptible(dlm_config.scan_secs * HZ); - } - return 0; -} - -static int dlm_scand_start(void) -{ - struct task_struct *p; - int error = 0; - - p = kthread_run(dlm_scand, NULL, "dlm_scand"); - if (IS_ERR(p)) - error = PTR_ERR(p); - else - scand_task = p; - return error; -} - -static void dlm_scand_stop(void) -{ - kthread_stop(scand_task); -} - -static struct dlm_ls *dlm_find_lockspace_name(char *name, int namelen) -{ - struct dlm_ls *ls; - - spin_lock(&lslist_lock); - - list_for_each_entry(ls, &lslist, ls_list) { - if (ls->ls_namelen == namelen && - memcmp(ls->ls_name, name, namelen) == 0) - goto out; - } - ls = NULL; - out: - spin_unlock(&lslist_lock); - return ls; -} - -struct dlm_ls *dlm_find_lockspace_global(uint32_t id) -{ - struct dlm_ls *ls; - - spin_lock(&lslist_lock); - - list_for_each_entry(ls, &lslist, ls_list) { - if (ls->ls_global_id == id) { - ls->ls_count++; - goto out; - } - } - ls = NULL; - out: - spin_unlock(&lslist_lock); - return ls; -} - -struct dlm_ls *dlm_find_lockspace_local(dlm_lockspace_t *lockspace) -{ - struct dlm_ls *ls; - - spin_lock(&lslist_lock); - list_for_each_entry(ls, &lslist, ls_list) { - if (ls->ls_local_handle == lockspace) { - ls->ls_count++; - goto out; - } - } - ls = NULL; - out: - spin_unlock(&lslist_lock); - return ls; -} - -struct dlm_ls *dlm_find_lockspace_device(int minor) -{ - struct dlm_ls *ls; - - spin_lock(&lslist_lock); - list_for_each_entry(ls, &lslist, ls_list) { - if (ls->ls_device.minor == minor) { - ls->ls_count++; - goto out; - } - } - ls = NULL; - out: - spin_unlock(&lslist_lock); - return ls; -} - -void dlm_put_lockspace(struct dlm_ls *ls) -{ - spin_lock(&lslist_lock); - ls->ls_count--; - spin_unlock(&lslist_lock); -} - -static void remove_lockspace(struct dlm_ls *ls) -{ - for (;;) { - spin_lock(&lslist_lock); - if (ls->ls_count == 0) { - list_del(&ls->ls_list); - spin_unlock(&lslist_lock); - return; - } - spin_unlock(&lslist_lock); - ssleep(1); - } -} - -static int threads_start(void) -{ - int error; - - /* Thread which process lock requests for all lockspace's */ - error = dlm_astd_start(); - if (error) { - log_print("cannot start dlm_astd thread %d", error); - goto fail; - } - - error = dlm_scand_start(); - if (error) { - log_print("cannot start dlm_scand thread %d", error); - goto astd_fail; - } - - /* Thread for sending/receiving messages for all lockspace's */ - error = dlm_lowcomms_start(); - if (error) { - log_print("cannot start dlm lowcomms %d", error); - goto scand_fail; - } - - return 0; - - scand_fail: - dlm_scand_stop(); - astd_fail: - dlm_astd_stop(); - fail: - return error; -} - -static void threads_stop(void) -{ - dlm_scand_stop(); - dlm_lowcomms_stop(); - dlm_astd_stop(); -} - -static int new_lockspace(char *name, int namelen, void **lockspace, - uint32_t flags, int lvblen) -{ - struct dlm_ls *ls; - int i, size, error = -ENOMEM; - - if (namelen > DLM_LOCKSPACE_LEN) - return -EINVAL; - - if (!lvblen || (lvblen % 8)) - return -EINVAL; - - if (!try_module_get(THIS_MODULE)) - return -EINVAL; - - ls = dlm_find_lockspace_name(name, namelen); - if (ls) { - *lockspace = ls; - module_put(THIS_MODULE); - return -EEXIST; - } - - ls = kzalloc(sizeof(struct dlm_ls) + namelen, GFP_KERNEL); - if (!ls) - goto out; - memcpy(ls->ls_name, name, namelen); - ls->ls_namelen = namelen; - ls->ls_exflags = flags; - ls->ls_lvblen = lvblen; - ls->ls_count = 0; - ls->ls_flags = 0; - - size = dlm_config.rsbtbl_size; - ls->ls_rsbtbl_size = size; - - ls->ls_rsbtbl = kmalloc(sizeof(struct dlm_rsbtable) * size, GFP_KERNEL); - if (!ls->ls_rsbtbl) - goto out_lsfree; - for (i = 0; i < size; i++) { - INIT_LIST_HEAD(&ls->ls_rsbtbl[i].list); - INIT_LIST_HEAD(&ls->ls_rsbtbl[i].toss); - rwlock_init(&ls->ls_rsbtbl[i].lock); - } - - size = dlm_config.lkbtbl_size; - ls->ls_lkbtbl_size = size; - - ls->ls_lkbtbl = kmalloc(sizeof(struct dlm_lkbtable) * size, GFP_KERNEL); - if (!ls->ls_lkbtbl) - goto out_rsbfree; - for (i = 0; i < size; i++) { - INIT_LIST_HEAD(&ls->ls_lkbtbl[i].list); - rwlock_init(&ls->ls_lkbtbl[i].lock); - ls->ls_lkbtbl[i].counter = 1; - } - - size = dlm_config.dirtbl_size; - ls->ls_dirtbl_size = size; - - ls->ls_dirtbl = kmalloc(sizeof(struct dlm_dirtable) * size, GFP_KERNEL); - if (!ls->ls_dirtbl) - goto out_lkbfree; - for (i = 0; i < size; i++) { - INIT_LIST_HEAD(&ls->ls_dirtbl[i].list); - rwlock_init(&ls->ls_dirtbl[i].lock); - } - - INIT_LIST_HEAD(&ls->ls_waiters); - mutex_init(&ls->ls_waiters_mutex); - - INIT_LIST_HEAD(&ls->ls_nodes); - INIT_LIST_HEAD(&ls->ls_nodes_gone); - ls->ls_num_nodes = 0; - ls->ls_low_nodeid = 0; - ls->ls_total_weight = 0; - ls->ls_node_array = NULL; - - memset(&ls->ls_stub_rsb, 0, sizeof(struct dlm_rsb)); - ls->ls_stub_rsb.res_ls = ls; - - ls->ls_debug_rsb_dentry = NULL; - ls->ls_debug_waiters_dentry = NULL; - - init_waitqueue_head(&ls->ls_uevent_wait); - ls->ls_uevent_result = 0; - - ls->ls_recoverd_task = NULL; - mutex_init(&ls->ls_recoverd_active); - spin_lock_init(&ls->ls_recover_lock); - ls->ls_recover_status = 0; - ls->ls_recover_seq = 0; - ls->ls_recover_args = NULL; - init_rwsem(&ls->ls_in_recovery); - INIT_LIST_HEAD(&ls->ls_requestqueue); - mutex_init(&ls->ls_requestqueue_mutex); - mutex_init(&ls->ls_clear_proc_locks); - - ls->ls_recover_buf = kmalloc(dlm_config.buffer_size, GFP_KERNEL); - if (!ls->ls_recover_buf) - goto out_dirfree; - - INIT_LIST_HEAD(&ls->ls_recover_list); - spin_lock_init(&ls->ls_recover_list_lock); - ls->ls_recover_list_count = 0; - ls->ls_local_handle = ls; - init_waitqueue_head(&ls->ls_wait_general); - INIT_LIST_HEAD(&ls->ls_root_list); - init_rwsem(&ls->ls_root_sem); - - down_write(&ls->ls_in_recovery); - - spin_lock(&lslist_lock); - list_add(&ls->ls_list, &lslist); - spin_unlock(&lslist_lock); - - /* needs to find ls in lslist */ - error = dlm_recoverd_start(ls); - if (error) { - log_error(ls, "can't start dlm_recoverd %d", error); - goto out_rcomfree; - } - - dlm_create_debug_file(ls); - - error = kobject_setup(ls); - if (error) - goto out_del; - - error = kobject_register(&ls->ls_kobj); - if (error) - goto out_del; - - error = do_uevent(ls, 1); - if (error) - goto out_unreg; - - *lockspace = ls; - return 0; - - out_unreg: - kobject_unregister(&ls->ls_kobj); - out_del: - dlm_delete_debug_file(ls); - dlm_recoverd_stop(ls); - out_rcomfree: - spin_lock(&lslist_lock); - list_del(&ls->ls_list); - spin_unlock(&lslist_lock); - kfree(ls->ls_recover_buf); - out_dirfree: - kfree(ls->ls_dirtbl); - out_lkbfree: - kfree(ls->ls_lkbtbl); - out_rsbfree: - kfree(ls->ls_rsbtbl); - out_lsfree: - kfree(ls); - out: - module_put(THIS_MODULE); - return error; -} - -int dlm_new_lockspace(char *name, int namelen, void **lockspace, - uint32_t flags, int lvblen) -{ - int error = 0; - - mutex_lock(&ls_lock); - if (!ls_count) - error = threads_start(); - if (error) - goto out; - - error = new_lockspace(name, namelen, lockspace, flags, lvblen); - if (!error) - ls_count++; - out: - mutex_unlock(&ls_lock); - return error; -} - -/* Return 1 if the lockspace still has active remote locks, - * 2 if the lockspace still has active local locks. - */ -static int lockspace_busy(struct dlm_ls *ls) -{ - int i, lkb_found = 0; - struct dlm_lkb *lkb; - - /* NOTE: We check the lockidtbl here rather than the resource table. - This is because there may be LKBs queued as ASTs that have been - unlinked from their RSBs and are pending deletion once the AST has - been delivered */ - - for (i = 0; i < ls->ls_lkbtbl_size; i++) { - read_lock(&ls->ls_lkbtbl[i].lock); - if (!list_empty(&ls->ls_lkbtbl[i].list)) { - lkb_found = 1; - list_for_each_entry(lkb, &ls->ls_lkbtbl[i].list, - lkb_idtbl_list) { - if (!lkb->lkb_nodeid) { - read_unlock(&ls->ls_lkbtbl[i].lock); - return 2; - } - } - } - read_unlock(&ls->ls_lkbtbl[i].lock); - } - return lkb_found; -} - -static int release_lockspace(struct dlm_ls *ls, int force) -{ - struct dlm_lkb *lkb; - struct dlm_rsb *rsb; - struct list_head *head; - int i; - int busy = lockspace_busy(ls); - - if (busy > force) - return -EBUSY; - - if (force < 3) - do_uevent(ls, 0); - - dlm_recoverd_stop(ls); - - remove_lockspace(ls); - - dlm_delete_debug_file(ls); - - dlm_astd_suspend(); - - kfree(ls->ls_recover_buf); - - /* - * Free direntry structs. - */ - - dlm_dir_clear(ls); - kfree(ls->ls_dirtbl); - - /* - * Free all lkb's on lkbtbl[] lists. - */ - - for (i = 0; i < ls->ls_lkbtbl_size; i++) { - head = &ls->ls_lkbtbl[i].list; - while (!list_empty(head)) { - lkb = list_entry(head->next, struct dlm_lkb, - lkb_idtbl_list); - - list_del(&lkb->lkb_idtbl_list); - - dlm_del_ast(lkb); - - if (lkb->lkb_lvbptr && lkb->lkb_flags & DLM_IFL_MSTCPY) - free_lvb(lkb->lkb_lvbptr); - - free_lkb(lkb); - } - } - dlm_astd_resume(); - - kfree(ls->ls_lkbtbl); - - /* - * Free all rsb's on rsbtbl[] lists - */ - - for (i = 0; i < ls->ls_rsbtbl_size; i++) { - head = &ls->ls_rsbtbl[i].list; - while (!list_empty(head)) { - rsb = list_entry(head->next, struct dlm_rsb, - res_hashchain); - - list_del(&rsb->res_hashchain); - free_rsb(rsb); - } - - head = &ls->ls_rsbtbl[i].toss; - while (!list_empty(head)) { - rsb = list_entry(head->next, struct dlm_rsb, - res_hashchain); - list_del(&rsb->res_hashchain); - free_rsb(rsb); - } - } - - kfree(ls->ls_rsbtbl); - - /* - * Free structures on any other lists - */ - - kfree(ls->ls_recover_args); - dlm_clear_free_entries(ls); - dlm_clear_members(ls); - dlm_clear_members_gone(ls); - kfree(ls->ls_node_array); - kobject_unregister(&ls->ls_kobj); - kfree(ls); - - mutex_lock(&ls_lock); - ls_count--; - if (!ls_count) - threads_stop(); - mutex_unlock(&ls_lock); - - module_put(THIS_MODULE); - return 0; -} - -/* - * Called when a system has released all its locks and is not going to use the - * lockspace any longer. We free everything we're managing for this lockspace. - * Remaining nodes will go through the recovery process as if we'd died. The - * lockspace must continue to function as usual, participating in recoveries, - * until this returns. - * - * Force has 4 possible values: - * 0 - don't destroy locksapce if it has any LKBs - * 1 - destroy lockspace if it has remote LKBs but not if it has local LKBs - * 2 - destroy lockspace regardless of LKBs - * 3 - destroy lockspace as part of a forced shutdown - */ - -int dlm_release_lockspace(void *lockspace, int force) -{ - struct dlm_ls *ls; - - ls = dlm_find_lockspace_local(lockspace); - if (!ls) - return -EINVAL; - dlm_put_lockspace(ls); - return release_lockspace(ls, force); -} - diff --git a/trunk/fs/dlm/lockspace.h b/trunk/fs/dlm/lockspace.h deleted file mode 100644 index 891eabbdd021..000000000000 --- a/trunk/fs/dlm/lockspace.h +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __LOCKSPACE_DOT_H__ -#define __LOCKSPACE_DOT_H__ - -int dlm_lockspace_init(void); -void dlm_lockspace_exit(void); -struct dlm_ls *dlm_find_lockspace_global(uint32_t id); -struct dlm_ls *dlm_find_lockspace_local(void *id); -struct dlm_ls *dlm_find_lockspace_device(int minor); -void dlm_put_lockspace(struct dlm_ls *ls); - -#endif /* __LOCKSPACE_DOT_H__ */ - diff --git a/trunk/fs/dlm/lowcomms.c b/trunk/fs/dlm/lowcomms.c deleted file mode 100644 index 23f5ce12080b..000000000000 --- a/trunk/fs/dlm/lowcomms.c +++ /dev/null @@ -1,1238 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -/* - * lowcomms.c - * - * This is the "low-level" comms layer. - * - * It is responsible for sending/receiving messages - * from other nodes in the cluster. - * - * Cluster nodes are referred to by their nodeids. nodeids are - * simply 32 bit numbers to the locking module - if they need to - * be expanded for the cluster infrastructure then that is it's - * responsibility. It is this layer's - * responsibility to resolve these into IP address or - * whatever it needs for inter-node communication. - * - * The comms level is two kernel threads that deal mainly with - * the receiving of messages from other nodes and passing them - * up to the mid-level comms layer (which understands the - * message format) for execution by the locking core, and - * a send thread which does all the setting up of connections - * to remote nodes and the sending of data. Threads are not allowed - * to send their own data because it may cause them to wait in times - * of high load. Also, this way, the sending thread can collect together - * messages bound for one node and send them in one block. - * - * I don't see any problem with the recv thread executing the locking - * code on behalf of remote processes as the locking code is - * short, efficient and never (well, hardly ever) waits. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "dlm_internal.h" -#include "lowcomms.h" -#include "config.h" -#include "midcomms.h" - -static struct sockaddr_storage *dlm_local_addr[DLM_MAX_ADDR_COUNT]; -static int dlm_local_count; -static int dlm_local_nodeid; - -/* One of these per connected node */ - -#define NI_INIT_PENDING 1 -#define NI_WRITE_PENDING 2 - -struct nodeinfo { - spinlock_t lock; - sctp_assoc_t assoc_id; - unsigned long flags; - struct list_head write_list; /* nodes with pending writes */ - struct list_head writequeue; /* outgoing writequeue_entries */ - spinlock_t writequeue_lock; - int nodeid; -}; - -static DEFINE_IDR(nodeinfo_idr); -static struct rw_semaphore nodeinfo_lock; -static int max_nodeid; - -struct cbuf { - unsigned base; - unsigned len; - unsigned mask; -}; - -/* Just the one of these, now. But this struct keeps - the connection-specific variables together */ - -#define CF_READ_PENDING 1 - -struct connection { - struct socket *sock; - unsigned long flags; - struct page *rx_page; - atomic_t waiting_requests; - struct cbuf cb; - int eagain_flag; -}; - -/* An entry waiting to be sent */ - -struct writequeue_entry { - struct list_head list; - struct page *page; - int offset; - int len; - int end; - int users; - struct nodeinfo *ni; -}; - -#define CBUF_ADD(cb, n) do { (cb)->len += n; } while(0) -#define CBUF_EMPTY(cb) ((cb)->len == 0) -#define CBUF_MAY_ADD(cb, n) (((cb)->len + (n)) < ((cb)->mask + 1)) -#define CBUF_DATA(cb) (((cb)->base + (cb)->len) & (cb)->mask) - -#define CBUF_INIT(cb, size) \ -do { \ - (cb)->base = (cb)->len = 0; \ - (cb)->mask = ((size)-1); \ -} while(0) - -#define CBUF_EAT(cb, n) \ -do { \ - (cb)->len -= (n); \ - (cb)->base += (n); \ - (cb)->base &= (cb)->mask; \ -} while(0) - - -/* List of nodes which have writes pending */ -static struct list_head write_nodes; -static spinlock_t write_nodes_lock; - -/* Maximum number of incoming messages to process before - * doing a schedule() - */ -#define MAX_RX_MSG_COUNT 25 - -/* Manage daemons */ -static struct task_struct *recv_task; -static struct task_struct *send_task; -static wait_queue_head_t lowcomms_recv_wait; -static atomic_t accepting; - -/* The SCTP connection */ -static struct connection sctp_con; - - -static int nodeid_to_addr(int nodeid, struct sockaddr *retaddr) -{ - struct sockaddr_storage addr; - int error; - - if (!dlm_local_count) - return -1; - - error = dlm_nodeid_to_addr(nodeid, &addr); - if (error) - return error; - - if (dlm_local_addr[0]->ss_family == AF_INET) { - struct sockaddr_in *in4 = (struct sockaddr_in *) &addr; - struct sockaddr_in *ret4 = (struct sockaddr_in *) retaddr; - ret4->sin_addr.s_addr = in4->sin_addr.s_addr; - } else { - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &addr; - struct sockaddr_in6 *ret6 = (struct sockaddr_in6 *) retaddr; - memcpy(&ret6->sin6_addr, &in6->sin6_addr, - sizeof(in6->sin6_addr)); - } - - return 0; -} - -static struct nodeinfo *nodeid2nodeinfo(int nodeid, int alloc) -{ - struct nodeinfo *ni; - int r; - int n; - - down_read(&nodeinfo_lock); - ni = idr_find(&nodeinfo_idr, nodeid); - up_read(&nodeinfo_lock); - - if (!ni && alloc) { - down_write(&nodeinfo_lock); - - ni = idr_find(&nodeinfo_idr, nodeid); - if (ni) - goto out_up; - - r = idr_pre_get(&nodeinfo_idr, alloc); - if (!r) - goto out_up; - - ni = kmalloc(sizeof(struct nodeinfo), alloc); - if (!ni) - goto out_up; - - r = idr_get_new_above(&nodeinfo_idr, ni, nodeid, &n); - if (r) { - kfree(ni); - ni = NULL; - goto out_up; - } - if (n != nodeid) { - idr_remove(&nodeinfo_idr, n); - kfree(ni); - ni = NULL; - goto out_up; - } - memset(ni, 0, sizeof(struct nodeinfo)); - spin_lock_init(&ni->lock); - INIT_LIST_HEAD(&ni->writequeue); - spin_lock_init(&ni->writequeue_lock); - ni->nodeid = nodeid; - - if (nodeid > max_nodeid) - max_nodeid = nodeid; - out_up: - up_write(&nodeinfo_lock); - } - - return ni; -} - -/* Don't call this too often... */ -static struct nodeinfo *assoc2nodeinfo(sctp_assoc_t assoc) -{ - int i; - struct nodeinfo *ni; - - for (i=1; i<=max_nodeid; i++) { - ni = nodeid2nodeinfo(i, 0); - if (ni && ni->assoc_id == assoc) - return ni; - } - return NULL; -} - -/* Data or notification available on socket */ -static void lowcomms_data_ready(struct sock *sk, int count_unused) -{ - atomic_inc(&sctp_con.waiting_requests); - if (test_and_set_bit(CF_READ_PENDING, &sctp_con.flags)) - return; - - wake_up_interruptible(&lowcomms_recv_wait); -} - - -/* Add the port number to an IP6 or 4 sockaddr and return the address length. - Also padd out the struct with zeros to make comparisons meaningful */ - -static void make_sockaddr(struct sockaddr_storage *saddr, uint16_t port, - int *addr_len) -{ - struct sockaddr_in *local4_addr; - struct sockaddr_in6 *local6_addr; - - if (!dlm_local_count) - return; - - if (!port) { - if (dlm_local_addr[0]->ss_family == AF_INET) { - local4_addr = (struct sockaddr_in *)dlm_local_addr[0]; - port = be16_to_cpu(local4_addr->sin_port); - } else { - local6_addr = (struct sockaddr_in6 *)dlm_local_addr[0]; - port = be16_to_cpu(local6_addr->sin6_port); - } - } - - saddr->ss_family = dlm_local_addr[0]->ss_family; - if (dlm_local_addr[0]->ss_family == AF_INET) { - struct sockaddr_in *in4_addr = (struct sockaddr_in *)saddr; - in4_addr->sin_port = cpu_to_be16(port); - memset(&in4_addr->sin_zero, 0, sizeof(in4_addr->sin_zero)); - memset(in4_addr+1, 0, sizeof(struct sockaddr_storage) - - sizeof(struct sockaddr_in)); - *addr_len = sizeof(struct sockaddr_in); - } else { - struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)saddr; - in6_addr->sin6_port = cpu_to_be16(port); - memset(in6_addr+1, 0, sizeof(struct sockaddr_storage) - - sizeof(struct sockaddr_in6)); - *addr_len = sizeof(struct sockaddr_in6); - } -} - -/* Close the connection and tidy up */ -static void close_connection(void) -{ - if (sctp_con.sock) { - sock_release(sctp_con.sock); - sctp_con.sock = NULL; - } - - if (sctp_con.rx_page) { - __free_page(sctp_con.rx_page); - sctp_con.rx_page = NULL; - } -} - -/* We only send shutdown messages to nodes that are not part of the cluster */ -static void send_shutdown(sctp_assoc_t associd) -{ - static char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; - struct msghdr outmessage; - struct cmsghdr *cmsg; - struct sctp_sndrcvinfo *sinfo; - int ret; - - outmessage.msg_name = NULL; - outmessage.msg_namelen = 0; - outmessage.msg_control = outcmsg; - outmessage.msg_controllen = sizeof(outcmsg); - outmessage.msg_flags = MSG_EOR; - - cmsg = CMSG_FIRSTHDR(&outmessage); - cmsg->cmsg_level = IPPROTO_SCTP; - cmsg->cmsg_type = SCTP_SNDRCV; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); - outmessage.msg_controllen = cmsg->cmsg_len; - sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); - memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo)); - - sinfo->sinfo_flags |= MSG_EOF; - sinfo->sinfo_assoc_id = associd; - - ret = kernel_sendmsg(sctp_con.sock, &outmessage, NULL, 0, 0); - - if (ret != 0) - log_print("send EOF to node failed: %d", ret); -} - - -/* INIT failed but we don't know which node... - restart INIT on all pending nodes */ -static void init_failed(void) -{ - int i; - struct nodeinfo *ni; - - for (i=1; i<=max_nodeid; i++) { - ni = nodeid2nodeinfo(i, 0); - if (!ni) - continue; - - if (test_and_clear_bit(NI_INIT_PENDING, &ni->flags)) { - ni->assoc_id = 0; - if (!test_and_set_bit(NI_WRITE_PENDING, &ni->flags)) { - spin_lock_bh(&write_nodes_lock); - list_add_tail(&ni->write_list, &write_nodes); - spin_unlock_bh(&write_nodes_lock); - } - } - } - wake_up_process(send_task); -} - -/* Something happened to an association */ -static void process_sctp_notification(struct msghdr *msg, char *buf) -{ - union sctp_notification *sn = (union sctp_notification *)buf; - - if (sn->sn_header.sn_type == SCTP_ASSOC_CHANGE) { - switch (sn->sn_assoc_change.sac_state) { - - case SCTP_COMM_UP: - case SCTP_RESTART: - { - /* Check that the new node is in the lockspace */ - struct sctp_prim prim; - mm_segment_t fs; - int nodeid; - int prim_len, ret; - int addr_len; - struct nodeinfo *ni; - - /* This seems to happen when we received a connection - * too early... or something... anyway, it happens but - * we always seem to get a real message too, see - * receive_from_sock */ - - if ((int)sn->sn_assoc_change.sac_assoc_id <= 0) { - log_print("COMM_UP for invalid assoc ID %d", - (int)sn->sn_assoc_change.sac_assoc_id); - init_failed(); - return; - } - memset(&prim, 0, sizeof(struct sctp_prim)); - prim_len = sizeof(struct sctp_prim); - prim.ssp_assoc_id = sn->sn_assoc_change.sac_assoc_id; - - fs = get_fs(); - set_fs(get_ds()); - ret = sctp_con.sock->ops->getsockopt(sctp_con.sock, - IPPROTO_SCTP, SCTP_PRIMARY_ADDR, - (char*)&prim, &prim_len); - set_fs(fs); - if (ret < 0) { - struct nodeinfo *ni; - - log_print("getsockopt/sctp_primary_addr on " - "new assoc %d failed : %d", - (int)sn->sn_assoc_change.sac_assoc_id, ret); - - /* Retry INIT later */ - ni = assoc2nodeinfo(sn->sn_assoc_change.sac_assoc_id); - if (ni) - clear_bit(NI_INIT_PENDING, &ni->flags); - return; - } - make_sockaddr(&prim.ssp_addr, 0, &addr_len); - if (dlm_addr_to_nodeid(&prim.ssp_addr, &nodeid)) { - log_print("reject connect from unknown addr"); - send_shutdown(prim.ssp_assoc_id); - return; - } - - ni = nodeid2nodeinfo(nodeid, GFP_KERNEL); - if (!ni) - return; - - /* Save the assoc ID */ - spin_lock(&ni->lock); - ni->assoc_id = sn->sn_assoc_change.sac_assoc_id; - spin_unlock(&ni->lock); - - log_print("got new/restarted association %d nodeid %d", - (int)sn->sn_assoc_change.sac_assoc_id, nodeid); - - /* Send any pending writes */ - clear_bit(NI_INIT_PENDING, &ni->flags); - if (!test_and_set_bit(NI_WRITE_PENDING, &ni->flags)) { - spin_lock_bh(&write_nodes_lock); - list_add_tail(&ni->write_list, &write_nodes); - spin_unlock_bh(&write_nodes_lock); - } - wake_up_process(send_task); - } - break; - - case SCTP_COMM_LOST: - case SCTP_SHUTDOWN_COMP: - { - struct nodeinfo *ni; - - ni = assoc2nodeinfo(sn->sn_assoc_change.sac_assoc_id); - if (ni) { - spin_lock(&ni->lock); - ni->assoc_id = 0; - spin_unlock(&ni->lock); - } - } - break; - - /* We don't know which INIT failed, so clear the PENDING flags - * on them all. if assoc_id is zero then it will then try - * again */ - - case SCTP_CANT_STR_ASSOC: - { - log_print("Can't start SCTP association - retrying"); - init_failed(); - } - break; - - default: - log_print("unexpected SCTP assoc change id=%d state=%d", - (int)sn->sn_assoc_change.sac_assoc_id, - sn->sn_assoc_change.sac_state); - } - } -} - -/* Data received from remote end */ -static int receive_from_sock(void) -{ - int ret = 0; - struct msghdr msg; - struct kvec iov[2]; - unsigned len; - int r; - struct sctp_sndrcvinfo *sinfo; - struct cmsghdr *cmsg; - struct nodeinfo *ni; - - /* These two are marginally too big for stack allocation, but this - * function is (currently) only called by dlm_recvd so static should be - * OK. - */ - static struct sockaddr_storage msgname; - static char incmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; - - if (sctp_con.sock == NULL) - goto out; - - if (sctp_con.rx_page == NULL) { - /* - * This doesn't need to be atomic, but I think it should - * improve performance if it is. - */ - sctp_con.rx_page = alloc_page(GFP_ATOMIC); - if (sctp_con.rx_page == NULL) - goto out_resched; - CBUF_INIT(&sctp_con.cb, PAGE_CACHE_SIZE); - } - - memset(&incmsg, 0, sizeof(incmsg)); - memset(&msgname, 0, sizeof(msgname)); - - memset(incmsg, 0, sizeof(incmsg)); - msg.msg_name = &msgname; - msg.msg_namelen = sizeof(msgname); - msg.msg_flags = 0; - msg.msg_control = incmsg; - msg.msg_controllen = sizeof(incmsg); - - /* I don't see why this circular buffer stuff is necessary for SCTP - * which is a packet-based protocol, but the whole thing breaks under - * load without it! The overhead is minimal (and is in the TCP lowcomms - * anyway, of course) so I'll leave it in until I can figure out what's - * really happening. - */ - - /* - * iov[0] is the bit of the circular buffer between the current end - * point (cb.base + cb.len) and the end of the buffer. - */ - iov[0].iov_len = sctp_con.cb.base - CBUF_DATA(&sctp_con.cb); - iov[0].iov_base = page_address(sctp_con.rx_page) + - CBUF_DATA(&sctp_con.cb); - iov[1].iov_len = 0; - - /* - * iov[1] is the bit of the circular buffer between the start of the - * buffer and the start of the currently used section (cb.base) - */ - if (CBUF_DATA(&sctp_con.cb) >= sctp_con.cb.base) { - iov[0].iov_len = PAGE_CACHE_SIZE - CBUF_DATA(&sctp_con.cb); - iov[1].iov_len = sctp_con.cb.base; - iov[1].iov_base = page_address(sctp_con.rx_page); - msg.msg_iovlen = 2; - } - len = iov[0].iov_len + iov[1].iov_len; - - r = ret = kernel_recvmsg(sctp_con.sock, &msg, iov, 1, len, - MSG_NOSIGNAL | MSG_DONTWAIT); - if (ret <= 0) - goto out_close; - - msg.msg_control = incmsg; - msg.msg_controllen = sizeof(incmsg); - cmsg = CMSG_FIRSTHDR(&msg); - sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); - - if (msg.msg_flags & MSG_NOTIFICATION) { - process_sctp_notification(&msg, page_address(sctp_con.rx_page)); - return 0; - } - - /* Is this a new association ? */ - ni = nodeid2nodeinfo(le32_to_cpu(sinfo->sinfo_ppid), GFP_KERNEL); - if (ni) { - ni->assoc_id = sinfo->sinfo_assoc_id; - if (test_and_clear_bit(NI_INIT_PENDING, &ni->flags)) { - - if (!test_and_set_bit(NI_WRITE_PENDING, &ni->flags)) { - spin_lock_bh(&write_nodes_lock); - list_add_tail(&ni->write_list, &write_nodes); - spin_unlock_bh(&write_nodes_lock); - } - wake_up_process(send_task); - } - } - - /* INIT sends a message with length of 1 - ignore it */ - if (r == 1) - return 0; - - CBUF_ADD(&sctp_con.cb, ret); - ret = dlm_process_incoming_buffer(cpu_to_le32(sinfo->sinfo_ppid), - page_address(sctp_con.rx_page), - sctp_con.cb.base, sctp_con.cb.len, - PAGE_CACHE_SIZE); - if (ret < 0) - goto out_close; - CBUF_EAT(&sctp_con.cb, ret); - - out: - ret = 0; - goto out_ret; - - out_resched: - lowcomms_data_ready(sctp_con.sock->sk, 0); - ret = 0; - schedule(); - goto out_ret; - - out_close: - if (ret != -EAGAIN) - log_print("error reading from sctp socket: %d", ret); - out_ret: - return ret; -} - -/* Bind to an IP address. SCTP allows multiple address so it can do multi-homing */ -static int add_bind_addr(struct sockaddr_storage *addr, int addr_len, int num) -{ - mm_segment_t fs; - int result = 0; - - fs = get_fs(); - set_fs(get_ds()); - if (num == 1) - result = sctp_con.sock->ops->bind(sctp_con.sock, - (struct sockaddr *) addr, addr_len); - else - result = sctp_con.sock->ops->setsockopt(sctp_con.sock, SOL_SCTP, - SCTP_SOCKOPT_BINDX_ADD, (char *)addr, addr_len); - set_fs(fs); - - if (result < 0) - log_print("Can't bind to port %d addr number %d", - dlm_config.tcp_port, num); - - return result; -} - -static void init_local(void) -{ - struct sockaddr_storage sas, *addr; - int i; - - dlm_local_nodeid = dlm_our_nodeid(); - - for (i = 0; i < DLM_MAX_ADDR_COUNT - 1; i++) { - if (dlm_our_addr(&sas, i)) - break; - - addr = kmalloc(sizeof(*addr), GFP_KERNEL); - if (!addr) - break; - memcpy(addr, &sas, sizeof(*addr)); - dlm_local_addr[dlm_local_count++] = addr; - } -} - -/* Initialise SCTP socket and bind to all interfaces */ -static int init_sock(void) -{ - mm_segment_t fs; - struct socket *sock = NULL; - struct sockaddr_storage localaddr; - struct sctp_event_subscribe subscribe; - int result = -EINVAL, num = 1, i, addr_len; - - if (!dlm_local_count) { - init_local(); - if (!dlm_local_count) { - log_print("no local IP address has been set"); - goto out; - } - } - - result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_SEQPACKET, - IPPROTO_SCTP, &sock); - if (result < 0) { - log_print("Can't create comms socket, check SCTP is loaded"); - goto out; - } - - /* Listen for events */ - memset(&subscribe, 0, sizeof(subscribe)); - subscribe.sctp_data_io_event = 1; - subscribe.sctp_association_event = 1; - subscribe.sctp_send_failure_event = 1; - subscribe.sctp_shutdown_event = 1; - subscribe.sctp_partial_delivery_event = 1; - - fs = get_fs(); - set_fs(get_ds()); - result = sock->ops->setsockopt(sock, SOL_SCTP, SCTP_EVENTS, - (char *)&subscribe, sizeof(subscribe)); - set_fs(fs); - - if (result < 0) { - log_print("Failed to set SCTP_EVENTS on socket: result=%d", - result); - goto create_delsock; - } - - /* Init con struct */ - sock->sk->sk_user_data = &sctp_con; - sctp_con.sock = sock; - sctp_con.sock->sk->sk_data_ready = lowcomms_data_ready; - - /* Bind to all interfaces. */ - for (i = 0; i < dlm_local_count; i++) { - memcpy(&localaddr, dlm_local_addr[i], sizeof(localaddr)); - make_sockaddr(&localaddr, dlm_config.tcp_port, &addr_len); - - result = add_bind_addr(&localaddr, addr_len, num); - if (result) - goto create_delsock; - ++num; - } - - result = sock->ops->listen(sock, 5); - if (result < 0) { - log_print("Can't set socket listening"); - goto create_delsock; - } - - return 0; - - create_delsock: - sock_release(sock); - sctp_con.sock = NULL; - out: - return result; -} - - -static struct writequeue_entry *new_writequeue_entry(int allocation) -{ - struct writequeue_entry *entry; - - entry = kmalloc(sizeof(struct writequeue_entry), allocation); - if (!entry) - return NULL; - - entry->page = alloc_page(allocation); - if (!entry->page) { - kfree(entry); - return NULL; - } - - entry->offset = 0; - entry->len = 0; - entry->end = 0; - entry->users = 0; - - return entry; -} - -void *dlm_lowcomms_get_buffer(int nodeid, int len, int allocation, char **ppc) -{ - struct writequeue_entry *e; - int offset = 0; - int users = 0; - struct nodeinfo *ni; - - if (!atomic_read(&accepting)) - return NULL; - - ni = nodeid2nodeinfo(nodeid, allocation); - if (!ni) - return NULL; - - spin_lock(&ni->writequeue_lock); - e = list_entry(ni->writequeue.prev, struct writequeue_entry, list); - if (((struct list_head *) e == &ni->writequeue) || - (PAGE_CACHE_SIZE - e->end < len)) { - e = NULL; - } else { - offset = e->end; - e->end += len; - users = e->users++; - } - spin_unlock(&ni->writequeue_lock); - - if (e) { - got_one: - if (users == 0) - kmap(e->page); - *ppc = page_address(e->page) + offset; - return e; - } - - e = new_writequeue_entry(allocation); - if (e) { - spin_lock(&ni->writequeue_lock); - offset = e->end; - e->end += len; - e->ni = ni; - users = e->users++; - list_add_tail(&e->list, &ni->writequeue); - spin_unlock(&ni->writequeue_lock); - goto got_one; - } - return NULL; -} - -void dlm_lowcomms_commit_buffer(void *arg) -{ - struct writequeue_entry *e = (struct writequeue_entry *) arg; - int users; - struct nodeinfo *ni = e->ni; - - if (!atomic_read(&accepting)) - return; - - spin_lock(&ni->writequeue_lock); - users = --e->users; - if (users) - goto out; - e->len = e->end - e->offset; - kunmap(e->page); - spin_unlock(&ni->writequeue_lock); - - if (!test_and_set_bit(NI_WRITE_PENDING, &ni->flags)) { - spin_lock_bh(&write_nodes_lock); - list_add_tail(&ni->write_list, &write_nodes); - spin_unlock_bh(&write_nodes_lock); - wake_up_process(send_task); - } - return; - - out: - spin_unlock(&ni->writequeue_lock); - return; -} - -static void free_entry(struct writequeue_entry *e) -{ - __free_page(e->page); - kfree(e); -} - -/* Initiate an SCTP association. In theory we could just use sendmsg() on - the first IP address and it should work, but this allows us to set up the - association before sending any valuable data that we can't afford to lose. - It also keeps the send path clean as it can now always use the association ID */ -static void initiate_association(int nodeid) -{ - struct sockaddr_storage rem_addr; - static char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; - struct msghdr outmessage; - struct cmsghdr *cmsg; - struct sctp_sndrcvinfo *sinfo; - int ret; - int addrlen; - char buf[1]; - struct kvec iov[1]; - struct nodeinfo *ni; - - log_print("Initiating association with node %d", nodeid); - - ni = nodeid2nodeinfo(nodeid, GFP_KERNEL); - if (!ni) - return; - - if (nodeid_to_addr(nodeid, (struct sockaddr *)&rem_addr)) { - log_print("no address for nodeid %d", nodeid); - return; - } - - make_sockaddr(&rem_addr, dlm_config.tcp_port, &addrlen); - - outmessage.msg_name = &rem_addr; - outmessage.msg_namelen = addrlen; - outmessage.msg_control = outcmsg; - outmessage.msg_controllen = sizeof(outcmsg); - outmessage.msg_flags = MSG_EOR; - - iov[0].iov_base = buf; - iov[0].iov_len = 1; - - /* Real INIT messages seem to cause trouble. Just send a 1 byte message - we can afford to lose */ - cmsg = CMSG_FIRSTHDR(&outmessage); - cmsg->cmsg_level = IPPROTO_SCTP; - cmsg->cmsg_type = SCTP_SNDRCV; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); - sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); - memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo)); - sinfo->sinfo_ppid = cpu_to_le32(dlm_local_nodeid); - - outmessage.msg_controllen = cmsg->cmsg_len; - ret = kernel_sendmsg(sctp_con.sock, &outmessage, iov, 1, 1); - if (ret < 0) { - log_print("send INIT to node failed: %d", ret); - /* Try again later */ - clear_bit(NI_INIT_PENDING, &ni->flags); - } -} - -/* Send a message */ -static int send_to_sock(struct nodeinfo *ni) -{ - int ret = 0; - struct writequeue_entry *e; - int len, offset; - struct msghdr outmsg; - static char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; - struct cmsghdr *cmsg; - struct sctp_sndrcvinfo *sinfo; - struct kvec iov; - - /* See if we need to init an association before we start - sending precious messages */ - spin_lock(&ni->lock); - if (!ni->assoc_id && !test_and_set_bit(NI_INIT_PENDING, &ni->flags)) { - spin_unlock(&ni->lock); - initiate_association(ni->nodeid); - return 0; - } - spin_unlock(&ni->lock); - - outmsg.msg_name = NULL; /* We use assoc_id */ - outmsg.msg_namelen = 0; - outmsg.msg_control = outcmsg; - outmsg.msg_controllen = sizeof(outcmsg); - outmsg.msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL | MSG_EOR; - - cmsg = CMSG_FIRSTHDR(&outmsg); - cmsg->cmsg_level = IPPROTO_SCTP; - cmsg->cmsg_type = SCTP_SNDRCV; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); - sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); - memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo)); - sinfo->sinfo_ppid = cpu_to_le32(dlm_local_nodeid); - sinfo->sinfo_assoc_id = ni->assoc_id; - outmsg.msg_controllen = cmsg->cmsg_len; - - spin_lock(&ni->writequeue_lock); - for (;;) { - if (list_empty(&ni->writequeue)) - break; - e = list_entry(ni->writequeue.next, struct writequeue_entry, - list); - len = e->len; - offset = e->offset; - BUG_ON(len == 0 && e->users == 0); - spin_unlock(&ni->writequeue_lock); - kmap(e->page); - - ret = 0; - if (len) { - iov.iov_base = page_address(e->page)+offset; - iov.iov_len = len; - - ret = kernel_sendmsg(sctp_con.sock, &outmsg, &iov, 1, - len); - if (ret == -EAGAIN) { - sctp_con.eagain_flag = 1; - goto out; - } else if (ret < 0) - goto send_error; - } else { - /* Don't starve people filling buffers */ - schedule(); - } - - spin_lock(&ni->writequeue_lock); - e->offset += ret; - e->len -= ret; - - if (e->len == 0 && e->users == 0) { - list_del(&e->list); - free_entry(e); - continue; - } - } - spin_unlock(&ni->writequeue_lock); - out: - return ret; - - send_error: - log_print("Error sending to node %d %d", ni->nodeid, ret); - spin_lock(&ni->lock); - if (!test_and_set_bit(NI_INIT_PENDING, &ni->flags)) { - ni->assoc_id = 0; - spin_unlock(&ni->lock); - initiate_association(ni->nodeid); - } else - spin_unlock(&ni->lock); - - return ret; -} - -/* Try to send any messages that are pending */ -static void process_output_queue(void) -{ - struct list_head *list; - struct list_head *temp; - - spin_lock_bh(&write_nodes_lock); - list_for_each_safe(list, temp, &write_nodes) { - struct nodeinfo *ni = - list_entry(list, struct nodeinfo, write_list); - clear_bit(NI_WRITE_PENDING, &ni->flags); - list_del(&ni->write_list); - - spin_unlock_bh(&write_nodes_lock); - - send_to_sock(ni); - spin_lock_bh(&write_nodes_lock); - } - spin_unlock_bh(&write_nodes_lock); -} - -/* Called after we've had -EAGAIN and been woken up */ -static void refill_write_queue(void) -{ - int i; - - for (i=1; i<=max_nodeid; i++) { - struct nodeinfo *ni = nodeid2nodeinfo(i, 0); - - if (ni) { - if (!test_and_set_bit(NI_WRITE_PENDING, &ni->flags)) { - spin_lock_bh(&write_nodes_lock); - list_add_tail(&ni->write_list, &write_nodes); - spin_unlock_bh(&write_nodes_lock); - } - } - } -} - -static void clean_one_writequeue(struct nodeinfo *ni) -{ - struct list_head *list; - struct list_head *temp; - - spin_lock(&ni->writequeue_lock); - list_for_each_safe(list, temp, &ni->writequeue) { - struct writequeue_entry *e = - list_entry(list, struct writequeue_entry, list); - list_del(&e->list); - free_entry(e); - } - spin_unlock(&ni->writequeue_lock); -} - -static void clean_writequeues(void) -{ - int i; - - for (i=1; i<=max_nodeid; i++) { - struct nodeinfo *ni = nodeid2nodeinfo(i, 0); - if (ni) - clean_one_writequeue(ni); - } -} - - -static void dealloc_nodeinfo(void) -{ - int i; - - for (i=1; i<=max_nodeid; i++) { - struct nodeinfo *ni = nodeid2nodeinfo(i, 0); - if (ni) { - idr_remove(&nodeinfo_idr, i); - kfree(ni); - } - } -} - -int dlm_lowcomms_close(int nodeid) -{ - struct nodeinfo *ni; - - ni = nodeid2nodeinfo(nodeid, 0); - if (!ni) - return -1; - - spin_lock(&ni->lock); - if (ni->assoc_id) { - ni->assoc_id = 0; - /* Don't send shutdown here, sctp will just queue it - till the node comes back up! */ - } - spin_unlock(&ni->lock); - - clean_one_writequeue(ni); - clear_bit(NI_INIT_PENDING, &ni->flags); - return 0; -} - -static int write_list_empty(void) -{ - int status; - - spin_lock_bh(&write_nodes_lock); - status = list_empty(&write_nodes); - spin_unlock_bh(&write_nodes_lock); - - return status; -} - -static int dlm_recvd(void *data) -{ - DECLARE_WAITQUEUE(wait, current); - - while (!kthread_should_stop()) { - int count = 0; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&lowcomms_recv_wait, &wait); - if (!test_bit(CF_READ_PENDING, &sctp_con.flags)) - schedule(); - remove_wait_queue(&lowcomms_recv_wait, &wait); - set_current_state(TASK_RUNNING); - - if (test_and_clear_bit(CF_READ_PENDING, &sctp_con.flags)) { - int ret; - - do { - ret = receive_from_sock(); - - /* Don't starve out everyone else */ - if (++count >= MAX_RX_MSG_COUNT) { - schedule(); - count = 0; - } - } while (!kthread_should_stop() && ret >=0); - } - schedule(); - } - - return 0; -} - -static int dlm_sendd(void *data) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(sctp_con.sock->sk->sk_sleep, &wait); - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - if (write_list_empty()) - schedule(); - set_current_state(TASK_RUNNING); - - if (sctp_con.eagain_flag) { - sctp_con.eagain_flag = 0; - refill_write_queue(); - } - process_output_queue(); - } - - remove_wait_queue(sctp_con.sock->sk->sk_sleep, &wait); - - return 0; -} - -static void daemons_stop(void) -{ - kthread_stop(recv_task); - kthread_stop(send_task); -} - -static int daemons_start(void) -{ - struct task_struct *p; - int error; - - p = kthread_run(dlm_recvd, NULL, "dlm_recvd"); - error = IS_ERR(p); - if (error) { - log_print("can't start dlm_recvd %d", error); - return error; - } - recv_task = p; - - p = kthread_run(dlm_sendd, NULL, "dlm_sendd"); - error = IS_ERR(p); - if (error) { - log_print("can't start dlm_sendd %d", error); - kthread_stop(recv_task); - return error; - } - send_task = p; - - return 0; -} - -/* - * This is quite likely to sleep... - */ -int dlm_lowcomms_start(void) -{ - int error; - - error = init_sock(); - if (error) - goto fail_sock; - error = daemons_start(); - if (error) - goto fail_sock; - atomic_set(&accepting, 1); - return 0; - - fail_sock: - close_connection(); - return error; -} - -/* Set all the activity flags to prevent any socket activity. */ - -void dlm_lowcomms_stop(void) -{ - atomic_set(&accepting, 0); - sctp_con.flags = 0x7; - daemons_stop(); - clean_writequeues(); - close_connection(); - dealloc_nodeinfo(); - max_nodeid = 0; -} - -int dlm_lowcomms_init(void) -{ - init_waitqueue_head(&lowcomms_recv_wait); - spin_lock_init(&write_nodes_lock); - INIT_LIST_HEAD(&write_nodes); - init_rwsem(&nodeinfo_lock); - return 0; -} - -void dlm_lowcomms_exit(void) -{ - int i; - - for (i = 0; i < dlm_local_count; i++) - kfree(dlm_local_addr[i]); - dlm_local_count = 0; - dlm_local_nodeid = 0; -} - diff --git a/trunk/fs/dlm/lowcomms.h b/trunk/fs/dlm/lowcomms.h deleted file mode 100644 index 6c04bb09cfa8..000000000000 --- a/trunk/fs/dlm/lowcomms.h +++ /dev/null @@ -1,26 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __LOWCOMMS_DOT_H__ -#define __LOWCOMMS_DOT_H__ - -int dlm_lowcomms_init(void); -void dlm_lowcomms_exit(void); -int dlm_lowcomms_start(void); -void dlm_lowcomms_stop(void); -int dlm_lowcomms_close(int nodeid); -void *dlm_lowcomms_get_buffer(int nodeid, int len, int allocation, char **ppc); -void dlm_lowcomms_commit_buffer(void *mh); - -#endif /* __LOWCOMMS_DOT_H__ */ - diff --git a/trunk/fs/dlm/lvb_table.h b/trunk/fs/dlm/lvb_table.h deleted file mode 100644 index cc3e92f3feef..000000000000 --- a/trunk/fs/dlm/lvb_table.h +++ /dev/null @@ -1,18 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __LVB_TABLE_DOT_H__ -#define __LVB_TABLE_DOT_H__ - -extern const int dlm_lvb_operations[8][8]; - -#endif diff --git a/trunk/fs/dlm/main.c b/trunk/fs/dlm/main.c deleted file mode 100644 index a8da8dc36b2e..000000000000 --- a/trunk/fs/dlm/main.c +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "lock.h" -#include "user.h" -#include "memory.h" -#include "lowcomms.h" -#include "config.h" - -#ifdef CONFIG_DLM_DEBUG -int dlm_register_debugfs(void); -void dlm_unregister_debugfs(void); -#else -static inline int dlm_register_debugfs(void) { return 0; } -static inline void dlm_unregister_debugfs(void) { } -#endif - -static int __init init_dlm(void) -{ - int error; - - error = dlm_memory_init(); - if (error) - goto out; - - error = dlm_lockspace_init(); - if (error) - goto out_mem; - - error = dlm_config_init(); - if (error) - goto out_lockspace; - - error = dlm_register_debugfs(); - if (error) - goto out_config; - - error = dlm_lowcomms_init(); - if (error) - goto out_debug; - - error = dlm_user_init(); - if (error) - goto out_lowcomms; - - printk("DLM (built %s %s) installed\n", __DATE__, __TIME__); - - return 0; - - out_lowcomms: - dlm_lowcomms_exit(); - out_debug: - dlm_unregister_debugfs(); - out_config: - dlm_config_exit(); - out_lockspace: - dlm_lockspace_exit(); - out_mem: - dlm_memory_exit(); - out: - return error; -} - -static void __exit exit_dlm(void) -{ - dlm_user_exit(); - dlm_lowcomms_exit(); - dlm_config_exit(); - dlm_memory_exit(); - dlm_lockspace_exit(); - dlm_unregister_debugfs(); -} - -module_init(init_dlm); -module_exit(exit_dlm); - -MODULE_DESCRIPTION("Distributed Lock Manager"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL_GPL(dlm_new_lockspace); -EXPORT_SYMBOL_GPL(dlm_release_lockspace); -EXPORT_SYMBOL_GPL(dlm_lock); -EXPORT_SYMBOL_GPL(dlm_unlock); - diff --git a/trunk/fs/dlm/member.c b/trunk/fs/dlm/member.c deleted file mode 100644 index a3f7de7f3a8f..000000000000 --- a/trunk/fs/dlm/member.c +++ /dev/null @@ -1,327 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "member.h" -#include "recoverd.h" -#include "recover.h" -#include "rcom.h" -#include "config.h" - -/* - * Following called by dlm_recoverd thread - */ - -static void add_ordered_member(struct dlm_ls *ls, struct dlm_member *new) -{ - struct dlm_member *memb = NULL; - struct list_head *tmp; - struct list_head *newlist = &new->list; - struct list_head *head = &ls->ls_nodes; - - list_for_each(tmp, head) { - memb = list_entry(tmp, struct dlm_member, list); - if (new->nodeid < memb->nodeid) - break; - } - - if (!memb) - list_add_tail(newlist, head); - else { - /* FIXME: can use list macro here */ - newlist->prev = tmp->prev; - newlist->next = tmp; - tmp->prev->next = newlist; - tmp->prev = newlist; - } -} - -static int dlm_add_member(struct dlm_ls *ls, int nodeid) -{ - struct dlm_member *memb; - int w; - - memb = kzalloc(sizeof(struct dlm_member), GFP_KERNEL); - if (!memb) - return -ENOMEM; - - w = dlm_node_weight(ls->ls_name, nodeid); - if (w < 0) - return w; - - memb->nodeid = nodeid; - memb->weight = w; - add_ordered_member(ls, memb); - ls->ls_num_nodes++; - return 0; -} - -static void dlm_remove_member(struct dlm_ls *ls, struct dlm_member *memb) -{ - list_move(&memb->list, &ls->ls_nodes_gone); - ls->ls_num_nodes--; -} - -static int dlm_is_member(struct dlm_ls *ls, int nodeid) -{ - struct dlm_member *memb; - - list_for_each_entry(memb, &ls->ls_nodes, list) { - if (memb->nodeid == nodeid) - return 1; - } - return 0; -} - -int dlm_is_removed(struct dlm_ls *ls, int nodeid) -{ - struct dlm_member *memb; - - list_for_each_entry(memb, &ls->ls_nodes_gone, list) { - if (memb->nodeid == nodeid) - return 1; - } - return 0; -} - -static void clear_memb_list(struct list_head *head) -{ - struct dlm_member *memb; - - while (!list_empty(head)) { - memb = list_entry(head->next, struct dlm_member, list); - list_del(&memb->list); - kfree(memb); - } -} - -void dlm_clear_members(struct dlm_ls *ls) -{ - clear_memb_list(&ls->ls_nodes); - ls->ls_num_nodes = 0; -} - -void dlm_clear_members_gone(struct dlm_ls *ls) -{ - clear_memb_list(&ls->ls_nodes_gone); -} - -static void make_member_array(struct dlm_ls *ls) -{ - struct dlm_member *memb; - int i, w, x = 0, total = 0, all_zero = 0, *array; - - kfree(ls->ls_node_array); - ls->ls_node_array = NULL; - - list_for_each_entry(memb, &ls->ls_nodes, list) { - if (memb->weight) - total += memb->weight; - } - - /* all nodes revert to weight of 1 if all have weight 0 */ - - if (!total) { - total = ls->ls_num_nodes; - all_zero = 1; - } - - ls->ls_total_weight = total; - - array = kmalloc(sizeof(int) * total, GFP_KERNEL); - if (!array) - return; - - list_for_each_entry(memb, &ls->ls_nodes, list) { - if (!all_zero && !memb->weight) - continue; - - if (all_zero) - w = 1; - else - w = memb->weight; - - DLM_ASSERT(x < total, printk("total %d x %d\n", total, x);); - - for (i = 0; i < w; i++) - array[x++] = memb->nodeid; - } - - ls->ls_node_array = array; -} - -/* send a status request to all members just to establish comms connections */ - -static int ping_members(struct dlm_ls *ls) -{ - struct dlm_member *memb; - int error = 0; - - list_for_each_entry(memb, &ls->ls_nodes, list) { - error = dlm_recovery_stopped(ls); - if (error) - break; - error = dlm_rcom_status(ls, memb->nodeid); - if (error) - break; - } - if (error) - log_debug(ls, "ping_members aborted %d last nodeid %d", - error, ls->ls_recover_nodeid); - return error; -} - -int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv, int *neg_out) -{ - struct dlm_member *memb, *safe; - int i, error, found, pos = 0, neg = 0, low = -1; - - /* move departed members from ls_nodes to ls_nodes_gone */ - - list_for_each_entry_safe(memb, safe, &ls->ls_nodes, list) { - found = 0; - for (i = 0; i < rv->node_count; i++) { - if (memb->nodeid == rv->nodeids[i]) { - found = 1; - break; - } - } - - if (!found) { - neg++; - dlm_remove_member(ls, memb); - log_debug(ls, "remove member %d", memb->nodeid); - } - } - - /* add new members to ls_nodes */ - - for (i = 0; i < rv->node_count; i++) { - if (dlm_is_member(ls, rv->nodeids[i])) - continue; - dlm_add_member(ls, rv->nodeids[i]); - pos++; - log_debug(ls, "add member %d", rv->nodeids[i]); - } - - list_for_each_entry(memb, &ls->ls_nodes, list) { - if (low == -1 || memb->nodeid < low) - low = memb->nodeid; - } - ls->ls_low_nodeid = low; - - make_member_array(ls); - dlm_set_recover_status(ls, DLM_RS_NODES); - *neg_out = neg; - - error = ping_members(ls); - if (error) - goto out; - - error = dlm_recover_members_wait(ls); - out: - log_debug(ls, "total members %d error %d", ls->ls_num_nodes, error); - return error; -} - -/* - * Following called from lockspace.c - */ - -int dlm_ls_stop(struct dlm_ls *ls) -{ - int new; - - /* - * A stop cancels any recovery that's in progress (see RECOVERY_STOP, - * dlm_recovery_stopped()) and prevents any new locks from being - * processed (see RUNNING, dlm_locking_stopped()). - */ - - spin_lock(&ls->ls_recover_lock); - set_bit(LSFL_RECOVERY_STOP, &ls->ls_flags); - new = test_and_clear_bit(LSFL_RUNNING, &ls->ls_flags); - ls->ls_recover_seq++; - spin_unlock(&ls->ls_recover_lock); - - /* - * This in_recovery lock does two things: - * - * 1) Keeps this function from returning until all threads are out - * of locking routines and locking is truely stopped. - * 2) Keeps any new requests from being processed until it's unlocked - * when recovery is complete. - */ - - if (new) - down_write(&ls->ls_in_recovery); - - /* - * The recoverd suspend/resume makes sure that dlm_recoverd (if - * running) has noticed the clearing of RUNNING above and quit - * processing the previous recovery. This will be true for all nodes - * before any nodes start the new recovery. - */ - - dlm_recoverd_suspend(ls); - ls->ls_recover_status = 0; - dlm_recoverd_resume(ls); - return 0; -} - -int dlm_ls_start(struct dlm_ls *ls) -{ - struct dlm_recover *rv = NULL, *rv_old; - int *ids = NULL; - int error, count; - - rv = kzalloc(sizeof(struct dlm_recover), GFP_KERNEL); - if (!rv) - return -ENOMEM; - - error = count = dlm_nodeid_list(ls->ls_name, &ids); - if (error <= 0) - goto fail; - - spin_lock(&ls->ls_recover_lock); - - /* the lockspace needs to be stopped before it can be started */ - - if (!dlm_locking_stopped(ls)) { - spin_unlock(&ls->ls_recover_lock); - log_error(ls, "start ignored: lockspace running"); - error = -EINVAL; - goto fail; - } - - rv->nodeids = ids; - rv->node_count = count; - rv->seq = ++ls->ls_recover_seq; - rv_old = ls->ls_recover_args; - ls->ls_recover_args = rv; - spin_unlock(&ls->ls_recover_lock); - - if (rv_old) { - kfree(rv_old->nodeids); - kfree(rv_old); - } - - dlm_recoverd_kick(ls); - return 0; - - fail: - kfree(rv); - kfree(ids); - return error; -} - diff --git a/trunk/fs/dlm/member.h b/trunk/fs/dlm/member.h deleted file mode 100644 index 927c08c19214..000000000000 --- a/trunk/fs/dlm/member.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __MEMBER_DOT_H__ -#define __MEMBER_DOT_H__ - -int dlm_ls_stop(struct dlm_ls *ls); -int dlm_ls_start(struct dlm_ls *ls); -void dlm_clear_members(struct dlm_ls *ls); -void dlm_clear_members_gone(struct dlm_ls *ls); -int dlm_recover_members(struct dlm_ls *ls, struct dlm_recover *rv,int *neg_out); -int dlm_is_removed(struct dlm_ls *ls, int nodeid); - -#endif /* __MEMBER_DOT_H__ */ - diff --git a/trunk/fs/dlm/memory.c b/trunk/fs/dlm/memory.c deleted file mode 100644 index 989b608fd836..000000000000 --- a/trunk/fs/dlm/memory.c +++ /dev/null @@ -1,116 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "config.h" -#include "memory.h" - -static kmem_cache_t *lkb_cache; - - -int dlm_memory_init(void) -{ - int ret = 0; - - lkb_cache = kmem_cache_create("dlm_lkb", sizeof(struct dlm_lkb), - __alignof__(struct dlm_lkb), 0, NULL, NULL); - if (!lkb_cache) - ret = -ENOMEM; - return ret; -} - -void dlm_memory_exit(void) -{ - if (lkb_cache) - kmem_cache_destroy(lkb_cache); -} - -char *allocate_lvb(struct dlm_ls *ls) -{ - char *p; - - p = kmalloc(ls->ls_lvblen, GFP_KERNEL); - if (p) - memset(p, 0, ls->ls_lvblen); - return p; -} - -void free_lvb(char *p) -{ - kfree(p); -} - -/* FIXME: have some minimal space built-in to rsb for the name and - kmalloc a separate name if needed, like dentries are done */ - -struct dlm_rsb *allocate_rsb(struct dlm_ls *ls, int namelen) -{ - struct dlm_rsb *r; - - DLM_ASSERT(namelen <= DLM_RESNAME_MAXLEN,); - - r = kmalloc(sizeof(*r) + namelen, GFP_KERNEL); - if (r) - memset(r, 0, sizeof(*r) + namelen); - return r; -} - -void free_rsb(struct dlm_rsb *r) -{ - if (r->res_lvbptr) - free_lvb(r->res_lvbptr); - kfree(r); -} - -struct dlm_lkb *allocate_lkb(struct dlm_ls *ls) -{ - struct dlm_lkb *lkb; - - lkb = kmem_cache_alloc(lkb_cache, GFP_KERNEL); - if (lkb) - memset(lkb, 0, sizeof(*lkb)); - return lkb; -} - -void free_lkb(struct dlm_lkb *lkb) -{ - if (lkb->lkb_flags & DLM_IFL_USER) { - struct dlm_user_args *ua; - ua = (struct dlm_user_args *)lkb->lkb_astparam; - if (ua) { - if (ua->lksb.sb_lvbptr) - kfree(ua->lksb.sb_lvbptr); - kfree(ua); - } - } - kmem_cache_free(lkb_cache, lkb); -} - -struct dlm_direntry *allocate_direntry(struct dlm_ls *ls, int namelen) -{ - struct dlm_direntry *de; - - DLM_ASSERT(namelen <= DLM_RESNAME_MAXLEN, - printk("namelen = %d\n", namelen);); - - de = kmalloc(sizeof(*de) + namelen, GFP_KERNEL); - if (de) - memset(de, 0, sizeof(*de) + namelen); - return de; -} - -void free_direntry(struct dlm_direntry *de) -{ - kfree(de); -} - diff --git a/trunk/fs/dlm/memory.h b/trunk/fs/dlm/memory.h deleted file mode 100644 index 6ead158ccc5c..000000000000 --- a/trunk/fs/dlm/memory.h +++ /dev/null @@ -1,29 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __MEMORY_DOT_H__ -#define __MEMORY_DOT_H__ - -int dlm_memory_init(void); -void dlm_memory_exit(void); -struct dlm_rsb *allocate_rsb(struct dlm_ls *ls, int namelen); -void free_rsb(struct dlm_rsb *r); -struct dlm_lkb *allocate_lkb(struct dlm_ls *ls); -void free_lkb(struct dlm_lkb *l); -struct dlm_direntry *allocate_direntry(struct dlm_ls *ls, int namelen); -void free_direntry(struct dlm_direntry *de); -char *allocate_lvb(struct dlm_ls *ls); -void free_lvb(char *l); - -#endif /* __MEMORY_DOT_H__ */ - diff --git a/trunk/fs/dlm/midcomms.c b/trunk/fs/dlm/midcomms.c deleted file mode 100644 index c9b1c3d535f4..000000000000 --- a/trunk/fs/dlm/midcomms.c +++ /dev/null @@ -1,140 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -/* - * midcomms.c - * - * This is the appallingly named "mid-level" comms layer. - * - * Its purpose is to take packets from the "real" comms layer, - * split them up into packets and pass them to the interested - * part of the locking mechanism. - * - * It also takes messages from the locking layer, formats them - * into packets and sends them to the comms layer. - */ - -#include "dlm_internal.h" -#include "lowcomms.h" -#include "config.h" -#include "rcom.h" -#include "lock.h" -#include "midcomms.h" - - -static void copy_from_cb(void *dst, const void *base, unsigned offset, - unsigned len, unsigned limit) -{ - unsigned copy = len; - - if ((copy + offset) > limit) - copy = limit - offset; - memcpy(dst, base + offset, copy); - len -= copy; - if (len) - memcpy(dst + copy, base, len); -} - -/* - * Called from the low-level comms layer to process a buffer of - * commands. - * - * Only complete messages are processed here, any "spare" bytes from - * the end of a buffer are saved and tacked onto the front of the next - * message that comes in. I doubt this will happen very often but we - * need to be able to cope with it and I don't want the task to be waiting - * for packets to come in when there is useful work to be done. - */ - -int dlm_process_incoming_buffer(int nodeid, const void *base, - unsigned offset, unsigned len, unsigned limit) -{ - unsigned char __tmp[DLM_INBUF_LEN]; - struct dlm_header *msg = (struct dlm_header *) __tmp; - int ret = 0; - int err = 0; - uint16_t msglen; - uint32_t lockspace; - - while (len > sizeof(struct dlm_header)) { - - /* Copy just the header to check the total length. The - message may wrap around the end of the buffer back to the - start, so we need to use a temp buffer and copy_from_cb. */ - - copy_from_cb(msg, base, offset, sizeof(struct dlm_header), - limit); - - msglen = le16_to_cpu(msg->h_length); - lockspace = msg->h_lockspace; - - err = -EINVAL; - if (msglen < sizeof(struct dlm_header)) - break; - err = -E2BIG; - if (msglen > dlm_config.buffer_size) { - log_print("message size %d from %d too big, buf len %d", - msglen, nodeid, len); - break; - } - err = 0; - - /* If only part of the full message is contained in this - buffer, then do nothing and wait for lowcomms to call - us again later with more data. We return 0 meaning - we've consumed none of the input buffer. */ - - if (msglen > len) - break; - - /* Allocate a larger temp buffer if the full message won't fit - in the buffer on the stack (which should work for most - ordinary messages). */ - - if (msglen > sizeof(__tmp) && - msg == (struct dlm_header *) __tmp) { - msg = kmalloc(dlm_config.buffer_size, GFP_KERNEL); - if (msg == NULL) - return ret; - } - - copy_from_cb(msg, base, offset, msglen, limit); - - BUG_ON(lockspace != msg->h_lockspace); - - ret += msglen; - offset += msglen; - offset &= (limit - 1); - len -= msglen; - - switch (msg->h_cmd) { - case DLM_MSG: - dlm_receive_message(msg, nodeid, 0); - break; - - case DLM_RCOM: - dlm_receive_rcom(msg, nodeid); - break; - - default: - log_print("unknown msg type %x from %u: %u %u %u %u", - msg->h_cmd, nodeid, msglen, len, offset, ret); - } - } - - if (msg != (struct dlm_header *) __tmp) - kfree(msg); - - return err ? err : ret; -} - diff --git a/trunk/fs/dlm/midcomms.h b/trunk/fs/dlm/midcomms.h deleted file mode 100644 index 95852a5f111d..000000000000 --- a/trunk/fs/dlm/midcomms.h +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __MIDCOMMS_DOT_H__ -#define __MIDCOMMS_DOT_H__ - -int dlm_process_incoming_buffer(int nodeid, const void *base, unsigned offset, - unsigned len, unsigned limit); - -#endif /* __MIDCOMMS_DOT_H__ */ - diff --git a/trunk/fs/dlm/rcom.c b/trunk/fs/dlm/rcom.c deleted file mode 100644 index 518239a8b1e9..000000000000 --- a/trunk/fs/dlm/rcom.c +++ /dev/null @@ -1,472 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "member.h" -#include "lowcomms.h" -#include "midcomms.h" -#include "rcom.h" -#include "recover.h" -#include "dir.h" -#include "config.h" -#include "memory.h" -#include "lock.h" -#include "util.h" - - -static int rcom_response(struct dlm_ls *ls) -{ - return test_bit(LSFL_RCOM_READY, &ls->ls_flags); -} - -static int create_rcom(struct dlm_ls *ls, int to_nodeid, int type, int len, - struct dlm_rcom **rc_ret, struct dlm_mhandle **mh_ret) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - char *mb; - int mb_len = sizeof(struct dlm_rcom) + len; - - mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, GFP_KERNEL, &mb); - if (!mh) { - log_print("create_rcom to %d type %d len %d ENOBUFS", - to_nodeid, type, len); - return -ENOBUFS; - } - memset(mb, 0, mb_len); - - rc = (struct dlm_rcom *) mb; - - rc->rc_header.h_version = (DLM_HEADER_MAJOR | DLM_HEADER_MINOR); - rc->rc_header.h_lockspace = ls->ls_global_id; - rc->rc_header.h_nodeid = dlm_our_nodeid(); - rc->rc_header.h_length = mb_len; - rc->rc_header.h_cmd = DLM_RCOM; - - rc->rc_type = type; - - *mh_ret = mh; - *rc_ret = rc; - return 0; -} - -static void send_rcom(struct dlm_ls *ls, struct dlm_mhandle *mh, - struct dlm_rcom *rc) -{ - dlm_rcom_out(rc); - dlm_lowcomms_commit_buffer(mh); -} - -/* When replying to a status request, a node also sends back its - configuration values. The requesting node then checks that the remote - node is configured the same way as itself. */ - -static void make_config(struct dlm_ls *ls, struct rcom_config *rf) -{ - rf->rf_lvblen = ls->ls_lvblen; - rf->rf_lsflags = ls->ls_exflags; -} - -static int check_config(struct dlm_ls *ls, struct rcom_config *rf, int nodeid) -{ - if (rf->rf_lvblen != ls->ls_lvblen || - rf->rf_lsflags != ls->ls_exflags) { - log_error(ls, "config mismatch: %d,%x nodeid %d: %d,%x", - ls->ls_lvblen, ls->ls_exflags, - nodeid, rf->rf_lvblen, rf->rf_lsflags); - return -EINVAL; - } - return 0; -} - -int dlm_rcom_status(struct dlm_ls *ls, int nodeid) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - int error = 0; - - memset(ls->ls_recover_buf, 0, dlm_config.buffer_size); - ls->ls_recover_nodeid = nodeid; - - if (nodeid == dlm_our_nodeid()) { - rc = (struct dlm_rcom *) ls->ls_recover_buf; - rc->rc_result = dlm_recover_status(ls); - goto out; - } - - error = create_rcom(ls, nodeid, DLM_RCOM_STATUS, 0, &rc, &mh); - if (error) - goto out; - rc->rc_id = ++ls->ls_rcom_seq; - - send_rcom(ls, mh, rc); - - error = dlm_wait_function(ls, &rcom_response); - clear_bit(LSFL_RCOM_READY, &ls->ls_flags); - if (error) - goto out; - - rc = (struct dlm_rcom *) ls->ls_recover_buf; - - if (rc->rc_result == -ESRCH) { - /* we pretend the remote lockspace exists with 0 status */ - log_debug(ls, "remote node %d not ready", nodeid); - rc->rc_result = 0; - } else - error = check_config(ls, (struct rcom_config *) rc->rc_buf, - nodeid); - /* the caller looks at rc_result for the remote recovery status */ - out: - return error; -} - -static void receive_rcom_status(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - int error, nodeid = rc_in->rc_header.h_nodeid; - - error = create_rcom(ls, nodeid, DLM_RCOM_STATUS_REPLY, - sizeof(struct rcom_config), &rc, &mh); - if (error) - return; - rc->rc_id = rc_in->rc_id; - rc->rc_result = dlm_recover_status(ls); - make_config(ls, (struct rcom_config *) rc->rc_buf); - - send_rcom(ls, mh, rc); -} - -static void receive_sync_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - if (rc_in->rc_id != ls->ls_rcom_seq) { - log_debug(ls, "reject old reply %d got %llx wanted %llx", - rc_in->rc_type, rc_in->rc_id, ls->ls_rcom_seq); - return; - } - memcpy(ls->ls_recover_buf, rc_in, rc_in->rc_header.h_length); - set_bit(LSFL_RCOM_READY, &ls->ls_flags); - wake_up(&ls->ls_wait_general); -} - -static void receive_rcom_status_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - receive_sync_reply(ls, rc_in); -} - -int dlm_rcom_names(struct dlm_ls *ls, int nodeid, char *last_name, int last_len) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - int error = 0, len = sizeof(struct dlm_rcom); - - memset(ls->ls_recover_buf, 0, dlm_config.buffer_size); - ls->ls_recover_nodeid = nodeid; - - if (nodeid == dlm_our_nodeid()) { - dlm_copy_master_names(ls, last_name, last_len, - ls->ls_recover_buf + len, - dlm_config.buffer_size - len, nodeid); - goto out; - } - - error = create_rcom(ls, nodeid, DLM_RCOM_NAMES, last_len, &rc, &mh); - if (error) - goto out; - memcpy(rc->rc_buf, last_name, last_len); - rc->rc_id = ++ls->ls_rcom_seq; - - send_rcom(ls, mh, rc); - - error = dlm_wait_function(ls, &rcom_response); - clear_bit(LSFL_RCOM_READY, &ls->ls_flags); - out: - return error; -} - -static void receive_rcom_names(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - int error, inlen, outlen; - int nodeid = rc_in->rc_header.h_nodeid; - uint32_t status = dlm_recover_status(ls); - - /* - * We can't run dlm_dir_rebuild_send (which uses ls_nodes) while - * dlm_recoverd is running ls_nodes_reconfig (which changes ls_nodes). - * It could only happen in rare cases where we get a late NAMES - * message from a previous instance of recovery. - */ - - if (!(status & DLM_RS_NODES)) { - log_debug(ls, "ignoring RCOM_NAMES from %u", nodeid); - return; - } - - nodeid = rc_in->rc_header.h_nodeid; - inlen = rc_in->rc_header.h_length - sizeof(struct dlm_rcom); - outlen = dlm_config.buffer_size - sizeof(struct dlm_rcom); - - error = create_rcom(ls, nodeid, DLM_RCOM_NAMES_REPLY, outlen, &rc, &mh); - if (error) - return; - rc->rc_id = rc_in->rc_id; - - dlm_copy_master_names(ls, rc_in->rc_buf, inlen, rc->rc_buf, outlen, - nodeid); - send_rcom(ls, mh, rc); -} - -static void receive_rcom_names_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - receive_sync_reply(ls, rc_in); -} - -int dlm_send_rcom_lookup(struct dlm_rsb *r, int dir_nodeid) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - struct dlm_ls *ls = r->res_ls; - int error; - - error = create_rcom(ls, dir_nodeid, DLM_RCOM_LOOKUP, r->res_length, - &rc, &mh); - if (error) - goto out; - memcpy(rc->rc_buf, r->res_name, r->res_length); - rc->rc_id = (unsigned long) r; - - send_rcom(ls, mh, rc); - out: - return error; -} - -static void receive_rcom_lookup(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - int error, ret_nodeid, nodeid = rc_in->rc_header.h_nodeid; - int len = rc_in->rc_header.h_length - sizeof(struct dlm_rcom); - - error = create_rcom(ls, nodeid, DLM_RCOM_LOOKUP_REPLY, 0, &rc, &mh); - if (error) - return; - - error = dlm_dir_lookup(ls, nodeid, rc_in->rc_buf, len, &ret_nodeid); - if (error) - ret_nodeid = error; - rc->rc_result = ret_nodeid; - rc->rc_id = rc_in->rc_id; - - send_rcom(ls, mh, rc); -} - -static void receive_rcom_lookup_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - dlm_recover_master_reply(ls, rc_in); -} - -static void pack_rcom_lock(struct dlm_rsb *r, struct dlm_lkb *lkb, - struct rcom_lock *rl) -{ - memset(rl, 0, sizeof(*rl)); - - rl->rl_ownpid = lkb->lkb_ownpid; - rl->rl_lkid = lkb->lkb_id; - rl->rl_exflags = lkb->lkb_exflags; - rl->rl_flags = lkb->lkb_flags; - rl->rl_lvbseq = lkb->lkb_lvbseq; - rl->rl_rqmode = lkb->lkb_rqmode; - rl->rl_grmode = lkb->lkb_grmode; - rl->rl_status = lkb->lkb_status; - rl->rl_wait_type = lkb->lkb_wait_type; - - if (lkb->lkb_bastaddr) - rl->rl_asts |= AST_BAST; - if (lkb->lkb_astaddr) - rl->rl_asts |= AST_COMP; - - rl->rl_namelen = r->res_length; - memcpy(rl->rl_name, r->res_name, r->res_length); - - /* FIXME: might we have an lvb without DLM_LKF_VALBLK set ? - If so, receive_rcom_lock_args() won't take this copy. */ - - if (lkb->lkb_lvbptr) - memcpy(rl->rl_lvb, lkb->lkb_lvbptr, r->res_ls->ls_lvblen); -} - -int dlm_send_rcom_lock(struct dlm_rsb *r, struct dlm_lkb *lkb) -{ - struct dlm_ls *ls = r->res_ls; - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - struct rcom_lock *rl; - int error, len = sizeof(struct rcom_lock); - - if (lkb->lkb_lvbptr) - len += ls->ls_lvblen; - - error = create_rcom(ls, r->res_nodeid, DLM_RCOM_LOCK, len, &rc, &mh); - if (error) - goto out; - - rl = (struct rcom_lock *) rc->rc_buf; - pack_rcom_lock(r, lkb, rl); - rc->rc_id = (unsigned long) r; - - send_rcom(ls, mh, rc); - out: - return error; -} - -static void receive_rcom_lock(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - int error, nodeid = rc_in->rc_header.h_nodeid; - - dlm_recover_master_copy(ls, rc_in); - - error = create_rcom(ls, nodeid, DLM_RCOM_LOCK_REPLY, - sizeof(struct rcom_lock), &rc, &mh); - if (error) - return; - - /* We send back the same rcom_lock struct we received, but - dlm_recover_master_copy() has filled in rl_remid and rl_result */ - - memcpy(rc->rc_buf, rc_in->rc_buf, sizeof(struct rcom_lock)); - rc->rc_id = rc_in->rc_id; - - send_rcom(ls, mh, rc); -} - -static void receive_rcom_lock_reply(struct dlm_ls *ls, struct dlm_rcom *rc_in) -{ - uint32_t status = dlm_recover_status(ls); - - if (!(status & DLM_RS_DIR)) { - log_debug(ls, "ignoring RCOM_LOCK_REPLY from %u", - rc_in->rc_header.h_nodeid); - return; - } - - dlm_recover_process_copy(ls, rc_in); -} - -static int send_ls_not_ready(int nodeid, struct dlm_rcom *rc_in) -{ - struct dlm_rcom *rc; - struct dlm_mhandle *mh; - char *mb; - int mb_len = sizeof(struct dlm_rcom); - - mh = dlm_lowcomms_get_buffer(nodeid, mb_len, GFP_KERNEL, &mb); - if (!mh) - return -ENOBUFS; - memset(mb, 0, mb_len); - - rc = (struct dlm_rcom *) mb; - - rc->rc_header.h_version = (DLM_HEADER_MAJOR | DLM_HEADER_MINOR); - rc->rc_header.h_lockspace = rc_in->rc_header.h_lockspace; - rc->rc_header.h_nodeid = dlm_our_nodeid(); - rc->rc_header.h_length = mb_len; - rc->rc_header.h_cmd = DLM_RCOM; - - rc->rc_type = DLM_RCOM_STATUS_REPLY; - rc->rc_id = rc_in->rc_id; - rc->rc_result = -ESRCH; - - dlm_rcom_out(rc); - dlm_lowcomms_commit_buffer(mh); - - return 0; -} - -/* Called by dlm_recvd; corresponds to dlm_receive_message() but special - recovery-only comms are sent through here. */ - -void dlm_receive_rcom(struct dlm_header *hd, int nodeid) -{ - struct dlm_rcom *rc = (struct dlm_rcom *) hd; - struct dlm_ls *ls; - - dlm_rcom_in(rc); - - /* If the lockspace doesn't exist then still send a status message - back; it's possible that it just doesn't have its global_id yet. */ - - ls = dlm_find_lockspace_global(hd->h_lockspace); - if (!ls) { - log_print("lockspace %x from %d not found", - hd->h_lockspace, nodeid); - send_ls_not_ready(nodeid, rc); - return; - } - - if (dlm_recovery_stopped(ls) && (rc->rc_type != DLM_RCOM_STATUS)) { - log_error(ls, "ignoring recovery message %x from %d", - rc->rc_type, nodeid); - goto out; - } - - if (nodeid != rc->rc_header.h_nodeid) { - log_error(ls, "bad rcom nodeid %d from %d", - rc->rc_header.h_nodeid, nodeid); - goto out; - } - - switch (rc->rc_type) { - case DLM_RCOM_STATUS: - receive_rcom_status(ls, rc); - break; - - case DLM_RCOM_NAMES: - receive_rcom_names(ls, rc); - break; - - case DLM_RCOM_LOOKUP: - receive_rcom_lookup(ls, rc); - break; - - case DLM_RCOM_LOCK: - receive_rcom_lock(ls, rc); - break; - - case DLM_RCOM_STATUS_REPLY: - receive_rcom_status_reply(ls, rc); - break; - - case DLM_RCOM_NAMES_REPLY: - receive_rcom_names_reply(ls, rc); - break; - - case DLM_RCOM_LOOKUP_REPLY: - receive_rcom_lookup_reply(ls, rc); - break; - - case DLM_RCOM_LOCK_REPLY: - receive_rcom_lock_reply(ls, rc); - break; - - default: - DLM_ASSERT(0, printk("rc_type=%x\n", rc->rc_type);); - } - out: - dlm_put_lockspace(ls); -} - diff --git a/trunk/fs/dlm/rcom.h b/trunk/fs/dlm/rcom.h deleted file mode 100644 index d7984321ff41..000000000000 --- a/trunk/fs/dlm/rcom.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __RCOM_DOT_H__ -#define __RCOM_DOT_H__ - -int dlm_rcom_status(struct dlm_ls *ls, int nodeid); -int dlm_rcom_names(struct dlm_ls *ls, int nodeid, char *last_name,int last_len); -int dlm_send_rcom_lookup(struct dlm_rsb *r, int dir_nodeid); -int dlm_send_rcom_lock(struct dlm_rsb *r, struct dlm_lkb *lkb); -void dlm_receive_rcom(struct dlm_header *hd, int nodeid); - -#endif - diff --git a/trunk/fs/dlm/recover.c b/trunk/fs/dlm/recover.c deleted file mode 100644 index a5e6d184872e..000000000000 --- a/trunk/fs/dlm/recover.c +++ /dev/null @@ -1,765 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "dir.h" -#include "config.h" -#include "ast.h" -#include "memory.h" -#include "rcom.h" -#include "lock.h" -#include "lowcomms.h" -#include "member.h" -#include "recover.h" - - -/* - * Recovery waiting routines: these functions wait for a particular reply from - * a remote node, or for the remote node to report a certain status. They need - * to abort if the lockspace is stopped indicating a node has failed (perhaps - * the one being waited for). - */ - -/* - * Wait until given function returns non-zero or lockspace is stopped - * (LS_RECOVERY_STOP set due to failure of a node in ls_nodes). When another - * function thinks it could have completed the waited-on task, they should wake - * up ls_wait_general to get an immediate response rather than waiting for the - * timer to detect the result. A timer wakes us up periodically while waiting - * to see if we should abort due to a node failure. This should only be called - * by the dlm_recoverd thread. - */ - -static void dlm_wait_timer_fn(unsigned long data) -{ - struct dlm_ls *ls = (struct dlm_ls *) data; - mod_timer(&ls->ls_timer, jiffies + (dlm_config.recover_timer * HZ)); - wake_up(&ls->ls_wait_general); -} - -int dlm_wait_function(struct dlm_ls *ls, int (*testfn) (struct dlm_ls *ls)) -{ - int error = 0; - - init_timer(&ls->ls_timer); - ls->ls_timer.function = dlm_wait_timer_fn; - ls->ls_timer.data = (long) ls; - ls->ls_timer.expires = jiffies + (dlm_config.recover_timer * HZ); - add_timer(&ls->ls_timer); - - wait_event(ls->ls_wait_general, testfn(ls) || dlm_recovery_stopped(ls)); - del_timer_sync(&ls->ls_timer); - - if (dlm_recovery_stopped(ls)) { - log_debug(ls, "dlm_wait_function aborted"); - error = -EINTR; - } - return error; -} - -/* - * An efficient way for all nodes to wait for all others to have a certain - * status. The node with the lowest nodeid polls all the others for their - * status (wait_status_all) and all the others poll the node with the low id - * for its accumulated result (wait_status_low). When all nodes have set - * status flag X, then status flag X_ALL will be set on the low nodeid. - */ - -uint32_t dlm_recover_status(struct dlm_ls *ls) -{ - uint32_t status; - spin_lock(&ls->ls_recover_lock); - status = ls->ls_recover_status; - spin_unlock(&ls->ls_recover_lock); - return status; -} - -void dlm_set_recover_status(struct dlm_ls *ls, uint32_t status) -{ - spin_lock(&ls->ls_recover_lock); - ls->ls_recover_status |= status; - spin_unlock(&ls->ls_recover_lock); -} - -static int wait_status_all(struct dlm_ls *ls, uint32_t wait_status) -{ - struct dlm_rcom *rc = (struct dlm_rcom *) ls->ls_recover_buf; - struct dlm_member *memb; - int error = 0, delay; - - list_for_each_entry(memb, &ls->ls_nodes, list) { - delay = 0; - for (;;) { - if (dlm_recovery_stopped(ls)) { - error = -EINTR; - goto out; - } - - error = dlm_rcom_status(ls, memb->nodeid); - if (error) - goto out; - - if (rc->rc_result & wait_status) - break; - if (delay < 1000) - delay += 20; - msleep(delay); - } - } - out: - return error; -} - -static int wait_status_low(struct dlm_ls *ls, uint32_t wait_status) -{ - struct dlm_rcom *rc = (struct dlm_rcom *) ls->ls_recover_buf; - int error = 0, delay = 0, nodeid = ls->ls_low_nodeid; - - for (;;) { - if (dlm_recovery_stopped(ls)) { - error = -EINTR; - goto out; - } - - error = dlm_rcom_status(ls, nodeid); - if (error) - break; - - if (rc->rc_result & wait_status) - break; - if (delay < 1000) - delay += 20; - msleep(delay); - } - out: - return error; -} - -static int wait_status(struct dlm_ls *ls, uint32_t status) -{ - uint32_t status_all = status << 1; - int error; - - if (ls->ls_low_nodeid == dlm_our_nodeid()) { - error = wait_status_all(ls, status); - if (!error) - dlm_set_recover_status(ls, status_all); - } else - error = wait_status_low(ls, status_all); - - return error; -} - -int dlm_recover_members_wait(struct dlm_ls *ls) -{ - return wait_status(ls, DLM_RS_NODES); -} - -int dlm_recover_directory_wait(struct dlm_ls *ls) -{ - return wait_status(ls, DLM_RS_DIR); -} - -int dlm_recover_locks_wait(struct dlm_ls *ls) -{ - return wait_status(ls, DLM_RS_LOCKS); -} - -int dlm_recover_done_wait(struct dlm_ls *ls) -{ - return wait_status(ls, DLM_RS_DONE); -} - -/* - * The recover_list contains all the rsb's for which we've requested the new - * master nodeid. As replies are returned from the resource directories the - * rsb's are removed from the list. When the list is empty we're done. - * - * The recover_list is later similarly used for all rsb's for which we've sent - * new lkb's and need to receive new corresponding lkid's. - * - * We use the address of the rsb struct as a simple local identifier for the - * rsb so we can match an rcom reply with the rsb it was sent for. - */ - -static int recover_list_empty(struct dlm_ls *ls) -{ - int empty; - - spin_lock(&ls->ls_recover_list_lock); - empty = list_empty(&ls->ls_recover_list); - spin_unlock(&ls->ls_recover_list_lock); - - return empty; -} - -static void recover_list_add(struct dlm_rsb *r) -{ - struct dlm_ls *ls = r->res_ls; - - spin_lock(&ls->ls_recover_list_lock); - if (list_empty(&r->res_recover_list)) { - list_add_tail(&r->res_recover_list, &ls->ls_recover_list); - ls->ls_recover_list_count++; - dlm_hold_rsb(r); - } - spin_unlock(&ls->ls_recover_list_lock); -} - -static void recover_list_del(struct dlm_rsb *r) -{ - struct dlm_ls *ls = r->res_ls; - - spin_lock(&ls->ls_recover_list_lock); - list_del_init(&r->res_recover_list); - ls->ls_recover_list_count--; - spin_unlock(&ls->ls_recover_list_lock); - - dlm_put_rsb(r); -} - -static struct dlm_rsb *recover_list_find(struct dlm_ls *ls, uint64_t id) -{ - struct dlm_rsb *r = NULL; - - spin_lock(&ls->ls_recover_list_lock); - - list_for_each_entry(r, &ls->ls_recover_list, res_recover_list) { - if (id == (unsigned long) r) - goto out; - } - r = NULL; - out: - spin_unlock(&ls->ls_recover_list_lock); - return r; -} - -static void recover_list_clear(struct dlm_ls *ls) -{ - struct dlm_rsb *r, *s; - - spin_lock(&ls->ls_recover_list_lock); - list_for_each_entry_safe(r, s, &ls->ls_recover_list, res_recover_list) { - list_del_init(&r->res_recover_list); - dlm_put_rsb(r); - ls->ls_recover_list_count--; - } - - if (ls->ls_recover_list_count != 0) { - log_error(ls, "warning: recover_list_count %d", - ls->ls_recover_list_count); - ls->ls_recover_list_count = 0; - } - spin_unlock(&ls->ls_recover_list_lock); -} - - -/* Master recovery: find new master node for rsb's that were - mastered on nodes that have been removed. - - dlm_recover_masters - recover_master - dlm_send_rcom_lookup -> receive_rcom_lookup - dlm_dir_lookup - receive_rcom_lookup_reply <- - dlm_recover_master_reply - set_new_master - set_master_lkbs - set_lock_master -*/ - -/* - * Set the lock master for all LKBs in a lock queue - * If we are the new master of the rsb, we may have received new - * MSTCPY locks from other nodes already which we need to ignore - * when setting the new nodeid. - */ - -static void set_lock_master(struct list_head *queue, int nodeid) -{ - struct dlm_lkb *lkb; - - list_for_each_entry(lkb, queue, lkb_statequeue) - if (!(lkb->lkb_flags & DLM_IFL_MSTCPY)) - lkb->lkb_nodeid = nodeid; -} - -static void set_master_lkbs(struct dlm_rsb *r) -{ - set_lock_master(&r->res_grantqueue, r->res_nodeid); - set_lock_master(&r->res_convertqueue, r->res_nodeid); - set_lock_master(&r->res_waitqueue, r->res_nodeid); -} - -/* - * Propogate the new master nodeid to locks - * The NEW_MASTER flag tells dlm_recover_locks() which rsb's to consider. - * The NEW_MASTER2 flag tells recover_lvb() and set_locks_purged() which - * rsb's to consider. - */ - -static void set_new_master(struct dlm_rsb *r, int nodeid) -{ - lock_rsb(r); - r->res_nodeid = nodeid; - set_master_lkbs(r); - rsb_set_flag(r, RSB_NEW_MASTER); - rsb_set_flag(r, RSB_NEW_MASTER2); - unlock_rsb(r); -} - -/* - * We do async lookups on rsb's that need new masters. The rsb's - * waiting for a lookup reply are kept on the recover_list. - */ - -static int recover_master(struct dlm_rsb *r) -{ - struct dlm_ls *ls = r->res_ls; - int error, dir_nodeid, ret_nodeid, our_nodeid = dlm_our_nodeid(); - - dir_nodeid = dlm_dir_nodeid(r); - - if (dir_nodeid == our_nodeid) { - error = dlm_dir_lookup(ls, our_nodeid, r->res_name, - r->res_length, &ret_nodeid); - if (error) - log_error(ls, "recover dir lookup error %d", error); - - if (ret_nodeid == our_nodeid) - ret_nodeid = 0; - set_new_master(r, ret_nodeid); - } else { - recover_list_add(r); - error = dlm_send_rcom_lookup(r, dir_nodeid); - } - - return error; -} - -/* - * When not using a directory, most resource names will hash to a new static - * master nodeid and the resource will need to be remastered. - */ - -static int recover_master_static(struct dlm_rsb *r) -{ - int master = dlm_dir_nodeid(r); - - if (master == dlm_our_nodeid()) - master = 0; - - if (r->res_nodeid != master) { - if (is_master(r)) - dlm_purge_mstcpy_locks(r); - set_new_master(r, master); - return 1; - } - return 0; -} - -/* - * Go through local root resources and for each rsb which has a master which - * has departed, get the new master nodeid from the directory. The dir will - * assign mastery to the first node to look up the new master. That means - * we'll discover in this lookup if we're the new master of any rsb's. - * - * We fire off all the dir lookup requests individually and asynchronously to - * the correct dir node. - */ - -int dlm_recover_masters(struct dlm_ls *ls) -{ - struct dlm_rsb *r; - int error = 0, count = 0; - - log_debug(ls, "dlm_recover_masters"); - - down_read(&ls->ls_root_sem); - list_for_each_entry(r, &ls->ls_root_list, res_root_list) { - if (dlm_recovery_stopped(ls)) { - up_read(&ls->ls_root_sem); - error = -EINTR; - goto out; - } - - if (dlm_no_directory(ls)) - count += recover_master_static(r); - else if (!is_master(r) && dlm_is_removed(ls, r->res_nodeid)) { - recover_master(r); - count++; - } - - schedule(); - } - up_read(&ls->ls_root_sem); - - log_debug(ls, "dlm_recover_masters %d resources", count); - - error = dlm_wait_function(ls, &recover_list_empty); - out: - if (error) - recover_list_clear(ls); - return error; -} - -int dlm_recover_master_reply(struct dlm_ls *ls, struct dlm_rcom *rc) -{ - struct dlm_rsb *r; - int nodeid; - - r = recover_list_find(ls, rc->rc_id); - if (!r) { - log_error(ls, "dlm_recover_master_reply no id %llx", - (unsigned long long)rc->rc_id); - goto out; - } - - nodeid = rc->rc_result; - if (nodeid == dlm_our_nodeid()) - nodeid = 0; - - set_new_master(r, nodeid); - recover_list_del(r); - - if (recover_list_empty(ls)) - wake_up(&ls->ls_wait_general); - out: - return 0; -} - - -/* Lock recovery: rebuild the process-copy locks we hold on a - remastered rsb on the new rsb master. - - dlm_recover_locks - recover_locks - recover_locks_queue - dlm_send_rcom_lock -> receive_rcom_lock - dlm_recover_master_copy - receive_rcom_lock_reply <- - dlm_recover_process_copy -*/ - - -/* - * keep a count of the number of lkb's we send to the new master; when we get - * an equal number of replies then recovery for the rsb is done - */ - -static int recover_locks_queue(struct dlm_rsb *r, struct list_head *head) -{ - struct dlm_lkb *lkb; - int error = 0; - - list_for_each_entry(lkb, head, lkb_statequeue) { - error = dlm_send_rcom_lock(r, lkb); - if (error) - break; - r->res_recover_locks_count++; - } - - return error; -} - -static int recover_locks(struct dlm_rsb *r) -{ - int error = 0; - - lock_rsb(r); - - DLM_ASSERT(!r->res_recover_locks_count, dlm_dump_rsb(r);); - - error = recover_locks_queue(r, &r->res_grantqueue); - if (error) - goto out; - error = recover_locks_queue(r, &r->res_convertqueue); - if (error) - goto out; - error = recover_locks_queue(r, &r->res_waitqueue); - if (error) - goto out; - - if (r->res_recover_locks_count) - recover_list_add(r); - else - rsb_clear_flag(r, RSB_NEW_MASTER); - out: - unlock_rsb(r); - return error; -} - -int dlm_recover_locks(struct dlm_ls *ls) -{ - struct dlm_rsb *r; - int error, count = 0; - - log_debug(ls, "dlm_recover_locks"); - - down_read(&ls->ls_root_sem); - list_for_each_entry(r, &ls->ls_root_list, res_root_list) { - if (is_master(r)) { - rsb_clear_flag(r, RSB_NEW_MASTER); - continue; - } - - if (!rsb_flag(r, RSB_NEW_MASTER)) - continue; - - if (dlm_recovery_stopped(ls)) { - error = -EINTR; - up_read(&ls->ls_root_sem); - goto out; - } - - error = recover_locks(r); - if (error) { - up_read(&ls->ls_root_sem); - goto out; - } - - count += r->res_recover_locks_count; - } - up_read(&ls->ls_root_sem); - - log_debug(ls, "dlm_recover_locks %d locks", count); - - error = dlm_wait_function(ls, &recover_list_empty); - out: - if (error) - recover_list_clear(ls); - else - dlm_set_recover_status(ls, DLM_RS_LOCKS); - return error; -} - -void dlm_recovered_lock(struct dlm_rsb *r) -{ - DLM_ASSERT(rsb_flag(r, RSB_NEW_MASTER), dlm_dump_rsb(r);); - - r->res_recover_locks_count--; - if (!r->res_recover_locks_count) { - rsb_clear_flag(r, RSB_NEW_MASTER); - recover_list_del(r); - } - - if (recover_list_empty(r->res_ls)) - wake_up(&r->res_ls->ls_wait_general); -} - -/* - * The lvb needs to be recovered on all master rsb's. This includes setting - * the VALNOTVALID flag if necessary, and determining the correct lvb contents - * based on the lvb's of the locks held on the rsb. - * - * RSB_VALNOTVALID is set if there are only NL/CR locks on the rsb. If it - * was already set prior to recovery, it's not cleared, regardless of locks. - * - * The LVB contents are only considered for changing when this is a new master - * of the rsb (NEW_MASTER2). Then, the rsb's lvb is taken from any lkb with - * mode > CR. If no lkb's exist with mode above CR, the lvb contents are taken - * from the lkb with the largest lvb sequence number. - */ - -static void recover_lvb(struct dlm_rsb *r) -{ - struct dlm_lkb *lkb, *high_lkb = NULL; - uint32_t high_seq = 0; - int lock_lvb_exists = 0; - int big_lock_exists = 0; - int lvblen = r->res_ls->ls_lvblen; - - list_for_each_entry(lkb, &r->res_grantqueue, lkb_statequeue) { - if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) - continue; - - lock_lvb_exists = 1; - - if (lkb->lkb_grmode > DLM_LOCK_CR) { - big_lock_exists = 1; - goto setflag; - } - - if (((int)lkb->lkb_lvbseq - (int)high_seq) >= 0) { - high_lkb = lkb; - high_seq = lkb->lkb_lvbseq; - } - } - - list_for_each_entry(lkb, &r->res_convertqueue, lkb_statequeue) { - if (!(lkb->lkb_exflags & DLM_LKF_VALBLK)) - continue; - - lock_lvb_exists = 1; - - if (lkb->lkb_grmode > DLM_LOCK_CR) { - big_lock_exists = 1; - goto setflag; - } - - if (((int)lkb->lkb_lvbseq - (int)high_seq) >= 0) { - high_lkb = lkb; - high_seq = lkb->lkb_lvbseq; - } - } - - setflag: - if (!lock_lvb_exists) - goto out; - - if (!big_lock_exists) - rsb_set_flag(r, RSB_VALNOTVALID); - - /* don't mess with the lvb unless we're the new master */ - if (!rsb_flag(r, RSB_NEW_MASTER2)) - goto out; - - if (!r->res_lvbptr) { - r->res_lvbptr = allocate_lvb(r->res_ls); - if (!r->res_lvbptr) - goto out; - } - - if (big_lock_exists) { - r->res_lvbseq = lkb->lkb_lvbseq; - memcpy(r->res_lvbptr, lkb->lkb_lvbptr, lvblen); - } else if (high_lkb) { - r->res_lvbseq = high_lkb->lkb_lvbseq; - memcpy(r->res_lvbptr, high_lkb->lkb_lvbptr, lvblen); - } else { - r->res_lvbseq = 0; - memset(r->res_lvbptr, 0, lvblen); - } - out: - return; -} - -/* All master rsb's flagged RECOVER_CONVERT need to be looked at. The locks - converting PR->CW or CW->PR need to have their lkb_grmode set. */ - -static void recover_conversion(struct dlm_rsb *r) -{ - struct dlm_lkb *lkb; - int grmode = -1; - - list_for_each_entry(lkb, &r->res_grantqueue, lkb_statequeue) { - if (lkb->lkb_grmode == DLM_LOCK_PR || - lkb->lkb_grmode == DLM_LOCK_CW) { - grmode = lkb->lkb_grmode; - break; - } - } - - list_for_each_entry(lkb, &r->res_convertqueue, lkb_statequeue) { - if (lkb->lkb_grmode != DLM_LOCK_IV) - continue; - if (grmode == -1) - lkb->lkb_grmode = lkb->lkb_rqmode; - else - lkb->lkb_grmode = grmode; - } -} - -/* We've become the new master for this rsb and waiting/converting locks may - need to be granted in dlm_grant_after_purge() due to locks that may have - existed from a removed node. */ - -static void set_locks_purged(struct dlm_rsb *r) -{ - if (!list_empty(&r->res_waitqueue) || !list_empty(&r->res_convertqueue)) - rsb_set_flag(r, RSB_LOCKS_PURGED); -} - -void dlm_recover_rsbs(struct dlm_ls *ls) -{ - struct dlm_rsb *r; - int count = 0; - - log_debug(ls, "dlm_recover_rsbs"); - - down_read(&ls->ls_root_sem); - list_for_each_entry(r, &ls->ls_root_list, res_root_list) { - lock_rsb(r); - if (is_master(r)) { - if (rsb_flag(r, RSB_RECOVER_CONVERT)) - recover_conversion(r); - if (rsb_flag(r, RSB_NEW_MASTER2)) - set_locks_purged(r); - recover_lvb(r); - count++; - } - rsb_clear_flag(r, RSB_RECOVER_CONVERT); - rsb_clear_flag(r, RSB_NEW_MASTER2); - unlock_rsb(r); - } - up_read(&ls->ls_root_sem); - - log_debug(ls, "dlm_recover_rsbs %d rsbs", count); -} - -/* Create a single list of all root rsb's to be used during recovery */ - -int dlm_create_root_list(struct dlm_ls *ls) -{ - struct dlm_rsb *r; - int i, error = 0; - - down_write(&ls->ls_root_sem); - if (!list_empty(&ls->ls_root_list)) { - log_error(ls, "root list not empty"); - error = -EINVAL; - goto out; - } - - for (i = 0; i < ls->ls_rsbtbl_size; i++) { - read_lock(&ls->ls_rsbtbl[i].lock); - list_for_each_entry(r, &ls->ls_rsbtbl[i].list, res_hashchain) { - list_add(&r->res_root_list, &ls->ls_root_list); - dlm_hold_rsb(r); - } - read_unlock(&ls->ls_rsbtbl[i].lock); - } - out: - up_write(&ls->ls_root_sem); - return error; -} - -void dlm_release_root_list(struct dlm_ls *ls) -{ - struct dlm_rsb *r, *safe; - - down_write(&ls->ls_root_sem); - list_for_each_entry_safe(r, safe, &ls->ls_root_list, res_root_list) { - list_del_init(&r->res_root_list); - dlm_put_rsb(r); - } - up_write(&ls->ls_root_sem); -} - -void dlm_clear_toss_list(struct dlm_ls *ls) -{ - struct dlm_rsb *r, *safe; - int i; - - for (i = 0; i < ls->ls_rsbtbl_size; i++) { - write_lock(&ls->ls_rsbtbl[i].lock); - list_for_each_entry_safe(r, safe, &ls->ls_rsbtbl[i].toss, - res_hashchain) { - list_del(&r->res_hashchain); - free_rsb(r); - } - write_unlock(&ls->ls_rsbtbl[i].lock); - } -} - diff --git a/trunk/fs/dlm/recover.h b/trunk/fs/dlm/recover.h deleted file mode 100644 index ebd0363f1e08..000000000000 --- a/trunk/fs/dlm/recover.h +++ /dev/null @@ -1,34 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __RECOVER_DOT_H__ -#define __RECOVER_DOT_H__ - -int dlm_wait_function(struct dlm_ls *ls, int (*testfn) (struct dlm_ls *ls)); -uint32_t dlm_recover_status(struct dlm_ls *ls); -void dlm_set_recover_status(struct dlm_ls *ls, uint32_t status); -int dlm_recover_members_wait(struct dlm_ls *ls); -int dlm_recover_directory_wait(struct dlm_ls *ls); -int dlm_recover_locks_wait(struct dlm_ls *ls); -int dlm_recover_done_wait(struct dlm_ls *ls); -int dlm_recover_masters(struct dlm_ls *ls); -int dlm_recover_master_reply(struct dlm_ls *ls, struct dlm_rcom *rc); -int dlm_recover_locks(struct dlm_ls *ls); -void dlm_recovered_lock(struct dlm_rsb *r); -int dlm_create_root_list(struct dlm_ls *ls); -void dlm_release_root_list(struct dlm_ls *ls); -void dlm_clear_toss_list(struct dlm_ls *ls); -void dlm_recover_rsbs(struct dlm_ls *ls); - -#endif /* __RECOVER_DOT_H__ */ - diff --git a/trunk/fs/dlm/recoverd.c b/trunk/fs/dlm/recoverd.c deleted file mode 100644 index 362e3eff4dc9..000000000000 --- a/trunk/fs/dlm/recoverd.c +++ /dev/null @@ -1,290 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "lockspace.h" -#include "member.h" -#include "dir.h" -#include "ast.h" -#include "recover.h" -#include "lowcomms.h" -#include "lock.h" -#include "requestqueue.h" -#include "recoverd.h" - - -/* If the start for which we're re-enabling locking (seq) has been superseded - by a newer stop (ls_recover_seq), we need to leave locking disabled. */ - -static int enable_locking(struct dlm_ls *ls, uint64_t seq) -{ - int error = -EINTR; - - spin_lock(&ls->ls_recover_lock); - if (ls->ls_recover_seq == seq) { - set_bit(LSFL_RUNNING, &ls->ls_flags); - up_write(&ls->ls_in_recovery); - error = 0; - } - spin_unlock(&ls->ls_recover_lock); - return error; -} - -static int ls_recover(struct dlm_ls *ls, struct dlm_recover *rv) -{ - unsigned long start; - int error, neg = 0; - - log_debug(ls, "recover %llx", rv->seq); - - mutex_lock(&ls->ls_recoverd_active); - - /* - * Suspending and resuming dlm_astd ensures that no lkb's from this ls - * will be processed by dlm_astd during recovery. - */ - - dlm_astd_suspend(); - dlm_astd_resume(); - - /* - * This list of root rsb's will be the basis of most of the recovery - * routines. - */ - - dlm_create_root_list(ls); - - /* - * Free all the tossed rsb's so we don't have to recover them. - */ - - dlm_clear_toss_list(ls); - - /* - * Add or remove nodes from the lockspace's ls_nodes list. - * Also waits for all nodes to complete dlm_recover_members. - */ - - error = dlm_recover_members(ls, rv, &neg); - if (error) { - log_error(ls, "recover_members failed %d", error); - goto fail; - } - start = jiffies; - - /* - * Rebuild our own share of the directory by collecting from all other - * nodes their master rsb names that hash to us. - */ - - error = dlm_recover_directory(ls); - if (error) { - log_error(ls, "recover_directory failed %d", error); - goto fail; - } - - /* - * Purge directory-related requests that are saved in requestqueue. - * All dir requests from before recovery are invalid now due to the dir - * rebuild and will be resent by the requesting nodes. - */ - - dlm_purge_requestqueue(ls); - - /* - * Wait for all nodes to complete directory rebuild. - */ - - error = dlm_recover_directory_wait(ls); - if (error) { - log_error(ls, "recover_directory_wait failed %d", error); - goto fail; - } - - /* - * We may have outstanding operations that are waiting for a reply from - * a failed node. Mark these to be resent after recovery. Unlock and - * cancel ops can just be completed. - */ - - dlm_recover_waiters_pre(ls); - - error = dlm_recovery_stopped(ls); - if (error) - goto fail; - - if (neg || dlm_no_directory(ls)) { - /* - * Clear lkb's for departed nodes. - */ - - dlm_purge_locks(ls); - - /* - * Get new master nodeid's for rsb's that were mastered on - * departed nodes. - */ - - error = dlm_recover_masters(ls); - if (error) { - log_error(ls, "recover_masters failed %d", error); - goto fail; - } - - /* - * Send our locks on remastered rsb's to the new masters. - */ - - error = dlm_recover_locks(ls); - if (error) { - log_error(ls, "recover_locks failed %d", error); - goto fail; - } - - error = dlm_recover_locks_wait(ls); - if (error) { - log_error(ls, "recover_locks_wait failed %d", error); - goto fail; - } - - /* - * Finalize state in master rsb's now that all locks can be - * checked. This includes conversion resolution and lvb - * settings. - */ - - dlm_recover_rsbs(ls); - } - - dlm_release_root_list(ls); - - dlm_set_recover_status(ls, DLM_RS_DONE); - error = dlm_recover_done_wait(ls); - if (error) { - log_error(ls, "recover_done_wait failed %d", error); - goto fail; - } - - dlm_clear_members_gone(ls); - - error = enable_locking(ls, rv->seq); - if (error) { - log_error(ls, "enable_locking failed %d", error); - goto fail; - } - - error = dlm_process_requestqueue(ls); - if (error) { - log_error(ls, "process_requestqueue failed %d", error); - goto fail; - } - - error = dlm_recover_waiters_post(ls); - if (error) { - log_error(ls, "recover_waiters_post failed %d", error); - goto fail; - } - - dlm_grant_after_purge(ls); - - dlm_astd_wake(); - - log_debug(ls, "recover %llx done: %u ms", rv->seq, - jiffies_to_msecs(jiffies - start)); - mutex_unlock(&ls->ls_recoverd_active); - - return 0; - - fail: - dlm_release_root_list(ls); - log_debug(ls, "recover %llx error %d", rv->seq, error); - mutex_unlock(&ls->ls_recoverd_active); - return error; -} - -static void do_ls_recovery(struct dlm_ls *ls) -{ - struct dlm_recover *rv = NULL; - - spin_lock(&ls->ls_recover_lock); - rv = ls->ls_recover_args; - ls->ls_recover_args = NULL; - clear_bit(LSFL_RECOVERY_STOP, &ls->ls_flags); - spin_unlock(&ls->ls_recover_lock); - - if (rv) { - ls_recover(ls, rv); - kfree(rv->nodeids); - kfree(rv); - } -} - -static int dlm_recoverd(void *arg) -{ - struct dlm_ls *ls; - - ls = dlm_find_lockspace_local(arg); - if (!ls) { - log_print("dlm_recoverd: no lockspace %p", arg); - return -1; - } - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - if (!test_bit(LSFL_WORK, &ls->ls_flags)) - schedule(); - set_current_state(TASK_RUNNING); - - if (test_and_clear_bit(LSFL_WORK, &ls->ls_flags)) - do_ls_recovery(ls); - } - - dlm_put_lockspace(ls); - return 0; -} - -void dlm_recoverd_kick(struct dlm_ls *ls) -{ - set_bit(LSFL_WORK, &ls->ls_flags); - wake_up_process(ls->ls_recoverd_task); -} - -int dlm_recoverd_start(struct dlm_ls *ls) -{ - struct task_struct *p; - int error = 0; - - p = kthread_run(dlm_recoverd, ls, "dlm_recoverd"); - if (IS_ERR(p)) - error = PTR_ERR(p); - else - ls->ls_recoverd_task = p; - return error; -} - -void dlm_recoverd_stop(struct dlm_ls *ls) -{ - kthread_stop(ls->ls_recoverd_task); -} - -void dlm_recoverd_suspend(struct dlm_ls *ls) -{ - wake_up(&ls->ls_wait_general); - mutex_lock(&ls->ls_recoverd_active); -} - -void dlm_recoverd_resume(struct dlm_ls *ls) -{ - mutex_unlock(&ls->ls_recoverd_active); -} - diff --git a/trunk/fs/dlm/recoverd.h b/trunk/fs/dlm/recoverd.h deleted file mode 100644 index 866657c5d69d..000000000000 --- a/trunk/fs/dlm/recoverd.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __RECOVERD_DOT_H__ -#define __RECOVERD_DOT_H__ - -void dlm_recoverd_kick(struct dlm_ls *ls); -void dlm_recoverd_stop(struct dlm_ls *ls); -int dlm_recoverd_start(struct dlm_ls *ls); -void dlm_recoverd_suspend(struct dlm_ls *ls); -void dlm_recoverd_resume(struct dlm_ls *ls); - -#endif /* __RECOVERD_DOT_H__ */ - diff --git a/trunk/fs/dlm/requestqueue.c b/trunk/fs/dlm/requestqueue.c deleted file mode 100644 index 7b2b089634a2..000000000000 --- a/trunk/fs/dlm/requestqueue.c +++ /dev/null @@ -1,184 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "member.h" -#include "lock.h" -#include "dir.h" -#include "config.h" -#include "requestqueue.h" - -struct rq_entry { - struct list_head list; - int nodeid; - char request[1]; -}; - -/* - * Requests received while the lockspace is in recovery get added to the - * request queue and processed when recovery is complete. This happens when - * the lockspace is suspended on some nodes before it is on others, or the - * lockspace is enabled on some while still suspended on others. - */ - -void dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_header *hd) -{ - struct rq_entry *e; - int length = hd->h_length; - - if (dlm_is_removed(ls, nodeid)) - return; - - e = kmalloc(sizeof(struct rq_entry) + length, GFP_KERNEL); - if (!e) { - log_print("dlm_add_requestqueue: out of memory\n"); - return; - } - - e->nodeid = nodeid; - memcpy(e->request, hd, length); - - mutex_lock(&ls->ls_requestqueue_mutex); - list_add_tail(&e->list, &ls->ls_requestqueue); - mutex_unlock(&ls->ls_requestqueue_mutex); -} - -int dlm_process_requestqueue(struct dlm_ls *ls) -{ - struct rq_entry *e; - struct dlm_header *hd; - int error = 0; - - mutex_lock(&ls->ls_requestqueue_mutex); - - for (;;) { - if (list_empty(&ls->ls_requestqueue)) { - mutex_unlock(&ls->ls_requestqueue_mutex); - error = 0; - break; - } - e = list_entry(ls->ls_requestqueue.next, struct rq_entry, list); - mutex_unlock(&ls->ls_requestqueue_mutex); - - hd = (struct dlm_header *) e->request; - error = dlm_receive_message(hd, e->nodeid, 1); - - if (error == -EINTR) { - /* entry is left on requestqueue */ - log_debug(ls, "process_requestqueue abort eintr"); - break; - } - - mutex_lock(&ls->ls_requestqueue_mutex); - list_del(&e->list); - kfree(e); - - if (dlm_locking_stopped(ls)) { - log_debug(ls, "process_requestqueue abort running"); - mutex_unlock(&ls->ls_requestqueue_mutex); - error = -EINTR; - break; - } - schedule(); - } - - return error; -} - -/* - * After recovery is done, locking is resumed and dlm_recoverd takes all the - * saved requests and processes them as they would have been by dlm_recvd. At - * the same time, dlm_recvd will start receiving new requests from remote - * nodes. We want to delay dlm_recvd processing new requests until - * dlm_recoverd has finished processing the old saved requests. - */ - -void dlm_wait_requestqueue(struct dlm_ls *ls) -{ - for (;;) { - mutex_lock(&ls->ls_requestqueue_mutex); - if (list_empty(&ls->ls_requestqueue)) - break; - if (dlm_locking_stopped(ls)) - break; - mutex_unlock(&ls->ls_requestqueue_mutex); - schedule(); - } - mutex_unlock(&ls->ls_requestqueue_mutex); -} - -static int purge_request(struct dlm_ls *ls, struct dlm_message *ms, int nodeid) -{ - uint32_t type = ms->m_type; - - if (dlm_is_removed(ls, nodeid)) - return 1; - - /* directory operations are always purged because the directory is - always rebuilt during recovery and the lookups resent */ - - if (type == DLM_MSG_REMOVE || - type == DLM_MSG_LOOKUP || - type == DLM_MSG_LOOKUP_REPLY) - return 1; - - if (!dlm_no_directory(ls)) - return 0; - - /* with no directory, the master is likely to change as a part of - recovery; requests to/from the defunct master need to be purged */ - - switch (type) { - case DLM_MSG_REQUEST: - case DLM_MSG_CONVERT: - case DLM_MSG_UNLOCK: - case DLM_MSG_CANCEL: - /* we're no longer the master of this resource, the sender - will resend to the new master (see waiter_needs_recovery) */ - - if (dlm_hash2nodeid(ls, ms->m_hash) != dlm_our_nodeid()) - return 1; - break; - - case DLM_MSG_REQUEST_REPLY: - case DLM_MSG_CONVERT_REPLY: - case DLM_MSG_UNLOCK_REPLY: - case DLM_MSG_CANCEL_REPLY: - case DLM_MSG_GRANT: - /* this reply is from the former master of the resource, - we'll resend to the new master if needed */ - - if (dlm_hash2nodeid(ls, ms->m_hash) != nodeid) - return 1; - break; - } - - return 0; -} - -void dlm_purge_requestqueue(struct dlm_ls *ls) -{ - struct dlm_message *ms; - struct rq_entry *e, *safe; - - mutex_lock(&ls->ls_requestqueue_mutex); - list_for_each_entry_safe(e, safe, &ls->ls_requestqueue, list) { - ms = (struct dlm_message *) e->request; - - if (purge_request(ls, ms, e->nodeid)) { - list_del(&e->list); - kfree(e); - } - } - mutex_unlock(&ls->ls_requestqueue_mutex); -} - diff --git a/trunk/fs/dlm/requestqueue.h b/trunk/fs/dlm/requestqueue.h deleted file mode 100644 index 349f0d292d95..000000000000 --- a/trunk/fs/dlm/requestqueue.h +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __REQUESTQUEUE_DOT_H__ -#define __REQUESTQUEUE_DOT_H__ - -void dlm_add_requestqueue(struct dlm_ls *ls, int nodeid, struct dlm_header *hd); -int dlm_process_requestqueue(struct dlm_ls *ls); -void dlm_wait_requestqueue(struct dlm_ls *ls); -void dlm_purge_requestqueue(struct dlm_ls *ls); - -#endif - diff --git a/trunk/fs/dlm/user.c b/trunk/fs/dlm/user.c deleted file mode 100644 index c37e93e4f2df..000000000000 --- a/trunk/fs/dlm/user.c +++ /dev/null @@ -1,788 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dlm_internal.h" -#include "lockspace.h" -#include "lock.h" -#include "lvb_table.h" - -static const char *name_prefix="dlm"; -static struct miscdevice ctl_device; -static struct file_operations device_fops; - -#ifdef CONFIG_COMPAT - -struct dlm_lock_params32 { - __u8 mode; - __u8 namelen; - __u16 flags; - __u32 lkid; - __u32 parent; - - __u32 castparam; - __u32 castaddr; - __u32 bastparam; - __u32 bastaddr; - __u32 lksb; - - char lvb[DLM_USER_LVB_LEN]; - char name[0]; -}; - -struct dlm_write_request32 { - __u32 version[3]; - __u8 cmd; - __u8 is64bit; - __u8 unused[2]; - - union { - struct dlm_lock_params32 lock; - struct dlm_lspace_params lspace; - } i; -}; - -struct dlm_lksb32 { - __u32 sb_status; - __u32 sb_lkid; - __u8 sb_flags; - __u32 sb_lvbptr; -}; - -struct dlm_lock_result32 { - __u32 length; - __u32 user_astaddr; - __u32 user_astparam; - __u32 user_lksb; - struct dlm_lksb32 lksb; - __u8 bast_mode; - __u8 unused[3]; - /* Offsets may be zero if no data is present */ - __u32 lvb_offset; -}; - -static void compat_input(struct dlm_write_request *kb, - struct dlm_write_request32 *kb32) -{ - kb->version[0] = kb32->version[0]; - kb->version[1] = kb32->version[1]; - kb->version[2] = kb32->version[2]; - - kb->cmd = kb32->cmd; - kb->is64bit = kb32->is64bit; - if (kb->cmd == DLM_USER_CREATE_LOCKSPACE || - kb->cmd == DLM_USER_REMOVE_LOCKSPACE) { - kb->i.lspace.flags = kb32->i.lspace.flags; - kb->i.lspace.minor = kb32->i.lspace.minor; - strcpy(kb->i.lspace.name, kb32->i.lspace.name); - } else { - kb->i.lock.mode = kb32->i.lock.mode; - kb->i.lock.namelen = kb32->i.lock.namelen; - kb->i.lock.flags = kb32->i.lock.flags; - kb->i.lock.lkid = kb32->i.lock.lkid; - kb->i.lock.parent = kb32->i.lock.parent; - kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam; - kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr; - kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam; - kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr; - kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb; - memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN); - memcpy(kb->i.lock.name, kb32->i.lock.name, kb->i.lock.namelen); - } -} - -static void compat_output(struct dlm_lock_result *res, - struct dlm_lock_result32 *res32) -{ - res32->length = res->length - (sizeof(struct dlm_lock_result) - - sizeof(struct dlm_lock_result32)); - res32->user_astaddr = (__u32)(long)res->user_astaddr; - res32->user_astparam = (__u32)(long)res->user_astparam; - res32->user_lksb = (__u32)(long)res->user_lksb; - res32->bast_mode = res->bast_mode; - - res32->lvb_offset = res->lvb_offset; - res32->length = res->length; - - res32->lksb.sb_status = res->lksb.sb_status; - res32->lksb.sb_flags = res->lksb.sb_flags; - res32->lksb.sb_lkid = res->lksb.sb_lkid; - res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr; -} -#endif - - -void dlm_user_add_ast(struct dlm_lkb *lkb, int type) -{ - struct dlm_ls *ls; - struct dlm_user_args *ua; - struct dlm_user_proc *proc; - int remove_ownqueue = 0; - - /* dlm_clear_proc_locks() sets ORPHAN/DEAD flag on each - lkb before dealing with it. We need to check this - flag before taking ls_clear_proc_locks mutex because if - it's set, dlm_clear_proc_locks() holds the mutex. */ - - if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) { - /* log_print("user_add_ast skip1 %x", lkb->lkb_flags); */ - return; - } - - ls = lkb->lkb_resource->res_ls; - mutex_lock(&ls->ls_clear_proc_locks); - - /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast - can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed - lkb->ua so we can't try to use it. */ - - if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) { - /* log_print("user_add_ast skip2 %x", lkb->lkb_flags); */ - goto out; - } - - DLM_ASSERT(lkb->lkb_astparam, dlm_print_lkb(lkb);); - ua = (struct dlm_user_args *)lkb->lkb_astparam; - proc = ua->proc; - - if (type == AST_BAST && ua->bastaddr == NULL) - goto out; - - spin_lock(&proc->asts_spin); - if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) { - kref_get(&lkb->lkb_ref); - list_add_tail(&lkb->lkb_astqueue, &proc->asts); - lkb->lkb_ast_type |= type; - wake_up_interruptible(&proc->wait); - } - - /* noqueue requests that fail may need to be removed from the - proc's locks list, there should be a better way of detecting - this situation than checking all these things... */ - - if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV && - ua->lksb.sb_status == -EAGAIN && !list_empty(&lkb->lkb_ownqueue)) - remove_ownqueue = 1; - - /* We want to copy the lvb to userspace when the completion - ast is read if the status is 0, the lock has an lvb and - lvb_ops says we should. We could probably have set_lvb_lock() - set update_user_lvb instead and not need old_mode */ - - if ((lkb->lkb_ast_type & AST_COMP) && - (lkb->lkb_lksb->sb_status == 0) && - lkb->lkb_lksb->sb_lvbptr && - dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1]) - ua->update_user_lvb = 1; - else - ua->update_user_lvb = 0; - - spin_unlock(&proc->asts_spin); - - if (remove_ownqueue) { - spin_lock(&ua->proc->locks_spin); - list_del_init(&lkb->lkb_ownqueue); - spin_unlock(&ua->proc->locks_spin); - dlm_put_lkb(lkb); - } - out: - mutex_unlock(&ls->ls_clear_proc_locks); -} - -static int device_user_lock(struct dlm_user_proc *proc, - struct dlm_lock_params *params) -{ - struct dlm_ls *ls; - struct dlm_user_args *ua; - int error = -ENOMEM; - - ls = dlm_find_lockspace_local(proc->lockspace); - if (!ls) - return -ENOENT; - - if (!params->castaddr || !params->lksb) { - error = -EINVAL; - goto out; - } - - ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL); - if (!ua) - goto out; - ua->proc = proc; - ua->user_lksb = params->lksb; - ua->castparam = params->castparam; - ua->castaddr = params->castaddr; - ua->bastparam = params->bastparam; - ua->bastaddr = params->bastaddr; - - if (params->flags & DLM_LKF_CONVERT) - error = dlm_user_convert(ls, ua, - params->mode, params->flags, - params->lkid, params->lvb); - else { - error = dlm_user_request(ls, ua, - params->mode, params->flags, - params->name, params->namelen, - params->parent); - if (!error) - error = ua->lksb.sb_lkid; - } - out: - dlm_put_lockspace(ls); - return error; -} - -static int device_user_unlock(struct dlm_user_proc *proc, - struct dlm_lock_params *params) -{ - struct dlm_ls *ls; - struct dlm_user_args *ua; - int error = -ENOMEM; - - ls = dlm_find_lockspace_local(proc->lockspace); - if (!ls) - return -ENOENT; - - ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL); - if (!ua) - goto out; - ua->proc = proc; - ua->user_lksb = params->lksb; - ua->castparam = params->castparam; - ua->castaddr = params->castaddr; - - if (params->flags & DLM_LKF_CANCEL) - error = dlm_user_cancel(ls, ua, params->flags, params->lkid); - else - error = dlm_user_unlock(ls, ua, params->flags, params->lkid, - params->lvb); - out: - dlm_put_lockspace(ls); - return error; -} - -static int device_create_lockspace(struct dlm_lspace_params *params) -{ - dlm_lockspace_t *lockspace; - struct dlm_ls *ls; - int error, len; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - error = dlm_new_lockspace(params->name, strlen(params->name), - &lockspace, 0, DLM_USER_LVB_LEN); - if (error) - return error; - - ls = dlm_find_lockspace_local(lockspace); - if (!ls) - return -ENOENT; - - error = -ENOMEM; - len = strlen(params->name) + strlen(name_prefix) + 2; - ls->ls_device.name = kzalloc(len, GFP_KERNEL); - if (!ls->ls_device.name) - goto fail; - snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix, - params->name); - ls->ls_device.fops = &device_fops; - ls->ls_device.minor = MISC_DYNAMIC_MINOR; - - error = misc_register(&ls->ls_device); - if (error) { - kfree(ls->ls_device.name); - goto fail; - } - - error = ls->ls_device.minor; - dlm_put_lockspace(ls); - return error; - - fail: - dlm_put_lockspace(ls); - dlm_release_lockspace(lockspace, 0); - return error; -} - -static int device_remove_lockspace(struct dlm_lspace_params *params) -{ - dlm_lockspace_t *lockspace; - struct dlm_ls *ls; - int error, force = 0; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - ls = dlm_find_lockspace_device(params->minor); - if (!ls) - return -ENOENT; - - error = misc_deregister(&ls->ls_device); - if (error) { - dlm_put_lockspace(ls); - goto out; - } - kfree(ls->ls_device.name); - - if (params->flags & DLM_USER_LSFLG_FORCEFREE) - force = 2; - - lockspace = ls->ls_local_handle; - - /* dlm_release_lockspace waits for references to go to zero, - so all processes will need to close their device for the ls - before the release will procede */ - - dlm_put_lockspace(ls); - error = dlm_release_lockspace(lockspace, force); - out: - return error; -} - -/* Check the user's version matches ours */ -static int check_version(struct dlm_write_request *req) -{ - if (req->version[0] != DLM_DEVICE_VERSION_MAJOR || - (req->version[0] == DLM_DEVICE_VERSION_MAJOR && - req->version[1] > DLM_DEVICE_VERSION_MINOR)) { - - printk(KERN_DEBUG "dlm: process %s (%d) version mismatch " - "user (%d.%d.%d) kernel (%d.%d.%d)\n", - current->comm, - current->pid, - req->version[0], - req->version[1], - req->version[2], - DLM_DEVICE_VERSION_MAJOR, - DLM_DEVICE_VERSION_MINOR, - DLM_DEVICE_VERSION_PATCH); - return -EINVAL; - } - return 0; -} - -/* - * device_write - * - * device_user_lock - * dlm_user_request -> request_lock - * dlm_user_convert -> convert_lock - * - * device_user_unlock - * dlm_user_unlock -> unlock_lock - * dlm_user_cancel -> cancel_lock - * - * device_create_lockspace - * dlm_new_lockspace - * - * device_remove_lockspace - * dlm_release_lockspace - */ - -/* a write to a lockspace device is a lock or unlock request, a write - to the control device is to create/remove a lockspace */ - -static ssize_t device_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct dlm_user_proc *proc = file->private_data; - struct dlm_write_request *kbuf; - sigset_t tmpsig, allsigs; - int error; - -#ifdef CONFIG_COMPAT - if (count < sizeof(struct dlm_write_request32)) -#else - if (count < sizeof(struct dlm_write_request)) -#endif - return -EINVAL; - - kbuf = kmalloc(count, GFP_KERNEL); - if (!kbuf) - return -ENOMEM; - - if (copy_from_user(kbuf, buf, count)) { - error = -EFAULT; - goto out_free; - } - - if (check_version(kbuf)) { - error = -EBADE; - goto out_free; - } - -#ifdef CONFIG_COMPAT - if (!kbuf->is64bit) { - struct dlm_write_request32 *k32buf; - k32buf = (struct dlm_write_request32 *)kbuf; - kbuf = kmalloc(count + (sizeof(struct dlm_write_request) - - sizeof(struct dlm_write_request32)), GFP_KERNEL); - if (!kbuf) - return -ENOMEM; - - if (proc) - set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags); - compat_input(kbuf, k32buf); - kfree(k32buf); - } -#endif - - /* do we really need this? can a write happen after a close? */ - if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) && - test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) - return -EINVAL; - - sigfillset(&allsigs); - sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); - - error = -EINVAL; - - switch (kbuf->cmd) - { - case DLM_USER_LOCK: - if (!proc) { - log_print("no locking on control device"); - goto out_sig; - } - error = device_user_lock(proc, &kbuf->i.lock); - break; - - case DLM_USER_UNLOCK: - if (!proc) { - log_print("no locking on control device"); - goto out_sig; - } - error = device_user_unlock(proc, &kbuf->i.lock); - break; - - case DLM_USER_CREATE_LOCKSPACE: - if (proc) { - log_print("create/remove only on control device"); - goto out_sig; - } - error = device_create_lockspace(&kbuf->i.lspace); - break; - - case DLM_USER_REMOVE_LOCKSPACE: - if (proc) { - log_print("create/remove only on control device"); - goto out_sig; - } - error = device_remove_lockspace(&kbuf->i.lspace); - break; - - default: - log_print("Unknown command passed to DLM device : %d\n", - kbuf->cmd); - } - - out_sig: - sigprocmask(SIG_SETMASK, &tmpsig, NULL); - recalc_sigpending(); - out_free: - kfree(kbuf); - return error; -} - -/* Every process that opens the lockspace device has its own "proc" structure - hanging off the open file that's used to keep track of locks owned by the - process and asts that need to be delivered to the process. */ - -static int device_open(struct inode *inode, struct file *file) -{ - struct dlm_user_proc *proc; - struct dlm_ls *ls; - - ls = dlm_find_lockspace_device(iminor(inode)); - if (!ls) - return -ENOENT; - - proc = kzalloc(sizeof(struct dlm_user_proc), GFP_KERNEL); - if (!proc) { - dlm_put_lockspace(ls); - return -ENOMEM; - } - - proc->lockspace = ls->ls_local_handle; - INIT_LIST_HEAD(&proc->asts); - INIT_LIST_HEAD(&proc->locks); - spin_lock_init(&proc->asts_spin); - spin_lock_init(&proc->locks_spin); - init_waitqueue_head(&proc->wait); - file->private_data = proc; - - return 0; -} - -static int device_close(struct inode *inode, struct file *file) -{ - struct dlm_user_proc *proc = file->private_data; - struct dlm_ls *ls; - sigset_t tmpsig, allsigs; - - ls = dlm_find_lockspace_local(proc->lockspace); - if (!ls) - return -ENOENT; - - sigfillset(&allsigs); - sigprocmask(SIG_BLOCK, &allsigs, &tmpsig); - - set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags); - - dlm_clear_proc_locks(ls, proc); - - /* at this point no more lkb's should exist for this lockspace, - so there's no chance of dlm_user_add_ast() being called and - looking for lkb->ua->proc */ - - kfree(proc); - file->private_data = NULL; - - dlm_put_lockspace(ls); - dlm_put_lockspace(ls); /* for the find in device_open() */ - - /* FIXME: AUTOFREE: if this ls is no longer used do - device_remove_lockspace() */ - - sigprocmask(SIG_SETMASK, &tmpsig, NULL); - recalc_sigpending(); - - return 0; -} - -static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, - int bmode, char __user *buf, size_t count) -{ -#ifdef CONFIG_COMPAT - struct dlm_lock_result32 result32; -#endif - struct dlm_lock_result result; - void *resultptr; - int error=0; - int len; - int struct_len; - - memset(&result, 0, sizeof(struct dlm_lock_result)); - memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb)); - result.user_lksb = ua->user_lksb; - - /* FIXME: dlm1 provides for the user's bastparam/addr to not be updated - in a conversion unless the conversion is successful. See code - in dlm_user_convert() for updating ua from ua_tmp. OpenVMS, though, - notes that a new blocking AST address and parameter are set even if - the conversion fails, so maybe we should just do that. */ - - if (type == AST_BAST) { - result.user_astaddr = ua->bastaddr; - result.user_astparam = ua->bastparam; - result.bast_mode = bmode; - } else { - result.user_astaddr = ua->castaddr; - result.user_astparam = ua->castparam; - } - -#ifdef CONFIG_COMPAT - if (compat) - len = sizeof(struct dlm_lock_result32); - else -#endif - len = sizeof(struct dlm_lock_result); - struct_len = len; - - /* copy lvb to userspace if there is one, it's been updated, and - the user buffer has space for it */ - - if (ua->update_user_lvb && ua->lksb.sb_lvbptr && - count >= len + DLM_USER_LVB_LEN) { - if (copy_to_user(buf+len, ua->lksb.sb_lvbptr, - DLM_USER_LVB_LEN)) { - error = -EFAULT; - goto out; - } - - result.lvb_offset = len; - len += DLM_USER_LVB_LEN; - } - - result.length = len; - resultptr = &result; -#ifdef CONFIG_COMPAT - if (compat) { - compat_output(&result, &result32); - resultptr = &result32; - } -#endif - - if (copy_to_user(buf, resultptr, struct_len)) - error = -EFAULT; - else - error = len; - out: - return error; -} - -/* a read returns a single ast described in a struct dlm_lock_result */ - -static ssize_t device_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct dlm_user_proc *proc = file->private_data; - struct dlm_lkb *lkb; - struct dlm_user_args *ua; - DECLARE_WAITQUEUE(wait, current); - int error, type=0, bmode=0, removed = 0; - -#ifdef CONFIG_COMPAT - if (count < sizeof(struct dlm_lock_result32)) -#else - if (count < sizeof(struct dlm_lock_result)) -#endif - return -EINVAL; - - /* do we really need this? can a read happen after a close? */ - if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) - return -EINVAL; - - spin_lock(&proc->asts_spin); - if (list_empty(&proc->asts)) { - if (file->f_flags & O_NONBLOCK) { - spin_unlock(&proc->asts_spin); - return -EAGAIN; - } - - add_wait_queue(&proc->wait, &wait); - - repeat: - set_current_state(TASK_INTERRUPTIBLE); - if (list_empty(&proc->asts) && !signal_pending(current)) { - spin_unlock(&proc->asts_spin); - schedule(); - spin_lock(&proc->asts_spin); - goto repeat; - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&proc->wait, &wait); - - if (signal_pending(current)) { - spin_unlock(&proc->asts_spin); - return -ERESTARTSYS; - } - } - - if (list_empty(&proc->asts)) { - spin_unlock(&proc->asts_spin); - return -EAGAIN; - } - - /* there may be both completion and blocking asts to return for - the lkb, don't remove lkb from asts list unless no asts remain */ - - lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue); - - if (lkb->lkb_ast_type & AST_COMP) { - lkb->lkb_ast_type &= ~AST_COMP; - type = AST_COMP; - } else if (lkb->lkb_ast_type & AST_BAST) { - lkb->lkb_ast_type &= ~AST_BAST; - type = AST_BAST; - bmode = lkb->lkb_bastmode; - } - - if (!lkb->lkb_ast_type) { - list_del(&lkb->lkb_astqueue); - removed = 1; - } - spin_unlock(&proc->asts_spin); - - ua = (struct dlm_user_args *)lkb->lkb_astparam; - error = copy_result_to_user(ua, - test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), - type, bmode, buf, count); - - /* removes reference for the proc->asts lists added by - dlm_user_add_ast() and may result in the lkb being freed */ - if (removed) - dlm_put_lkb(lkb); - - return error; -} - -static unsigned int device_poll(struct file *file, poll_table *wait) -{ - struct dlm_user_proc *proc = file->private_data; - - poll_wait(file, &proc->wait, wait); - - spin_lock(&proc->asts_spin); - if (!list_empty(&proc->asts)) { - spin_unlock(&proc->asts_spin); - return POLLIN | POLLRDNORM; - } - spin_unlock(&proc->asts_spin); - return 0; -} - -static int ctl_device_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} - -static int ctl_device_close(struct inode *inode, struct file *file) -{ - return 0; -} - -static struct file_operations device_fops = { - .open = device_open, - .release = device_close, - .read = device_read, - .write = device_write, - .poll = device_poll, - .owner = THIS_MODULE, -}; - -static struct file_operations ctl_device_fops = { - .open = ctl_device_open, - .release = ctl_device_close, - .write = device_write, - .owner = THIS_MODULE, -}; - -int dlm_user_init(void) -{ - int error; - - ctl_device.name = "dlm-control"; - ctl_device.fops = &ctl_device_fops; - ctl_device.minor = MISC_DYNAMIC_MINOR; - - error = misc_register(&ctl_device); - if (error) - log_print("misc_register failed for control device"); - - return error; -} - -void dlm_user_exit(void) -{ - misc_deregister(&ctl_device); -} - diff --git a/trunk/fs/dlm/user.h b/trunk/fs/dlm/user.h deleted file mode 100644 index d38e9f3e4151..000000000000 --- a/trunk/fs/dlm/user.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -#ifndef __USER_DOT_H__ -#define __USER_DOT_H__ - -void dlm_user_add_ast(struct dlm_lkb *lkb, int type); -int dlm_user_init(void); -void dlm_user_exit(void); - -#endif diff --git a/trunk/fs/dlm/util.c b/trunk/fs/dlm/util.c deleted file mode 100644 index 767197db9944..000000000000 --- a/trunk/fs/dlm/util.c +++ /dev/null @@ -1,161 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#include "dlm_internal.h" -#include "rcom.h" -#include "util.h" - -static void header_out(struct dlm_header *hd) -{ - hd->h_version = cpu_to_le32(hd->h_version); - hd->h_lockspace = cpu_to_le32(hd->h_lockspace); - hd->h_nodeid = cpu_to_le32(hd->h_nodeid); - hd->h_length = cpu_to_le16(hd->h_length); -} - -static void header_in(struct dlm_header *hd) -{ - hd->h_version = le32_to_cpu(hd->h_version); - hd->h_lockspace = le32_to_cpu(hd->h_lockspace); - hd->h_nodeid = le32_to_cpu(hd->h_nodeid); - hd->h_length = le16_to_cpu(hd->h_length); -} - -void dlm_message_out(struct dlm_message *ms) -{ - struct dlm_header *hd = (struct dlm_header *) ms; - - header_out(hd); - - ms->m_type = cpu_to_le32(ms->m_type); - ms->m_nodeid = cpu_to_le32(ms->m_nodeid); - ms->m_pid = cpu_to_le32(ms->m_pid); - ms->m_lkid = cpu_to_le32(ms->m_lkid); - ms->m_remid = cpu_to_le32(ms->m_remid); - ms->m_parent_lkid = cpu_to_le32(ms->m_parent_lkid); - ms->m_parent_remid = cpu_to_le32(ms->m_parent_remid); - ms->m_exflags = cpu_to_le32(ms->m_exflags); - ms->m_sbflags = cpu_to_le32(ms->m_sbflags); - ms->m_flags = cpu_to_le32(ms->m_flags); - ms->m_lvbseq = cpu_to_le32(ms->m_lvbseq); - ms->m_hash = cpu_to_le32(ms->m_hash); - ms->m_status = cpu_to_le32(ms->m_status); - ms->m_grmode = cpu_to_le32(ms->m_grmode); - ms->m_rqmode = cpu_to_le32(ms->m_rqmode); - ms->m_bastmode = cpu_to_le32(ms->m_bastmode); - ms->m_asts = cpu_to_le32(ms->m_asts); - ms->m_result = cpu_to_le32(ms->m_result); -} - -void dlm_message_in(struct dlm_message *ms) -{ - struct dlm_header *hd = (struct dlm_header *) ms; - - header_in(hd); - - ms->m_type = le32_to_cpu(ms->m_type); - ms->m_nodeid = le32_to_cpu(ms->m_nodeid); - ms->m_pid = le32_to_cpu(ms->m_pid); - ms->m_lkid = le32_to_cpu(ms->m_lkid); - ms->m_remid = le32_to_cpu(ms->m_remid); - ms->m_parent_lkid = le32_to_cpu(ms->m_parent_lkid); - ms->m_parent_remid = le32_to_cpu(ms->m_parent_remid); - ms->m_exflags = le32_to_cpu(ms->m_exflags); - ms->m_sbflags = le32_to_cpu(ms->m_sbflags); - ms->m_flags = le32_to_cpu(ms->m_flags); - ms->m_lvbseq = le32_to_cpu(ms->m_lvbseq); - ms->m_hash = le32_to_cpu(ms->m_hash); - ms->m_status = le32_to_cpu(ms->m_status); - ms->m_grmode = le32_to_cpu(ms->m_grmode); - ms->m_rqmode = le32_to_cpu(ms->m_rqmode); - ms->m_bastmode = le32_to_cpu(ms->m_bastmode); - ms->m_asts = le32_to_cpu(ms->m_asts); - ms->m_result = le32_to_cpu(ms->m_result); -} - -static void rcom_lock_out(struct rcom_lock *rl) -{ - rl->rl_ownpid = cpu_to_le32(rl->rl_ownpid); - rl->rl_lkid = cpu_to_le32(rl->rl_lkid); - rl->rl_remid = cpu_to_le32(rl->rl_remid); - rl->rl_parent_lkid = cpu_to_le32(rl->rl_parent_lkid); - rl->rl_parent_remid = cpu_to_le32(rl->rl_parent_remid); - rl->rl_exflags = cpu_to_le32(rl->rl_exflags); - rl->rl_flags = cpu_to_le32(rl->rl_flags); - rl->rl_lvbseq = cpu_to_le32(rl->rl_lvbseq); - rl->rl_result = cpu_to_le32(rl->rl_result); - rl->rl_wait_type = cpu_to_le16(rl->rl_wait_type); - rl->rl_namelen = cpu_to_le16(rl->rl_namelen); -} - -static void rcom_lock_in(struct rcom_lock *rl) -{ - rl->rl_ownpid = le32_to_cpu(rl->rl_ownpid); - rl->rl_lkid = le32_to_cpu(rl->rl_lkid); - rl->rl_remid = le32_to_cpu(rl->rl_remid); - rl->rl_parent_lkid = le32_to_cpu(rl->rl_parent_lkid); - rl->rl_parent_remid = le32_to_cpu(rl->rl_parent_remid); - rl->rl_exflags = le32_to_cpu(rl->rl_exflags); - rl->rl_flags = le32_to_cpu(rl->rl_flags); - rl->rl_lvbseq = le32_to_cpu(rl->rl_lvbseq); - rl->rl_result = le32_to_cpu(rl->rl_result); - rl->rl_wait_type = le16_to_cpu(rl->rl_wait_type); - rl->rl_namelen = le16_to_cpu(rl->rl_namelen); -} - -static void rcom_config_out(struct rcom_config *rf) -{ - rf->rf_lvblen = cpu_to_le32(rf->rf_lvblen); - rf->rf_lsflags = cpu_to_le32(rf->rf_lsflags); -} - -static void rcom_config_in(struct rcom_config *rf) -{ - rf->rf_lvblen = le32_to_cpu(rf->rf_lvblen); - rf->rf_lsflags = le32_to_cpu(rf->rf_lsflags); -} - -void dlm_rcom_out(struct dlm_rcom *rc) -{ - struct dlm_header *hd = (struct dlm_header *) rc; - int type = rc->rc_type; - - header_out(hd); - - rc->rc_type = cpu_to_le32(rc->rc_type); - rc->rc_result = cpu_to_le32(rc->rc_result); - rc->rc_id = cpu_to_le64(rc->rc_id); - - if (type == DLM_RCOM_LOCK) - rcom_lock_out((struct rcom_lock *) rc->rc_buf); - - else if (type == DLM_RCOM_STATUS_REPLY) - rcom_config_out((struct rcom_config *) rc->rc_buf); -} - -void dlm_rcom_in(struct dlm_rcom *rc) -{ - struct dlm_header *hd = (struct dlm_header *) rc; - - header_in(hd); - - rc->rc_type = le32_to_cpu(rc->rc_type); - rc->rc_result = le32_to_cpu(rc->rc_result); - rc->rc_id = le64_to_cpu(rc->rc_id); - - if (rc->rc_type == DLM_RCOM_LOCK) - rcom_lock_in((struct rcom_lock *) rc->rc_buf); - - else if (rc->rc_type == DLM_RCOM_STATUS_REPLY) - rcom_config_in((struct rcom_config *) rc->rc_buf); -} - diff --git a/trunk/fs/dlm/util.h b/trunk/fs/dlm/util.h deleted file mode 100644 index 2b25915161c0..000000000000 --- a/trunk/fs/dlm/util.h +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) 2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __UTIL_DOT_H__ -#define __UTIL_DOT_H__ - -void dlm_message_out(struct dlm_message *ms); -void dlm_message_in(struct dlm_message *ms); -void dlm_rcom_out(struct dlm_rcom *rc); -void dlm_rcom_in(struct dlm_rcom *rc); - -#endif - diff --git a/trunk/fs/ecryptfs/Makefile b/trunk/fs/ecryptfs/Makefile deleted file mode 100644 index ca6562451eeb..000000000000 --- a/trunk/fs/ecryptfs/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux 2.6 eCryptfs -# - -obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o - -ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o debug.o diff --git a/trunk/fs/ecryptfs/crypto.c b/trunk/fs/ecryptfs/crypto.c deleted file mode 100644 index ed35a9712fa1..000000000000 --- a/trunk/fs/ecryptfs/crypto.c +++ /dev/null @@ -1,1659 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * - * Copyright (C) 1997-2004 Erez Zadok - * Copyright (C) 2001-2004 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * Michael C. Thompson - * - * 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 "ecryptfs_kernel.h" - -static int -ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv); -static int -ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv); - -/** - * ecryptfs_to_hex - * @dst: Buffer to take hex character representation of contents of - * src; must be at least of size (src_size * 2) - * @src: Buffer to be converted to a hex string respresentation - * @src_size: number of bytes to convert - */ -void ecryptfs_to_hex(char *dst, char *src, size_t src_size) -{ - int x; - - for (x = 0; x < src_size; x++) - sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]); -} - -/** - * ecryptfs_from_hex - * @dst: Buffer to take the bytes from src hex; must be at least of - * size (src_size / 2) - * @src: Buffer to be converted from a hex string respresentation to raw value - * @dst_size: size of dst buffer, or number of hex characters pairs to convert - */ -void ecryptfs_from_hex(char *dst, char *src, int dst_size) -{ - int x; - char tmp[3] = { 0, }; - - for (x = 0; x < dst_size; x++) { - tmp[0] = src[x * 2]; - tmp[1] = src[x * 2 + 1]; - dst[x] = (unsigned char)simple_strtol(tmp, NULL, 16); - } -} - -/** - * ecryptfs_calculate_md5 - calculates the md5 of @src - * @dst: Pointer to 16 bytes of allocated memory - * @crypt_stat: Pointer to crypt_stat struct for the current inode - * @src: Data to be md5'd - * @len: Length of @src - * - * Uses the allocated crypto context that crypt_stat references to - * generate the MD5 sum of the contents of src. - */ -static int ecryptfs_calculate_md5(char *dst, - struct ecryptfs_crypt_stat *crypt_stat, - char *src, int len) -{ - int rc = 0; - struct scatterlist sg; - - mutex_lock(&crypt_stat->cs_md5_tfm_mutex); - sg_init_one(&sg, (u8 *)src, len); - if (!crypt_stat->md5_tfm) { - crypt_stat->md5_tfm = - crypto_alloc_tfm("md5", CRYPTO_TFM_REQ_MAY_SLEEP); - if (!crypt_stat->md5_tfm) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Error attempting to " - "allocate crypto context\n"); - goto out; - } - } - crypto_digest_init(crypt_stat->md5_tfm); - crypto_digest_update(crypt_stat->md5_tfm, &sg, 1); - crypto_digest_final(crypt_stat->md5_tfm, dst); - mutex_unlock(&crypt_stat->cs_md5_tfm_mutex); -out: - return rc; -} - -/** - * ecryptfs_derive_iv - * @iv: destination for the derived iv vale - * @crypt_stat: Pointer to crypt_stat struct for the current inode - * @offset: Offset of the page whose's iv we are to derive - * - * Generate the initialization vector from the given root IV and page - * offset. - * - * Returns zero on success; non-zero on error. - */ -static int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, - pgoff_t offset) -{ - int rc = 0; - char dst[MD5_DIGEST_SIZE]; - char src[ECRYPTFS_MAX_IV_BYTES + 16]; - - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "root iv:\n"); - ecryptfs_dump_hex(crypt_stat->root_iv, crypt_stat->iv_bytes); - } - /* TODO: It is probably secure to just cast the least - * significant bits of the root IV into an unsigned long and - * add the offset to that rather than go through all this - * hashing business. -Halcrow */ - memcpy(src, crypt_stat->root_iv, crypt_stat->iv_bytes); - memset((src + crypt_stat->iv_bytes), 0, 16); - snprintf((src + crypt_stat->iv_bytes), 16, "%ld", offset); - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "source:\n"); - ecryptfs_dump_hex(src, (crypt_stat->iv_bytes + 16)); - } - rc = ecryptfs_calculate_md5(dst, crypt_stat, src, - (crypt_stat->iv_bytes + 16)); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error attempting to compute " - "MD5 while generating IV for a page\n"); - goto out; - } - memcpy(iv, dst, crypt_stat->iv_bytes); - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "derived iv:\n"); - ecryptfs_dump_hex(iv, crypt_stat->iv_bytes); - } -out: - return rc; -} - -/** - * ecryptfs_init_crypt_stat - * @crypt_stat: Pointer to the crypt_stat struct to initialize. - * - * Initialize the crypt_stat structure. - */ -void -ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) -{ - memset((void *)crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat)); - mutex_init(&crypt_stat->cs_mutex); - mutex_init(&crypt_stat->cs_tfm_mutex); - mutex_init(&crypt_stat->cs_md5_tfm_mutex); - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_STRUCT_INITIALIZED); -} - -/** - * ecryptfs_destruct_crypt_stat - * @crypt_stat: Pointer to the crypt_stat struct to initialize. - * - * Releases all memory associated with a crypt_stat struct. - */ -void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat) -{ - if (crypt_stat->tfm) - crypto_free_tfm(crypt_stat->tfm); - if (crypt_stat->md5_tfm) - crypto_free_tfm(crypt_stat->md5_tfm); - memset(crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat)); -} - -void ecryptfs_destruct_mount_crypt_stat( - struct ecryptfs_mount_crypt_stat *mount_crypt_stat) -{ - if (mount_crypt_stat->global_auth_tok_key) - key_put(mount_crypt_stat->global_auth_tok_key); - if (mount_crypt_stat->global_key_tfm) - crypto_free_tfm(mount_crypt_stat->global_key_tfm); - memset(mount_crypt_stat, 0, sizeof(struct ecryptfs_mount_crypt_stat)); -} - -/** - * virt_to_scatterlist - * @addr: Virtual address - * @size: Size of data; should be an even multiple of the block size - * @sg: Pointer to scatterlist array; set to NULL to obtain only - * the number of scatterlist structs required in array - * @sg_size: Max array size - * - * Fills in a scatterlist array with page references for a passed - * virtual address. - * - * Returns the number of scatterlist structs in array used - */ -int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, - int sg_size) -{ - int i = 0; - struct page *pg; - int offset; - int remainder_of_page; - - while (size > 0 && i < sg_size) { - pg = virt_to_page(addr); - offset = offset_in_page(addr); - if (sg) { - sg[i].page = pg; - sg[i].offset = offset; - } - remainder_of_page = PAGE_CACHE_SIZE - offset; - if (size >= remainder_of_page) { - if (sg) - sg[i].length = remainder_of_page; - addr += remainder_of_page; - size -= remainder_of_page; - } else { - if (sg) - sg[i].length = size; - addr += size; - size = 0; - } - i++; - } - if (size > 0) - return -ENOMEM; - return i; -} - -/** - * encrypt_scatterlist - * @crypt_stat: Pointer to the crypt_stat struct to initialize. - * @dest_sg: Destination of encrypted data - * @src_sg: Data to be encrypted - * @size: Length of data to be encrypted - * @iv: iv to use during encryption - * - * Returns the number of bytes encrypted; negative value on error - */ -static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, - struct scatterlist *dest_sg, - struct scatterlist *src_sg, int size, - unsigned char *iv) -{ - int rc = 0; - - BUG_ON(!crypt_stat || !crypt_stat->tfm - || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, - ECRYPTFS_STRUCT_INITIALIZED)); - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Key size [%d]; key:\n", - crypt_stat->key_size); - ecryptfs_dump_hex(crypt_stat->key, - crypt_stat->key_size); - } - /* Consider doing this once, when the file is opened */ - mutex_lock(&crypt_stat->cs_tfm_mutex); - rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key, - crypt_stat->key_size); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", - rc); - mutex_unlock(&crypt_stat->cs_tfm_mutex); - rc = -EINVAL; - goto out; - } - ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size); - crypto_cipher_encrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, iv); - mutex_unlock(&crypt_stat->cs_tfm_mutex); -out: - return rc; -} - -static void -ecryptfs_extent_to_lwr_pg_idx_and_offset(unsigned long *lower_page_idx, - int *byte_offset, - struct ecryptfs_crypt_stat *crypt_stat, - unsigned long extent_num) -{ - unsigned long lower_extent_num; - int extents_occupied_by_headers_at_front; - int bytes_occupied_by_headers_at_front; - int extent_offset; - int extents_per_page; - - bytes_occupied_by_headers_at_front = - ( crypt_stat->header_extent_size - * crypt_stat->num_header_extents_at_front ); - extents_occupied_by_headers_at_front = - ( bytes_occupied_by_headers_at_front - / crypt_stat->extent_size ); - lower_extent_num = extents_occupied_by_headers_at_front + extent_num; - extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; - (*lower_page_idx) = lower_extent_num / extents_per_page; - extent_offset = lower_extent_num % extents_per_page; - (*byte_offset) = extent_offset * crypt_stat->extent_size; - ecryptfs_printk(KERN_DEBUG, " * crypt_stat->header_extent_size = " - "[%d]\n", crypt_stat->header_extent_size); - ecryptfs_printk(KERN_DEBUG, " * crypt_stat->" - "num_header_extents_at_front = [%d]\n", - crypt_stat->num_header_extents_at_front); - ecryptfs_printk(KERN_DEBUG, " * extents_occupied_by_headers_at_" - "front = [%d]\n", extents_occupied_by_headers_at_front); - ecryptfs_printk(KERN_DEBUG, " * lower_extent_num = [0x%.16x]\n", - lower_extent_num); - ecryptfs_printk(KERN_DEBUG, " * extents_per_page = [%d]\n", - extents_per_page); - ecryptfs_printk(KERN_DEBUG, " * (*lower_page_idx) = [0x%.16x]\n", - (*lower_page_idx)); - ecryptfs_printk(KERN_DEBUG, " * extent_offset = [%d]\n", - extent_offset); - ecryptfs_printk(KERN_DEBUG, " * (*byte_offset) = [%d]\n", - (*byte_offset)); -} - -static int ecryptfs_write_out_page(struct ecryptfs_page_crypt_context *ctx, - struct page *lower_page, - struct inode *lower_inode, - int byte_offset_in_page, int bytes_to_write) -{ - int rc = 0; - - if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) { - rc = ecryptfs_commit_lower_page(lower_page, lower_inode, - ctx->param.lower_file, - byte_offset_in_page, - bytes_to_write); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error calling lower " - "commit; rc = [%d]\n", rc); - goto out; - } - } else { - rc = ecryptfs_writepage_and_release_lower_page(lower_page, - lower_inode, - ctx->param.wbc); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error calling lower " - "writepage(); rc = [%d]\n", rc); - goto out; - } - } -out: - return rc; -} - -static int ecryptfs_read_in_page(struct ecryptfs_page_crypt_context *ctx, - struct page **lower_page, - struct inode *lower_inode, - unsigned long lower_page_idx, - int byte_offset_in_page) -{ - int rc = 0; - - if (ctx->mode == ECRYPTFS_PREPARE_COMMIT_MODE) { - /* TODO: Limit this to only the data extents that are - * needed */ - rc = ecryptfs_get_lower_page(lower_page, lower_inode, - ctx->param.lower_file, - lower_page_idx, - byte_offset_in_page, - (PAGE_CACHE_SIZE - - byte_offset_in_page)); - if (rc) { - ecryptfs_printk( - KERN_ERR, "Error attempting to grab, map, " - "and prepare_write lower page with index " - "[0x%.16x]; rc = [%d]\n", lower_page_idx, rc); - goto out; - } - } else { - rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, - lower_inode, - lower_page_idx); - if (rc) { - ecryptfs_printk( - KERN_ERR, "Error attempting to grab and map " - "lower page with index [0x%.16x]; rc = [%d]\n", - lower_page_idx, rc); - goto out; - } - } -out: - return rc; -} - -/** - * ecryptfs_encrypt_page - * @ctx: The context of the page - * - * Encrypt an eCryptfs page. This is done on a per-extent basis. Note - * that eCryptfs pages may straddle the lower pages -- for instance, - * if the file was created on a machine with an 8K page size - * (resulting in an 8K header), and then the file is copied onto a - * host with a 32K page size, then when reading page 0 of the eCryptfs - * file, 24K of page 0 of the lower file will be read and decrypted, - * and then 8K of page 1 of the lower file will be read and decrypted. - * - * The actual operations performed on each page depends on the - * contents of the ecryptfs_page_crypt_context struct. - * - * Returns zero on success; negative on error - */ -int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx) -{ - char extent_iv[ECRYPTFS_MAX_IV_BYTES]; - unsigned long base_extent; - unsigned long extent_offset = 0; - unsigned long lower_page_idx = 0; - unsigned long prior_lower_page_idx = 0; - struct page *lower_page; - struct inode *lower_inode; - struct ecryptfs_inode_info *inode_info; - struct ecryptfs_crypt_stat *crypt_stat; - int rc = 0; - int lower_byte_offset = 0; - int orig_byte_offset = 0; - int num_extents_per_page; -#define ECRYPTFS_PAGE_STATE_UNREAD 0 -#define ECRYPTFS_PAGE_STATE_READ 1 -#define ECRYPTFS_PAGE_STATE_MODIFIED 2 -#define ECRYPTFS_PAGE_STATE_WRITTEN 3 - int page_state; - - lower_inode = ecryptfs_inode_to_lower(ctx->page->mapping->host); - inode_info = ecryptfs_inode_to_private(ctx->page->mapping->host); - crypt_stat = &inode_info->crypt_stat; - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) { - rc = ecryptfs_copy_page_to_lower(ctx->page, lower_inode, - ctx->param.lower_file); - if (rc) - ecryptfs_printk(KERN_ERR, "Error attempting to copy " - "page at index [0x%.16x]\n", - ctx->page->index); - goto out; - } - num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; - base_extent = (ctx->page->index * num_extents_per_page); - page_state = ECRYPTFS_PAGE_STATE_UNREAD; - while (extent_offset < num_extents_per_page) { - ecryptfs_extent_to_lwr_pg_idx_and_offset( - &lower_page_idx, &lower_byte_offset, crypt_stat, - (base_extent + extent_offset)); - if (prior_lower_page_idx != lower_page_idx - && page_state == ECRYPTFS_PAGE_STATE_MODIFIED) { - rc = ecryptfs_write_out_page(ctx, lower_page, - lower_inode, - orig_byte_offset, - (PAGE_CACHE_SIZE - - orig_byte_offset)); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting " - "to write out page; rc = [%d]" - "\n", rc); - goto out; - } - page_state = ECRYPTFS_PAGE_STATE_WRITTEN; - } - if (page_state == ECRYPTFS_PAGE_STATE_UNREAD - || page_state == ECRYPTFS_PAGE_STATE_WRITTEN) { - rc = ecryptfs_read_in_page(ctx, &lower_page, - lower_inode, lower_page_idx, - lower_byte_offset); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting " - "to read in lower page with " - "index [0x%.16x]; rc = [%d]\n", - lower_page_idx, rc); - goto out; - } - orig_byte_offset = lower_byte_offset; - prior_lower_page_idx = lower_page_idx; - page_state = ECRYPTFS_PAGE_STATE_READ; - } - BUG_ON(!(page_state == ECRYPTFS_PAGE_STATE_MODIFIED - || page_state == ECRYPTFS_PAGE_STATE_READ)); - rc = ecryptfs_derive_iv(extent_iv, crypt_stat, - (base_extent + extent_offset)); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting to " - "derive IV for extent [0x%.16x]; " - "rc = [%d]\n", - (base_extent + extent_offset), rc); - goto out; - } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Encrypting extent " - "with iv:\n"); - ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " - "encryption:\n"); - ecryptfs_dump_hex((char *) - (page_address(ctx->page) - + (extent_offset - * crypt_stat->extent_size)), 8); - } - rc = ecryptfs_encrypt_page_offset( - crypt_stat, lower_page, lower_byte_offset, ctx->page, - (extent_offset * crypt_stat->extent_size), - crypt_stat->extent_size, extent_iv); - ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16x]; " - "rc = [%d]\n", - (base_extent + extent_offset), rc); - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " - "encryption:\n"); - ecryptfs_dump_hex((char *)(page_address(lower_page) - + lower_byte_offset), 8); - } - page_state = ECRYPTFS_PAGE_STATE_MODIFIED; - extent_offset++; - } - BUG_ON(orig_byte_offset != 0); - rc = ecryptfs_write_out_page(ctx, lower_page, lower_inode, 0, - (lower_byte_offset - + crypt_stat->extent_size)); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting to write out " - "page; rc = [%d]\n", rc); - goto out; - } -out: - return rc; -} - -/** - * ecryptfs_decrypt_page - * @file: The ecryptfs file - * @page: The page in ecryptfs to decrypt - * - * Decrypt an eCryptfs page. This is done on a per-extent basis. Note - * that eCryptfs pages may straddle the lower pages -- for instance, - * if the file was created on a machine with an 8K page size - * (resulting in an 8K header), and then the file is copied onto a - * host with a 32K page size, then when reading page 0 of the eCryptfs - * file, 24K of page 0 of the lower file will be read and decrypted, - * and then 8K of page 1 of the lower file will be read and decrypted. - * - * Returns zero on success; negative on error - */ -int ecryptfs_decrypt_page(struct file *file, struct page *page) -{ - char extent_iv[ECRYPTFS_MAX_IV_BYTES]; - unsigned long base_extent; - unsigned long extent_offset = 0; - unsigned long lower_page_idx = 0; - unsigned long prior_lower_page_idx = 0; - struct page *lower_page; - char *lower_page_virt = NULL; - struct inode *lower_inode; - struct ecryptfs_crypt_stat *crypt_stat; - int rc = 0; - int byte_offset; - int num_extents_per_page; - int page_state; - - crypt_stat = &(ecryptfs_inode_to_private( - page->mapping->host)->crypt_stat); - lower_inode = ecryptfs_inode_to_lower(page->mapping->host); - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)) { - rc = ecryptfs_do_readpage(file, page, page->index); - if (rc) - ecryptfs_printk(KERN_ERR, "Error attempting to copy " - "page at index [0x%.16x]\n", - page->index); - goto out; - } - num_extents_per_page = PAGE_CACHE_SIZE / crypt_stat->extent_size; - base_extent = (page->index * num_extents_per_page); - lower_page_virt = kmem_cache_alloc(ecryptfs_lower_page_cache, - SLAB_KERNEL); - if (!lower_page_virt) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Error getting page for encrypted " - "lower page(s)\n"); - goto out; - } - lower_page = virt_to_page(lower_page_virt); - page_state = ECRYPTFS_PAGE_STATE_UNREAD; - while (extent_offset < num_extents_per_page) { - ecryptfs_extent_to_lwr_pg_idx_and_offset( - &lower_page_idx, &byte_offset, crypt_stat, - (base_extent + extent_offset)); - if (prior_lower_page_idx != lower_page_idx - || page_state == ECRYPTFS_PAGE_STATE_UNREAD) { - rc = ecryptfs_do_readpage(file, lower_page, - lower_page_idx); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error reading " - "lower encrypted page; rc = " - "[%d]\n", rc); - goto out; - } - prior_lower_page_idx = lower_page_idx; - page_state = ECRYPTFS_PAGE_STATE_READ; - } - rc = ecryptfs_derive_iv(extent_iv, crypt_stat, - (base_extent + extent_offset)); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting to " - "derive IV for extent [0x%.16x]; rc = " - "[%d]\n", - (base_extent + extent_offset), rc); - goto out; - } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Decrypting extent " - "with iv:\n"); - ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes); - ecryptfs_printk(KERN_DEBUG, "First 8 bytes before " - "decryption:\n"); - ecryptfs_dump_hex((lower_page_virt + byte_offset), 8); - } - rc = ecryptfs_decrypt_page_offset(crypt_stat, page, - (extent_offset - * crypt_stat->extent_size), - lower_page, byte_offset, - crypt_stat->extent_size, - extent_iv); - if (rc != crypt_stat->extent_size) { - ecryptfs_printk(KERN_ERR, "Error attempting to " - "decrypt extent [0x%.16x]\n", - (base_extent + extent_offset)); - goto out; - } - rc = 0; - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "First 8 bytes after " - "decryption:\n"); - ecryptfs_dump_hex((char *)(page_address(page) - + byte_offset), 8); - } - extent_offset++; - } -out: - if (lower_page_virt) - kmem_cache_free(ecryptfs_lower_page_cache, lower_page_virt); - return rc; -} - -/** - * decrypt_scatterlist - * - * Returns the number of bytes decrypted; negative value on error - */ -static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, - struct scatterlist *dest_sg, - struct scatterlist *src_sg, int size, - unsigned char *iv) -{ - int rc = 0; - - /* Consider doing this once, when the file is opened */ - mutex_lock(&crypt_stat->cs_tfm_mutex); - rc = crypto_cipher_setkey(crypt_stat->tfm, crypt_stat->key, - crypt_stat->key_size); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n", - rc); - mutex_unlock(&crypt_stat->cs_tfm_mutex); - rc = -EINVAL; - goto out; - } - ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size); - rc = crypto_cipher_decrypt_iv(crypt_stat->tfm, dest_sg, src_sg, size, - iv); - mutex_unlock(&crypt_stat->cs_tfm_mutex); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n", - rc); - goto out; - } - rc = size; -out: - return rc; -} - -/** - * ecryptfs_encrypt_page_offset - * - * Returns the number of bytes encrypted - */ -static int -ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv) -{ - struct scatterlist src_sg, dst_sg; - - src_sg.page = src_page; - src_sg.offset = src_offset; - src_sg.length = size; - dst_sg.page = dst_page; - dst_sg.offset = dst_offset; - dst_sg.length = size; - return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); -} - -/** - * ecryptfs_decrypt_page_offset - * - * Returns the number of bytes decrypted - */ -static int -ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv) -{ - struct scatterlist src_sg, dst_sg; - - src_sg.page = src_page; - src_sg.offset = src_offset; - src_sg.length = size; - dst_sg.page = dst_page; - dst_sg.offset = dst_offset; - dst_sg.length = size; - return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); -} - -#define ECRYPTFS_MAX_SCATTERLIST_LEN 4 - -/** - * ecryptfs_init_crypt_ctx - * @crypt_stat: Uninitilized crypt stats structure - * - * Initialize the crypto context. - * - * TODO: Performance: Keep a cache of initialized cipher contexts; - * only init if needed - */ -int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat) -{ - int rc = -EINVAL; - - if (!crypt_stat->cipher) { - ecryptfs_printk(KERN_ERR, "No cipher specified\n"); - goto out; - } - ecryptfs_printk(KERN_DEBUG, - "Initializing cipher [%s]; strlen = [%d]; " - "key_size_bits = [%d]\n", - crypt_stat->cipher, (int)strlen(crypt_stat->cipher), - crypt_stat->key_size << 3); - if (crypt_stat->tfm) { - rc = 0; - goto out; - } - mutex_lock(&crypt_stat->cs_tfm_mutex); - crypt_stat->tfm = crypto_alloc_tfm(crypt_stat->cipher, - ECRYPTFS_DEFAULT_CHAINING_MODE - | CRYPTO_TFM_REQ_WEAK_KEY); - mutex_unlock(&crypt_stat->cs_tfm_mutex); - if (!crypt_stat->tfm) { - ecryptfs_printk(KERN_ERR, "cryptfs: init_crypt_ctx(): " - "Error initializing cipher [%s]\n", - crypt_stat->cipher); - goto out; - } - rc = 0; -out: - return rc; -} - -static void set_extent_mask_and_shift(struct ecryptfs_crypt_stat *crypt_stat) -{ - int extent_size_tmp; - - crypt_stat->extent_mask = 0xFFFFFFFF; - crypt_stat->extent_shift = 0; - if (crypt_stat->extent_size == 0) - return; - extent_size_tmp = crypt_stat->extent_size; - while ((extent_size_tmp & 0x01) == 0) { - extent_size_tmp >>= 1; - crypt_stat->extent_mask <<= 1; - crypt_stat->extent_shift++; - } -} - -void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat) -{ - /* Default values; may be overwritten as we are parsing the - * packets. */ - crypt_stat->extent_size = ECRYPTFS_DEFAULT_EXTENT_SIZE; - set_extent_mask_and_shift(crypt_stat); - crypt_stat->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES; - if (PAGE_CACHE_SIZE <= ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { - crypt_stat->header_extent_size = - ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE; - } else - crypt_stat->header_extent_size = PAGE_CACHE_SIZE; - crypt_stat->num_header_extents_at_front = 1; -} - -/** - * ecryptfs_compute_root_iv - * @crypt_stats - * - * On error, sets the root IV to all 0's. - */ -int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat) -{ - int rc = 0; - char dst[MD5_DIGEST_SIZE]; - - BUG_ON(crypt_stat->iv_bytes > MD5_DIGEST_SIZE); - BUG_ON(crypt_stat->iv_bytes <= 0); - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID)) { - rc = -EINVAL; - ecryptfs_printk(KERN_WARNING, "Session key not valid; " - "cannot generate root IV\n"); - goto out; - } - rc = ecryptfs_calculate_md5(dst, crypt_stat, crypt_stat->key, - crypt_stat->key_size); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error attempting to compute " - "MD5 while generating root IV\n"); - goto out; - } - memcpy(crypt_stat->root_iv, dst, crypt_stat->iv_bytes); -out: - if (rc) { - memset(crypt_stat->root_iv, 0, crypt_stat->iv_bytes); - ECRYPTFS_SET_FLAG(crypt_stat->flags, - ECRYPTFS_SECURITY_WARNING); - } - return rc; -} - -static void ecryptfs_generate_new_key(struct ecryptfs_crypt_stat *crypt_stat) -{ - get_random_bytes(crypt_stat->key, crypt_stat->key_size); - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); - ecryptfs_compute_root_iv(crypt_stat); - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Generated new session key:\n"); - ecryptfs_dump_hex(crypt_stat->key, - crypt_stat->key_size); - } -} - -/** - * ecryptfs_set_default_crypt_stat_vals - * @crypt_stat - * - * Default values in the event that policy does not override them. - */ -static void ecryptfs_set_default_crypt_stat_vals( - struct ecryptfs_crypt_stat *crypt_stat, - struct ecryptfs_mount_crypt_stat *mount_crypt_stat) -{ - ecryptfs_set_default_sizes(crypt_stat); - strcpy(crypt_stat->cipher, ECRYPTFS_DEFAULT_CIPHER); - crypt_stat->key_size = ECRYPTFS_DEFAULT_KEY_BYTES; - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); - crypt_stat->file_version = ECRYPTFS_FILE_VERSION; - crypt_stat->mount_crypt_stat = mount_crypt_stat; -} - -/** - * ecryptfs_new_file_context - * @ecryptfs_dentry - * - * If the crypto context for the file has not yet been established, - * this is where we do that. Establishing a new crypto context - * involves the following decisions: - * - What cipher to use? - * - What set of authentication tokens to use? - * Here we just worry about getting enough information into the - * authentication tokens so that we know that they are available. - * We associate the available authentication tokens with the new file - * via the set of signatures in the crypt_stat struct. Later, when - * the headers are actually written out, we may again defer to - * userspace to perform the encryption of the session key; for the - * foreseeable future, this will be the case with public key packets. - * - * Returns zero on success; non-zero otherwise - */ -/* Associate an authentication token(s) with the file */ -int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) -{ - int rc = 0; - struct ecryptfs_crypt_stat *crypt_stat = - &ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat; - struct ecryptfs_mount_crypt_stat *mount_crypt_stat = - &ecryptfs_superblock_to_private( - ecryptfs_dentry->d_sb)->mount_crypt_stat; - int cipher_name_len; - - ecryptfs_set_default_crypt_stat_vals(crypt_stat, mount_crypt_stat); - /* See if there are mount crypt options */ - if (mount_crypt_stat->global_auth_tok) { - ecryptfs_printk(KERN_DEBUG, "Initializing context for new " - "file using mount_crypt_stat\n"); - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); - memcpy(crypt_stat->keysigs[crypt_stat->num_keysigs++], - mount_crypt_stat->global_auth_tok_sig, - ECRYPTFS_SIG_SIZE_HEX); - cipher_name_len = - strlen(mount_crypt_stat->global_default_cipher_name); - memcpy(crypt_stat->cipher, - mount_crypt_stat->global_default_cipher_name, - cipher_name_len); - crypt_stat->cipher[cipher_name_len] = '\0'; - crypt_stat->key_size = - mount_crypt_stat->global_default_cipher_key_size; - ecryptfs_generate_new_key(crypt_stat); - } else - /* We should not encounter this scenario since we - * should detect lack of global_auth_tok at mount time - * TODO: Applies to 0.1 release only; remove in future - * release */ - BUG(); - rc = ecryptfs_init_crypt_ctx(crypt_stat); - if (rc) - ecryptfs_printk(KERN_ERR, "Error initializing cryptographic " - "context for cipher [%s]: rc = [%d]\n", - crypt_stat->cipher, rc); - return rc; -} - -/** - * contains_ecryptfs_marker - check for the ecryptfs marker - * @data: The data block in which to check - * - * Returns one if marker found; zero if not found - */ -int contains_ecryptfs_marker(char *data) -{ - u32 m_1, m_2; - - memcpy(&m_1, data, 4); - m_1 = be32_to_cpu(m_1); - memcpy(&m_2, (data + 4), 4); - m_2 = be32_to_cpu(m_2); - if ((m_1 ^ MAGIC_ECRYPTFS_MARKER) == m_2) - return 1; - ecryptfs_printk(KERN_DEBUG, "m_1 = [0x%.8x]; m_2 = [0x%.8x]; " - "MAGIC_ECRYPTFS_MARKER = [0x%.8x]\n", m_1, m_2, - MAGIC_ECRYPTFS_MARKER); - ecryptfs_printk(KERN_DEBUG, "(m_1 ^ MAGIC_ECRYPTFS_MARKER) = " - "[0x%.8x]\n", (m_1 ^ MAGIC_ECRYPTFS_MARKER)); - return 0; -} - -struct ecryptfs_flag_map_elem { - u32 file_flag; - u32 local_flag; -}; - -/* Add support for additional flags by adding elements here. */ -static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = { - {0x00000001, ECRYPTFS_ENABLE_HMAC}, - {0x00000002, ECRYPTFS_ENCRYPTED} -}; - -/** - * ecryptfs_process_flags - * @crypt_stat - * @page_virt: Source data to be parsed - * @bytes_read: Updated with the number of bytes read - * - * Returns zero on success; non-zero if the flag set is invalid - */ -static int ecryptfs_process_flags(struct ecryptfs_crypt_stat *crypt_stat, - char *page_virt, int *bytes_read) -{ - int rc = 0; - int i; - u32 flags; - - memcpy(&flags, page_virt, 4); - flags = be32_to_cpu(flags); - for (i = 0; i < ((sizeof(ecryptfs_flag_map) - / sizeof(struct ecryptfs_flag_map_elem))); i++) - if (flags & ecryptfs_flag_map[i].file_flag) { - ECRYPTFS_SET_FLAG(crypt_stat->flags, - ecryptfs_flag_map[i].local_flag); - } else - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, - ecryptfs_flag_map[i].local_flag); - /* Version is in top 8 bits of the 32-bit flag vector */ - crypt_stat->file_version = ((flags >> 24) & 0xFF); - (*bytes_read) = 4; - return rc; -} - -/** - * write_ecryptfs_marker - * @page_virt: The pointer to in a page to begin writing the marker - * @written: Number of bytes written - * - * Marker = 0x3c81b7f5 - */ -static void write_ecryptfs_marker(char *page_virt, size_t *written) -{ - u32 m_1, m_2; - - get_random_bytes(&m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); - m_2 = (m_1 ^ MAGIC_ECRYPTFS_MARKER); - m_1 = cpu_to_be32(m_1); - memcpy(page_virt, &m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); - m_2 = cpu_to_be32(m_2); - memcpy(page_virt + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &m_2, - (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); - (*written) = MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; -} - -static void -write_ecryptfs_flags(char *page_virt, struct ecryptfs_crypt_stat *crypt_stat, - size_t *written) -{ - u32 flags = 0; - int i; - - for (i = 0; i < ((sizeof(ecryptfs_flag_map) - / sizeof(struct ecryptfs_flag_map_elem))); i++) - if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, - ecryptfs_flag_map[i].local_flag)) - flags |= ecryptfs_flag_map[i].file_flag; - /* Version is in top 8 bits of the 32-bit flag vector */ - flags |= ((((u8)crypt_stat->file_version) << 24) & 0xFF000000); - flags = cpu_to_be32(flags); - memcpy(page_virt, &flags, 4); - (*written) = 4; -} - -struct ecryptfs_cipher_code_str_map_elem { - char cipher_str[16]; - u16 cipher_code; -}; - -/* Add support for additional ciphers by adding elements here. The - * cipher_code is whatever OpenPGP applicatoins use to identify the - * ciphers. List in order of probability. */ -static struct ecryptfs_cipher_code_str_map_elem -ecryptfs_cipher_code_str_map[] = { - {"aes",RFC2440_CIPHER_AES_128 }, - {"blowfish", RFC2440_CIPHER_BLOWFISH}, - {"des3_ede", RFC2440_CIPHER_DES3_EDE}, - {"cast5", RFC2440_CIPHER_CAST_5}, - {"twofish", RFC2440_CIPHER_TWOFISH}, - {"cast6", RFC2440_CIPHER_CAST_6}, - {"aes", RFC2440_CIPHER_AES_192}, - {"aes", RFC2440_CIPHER_AES_256} -}; - -/** - * ecryptfs_code_for_cipher_string - * @str: The string representing the cipher name - * - * Returns zero on no match, or the cipher code on match - */ -u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat) -{ - int i; - u16 code = 0; - struct ecryptfs_cipher_code_str_map_elem *map = - ecryptfs_cipher_code_str_map; - - if (strcmp(crypt_stat->cipher, "aes") == 0) { - switch (crypt_stat->key_size) { - case 16: - code = RFC2440_CIPHER_AES_128; - break; - case 24: - code = RFC2440_CIPHER_AES_192; - break; - case 32: - code = RFC2440_CIPHER_AES_256; - } - } else { - for (i = 0; i < ARRAY_SIZE(ecryptfs_cipher_code_str_map); i++) - if (strcmp(crypt_stat->cipher, map[i].cipher_str) == 0){ - code = map[i].cipher_code; - break; - } - } - return code; -} - -/** - * ecryptfs_cipher_code_to_string - * @str: Destination to write out the cipher name - * @cipher_code: The code to convert to cipher name string - * - * Returns zero on success - */ -int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code) -{ - int rc = 0; - int i; - - str[0] = '\0'; - for (i = 0; i < ARRAY_SIZE(ecryptfs_cipher_code_str_map); i++) - if (cipher_code == ecryptfs_cipher_code_str_map[i].cipher_code) - strcpy(str, ecryptfs_cipher_code_str_map[i].cipher_str); - if (str[0] == '\0') { - ecryptfs_printk(KERN_WARNING, "Cipher code not recognized: " - "[%d]\n", cipher_code); - rc = -EINVAL; - } - return rc; -} - -/** - * ecryptfs_read_header_region - * @data - * @dentry - * @nd - * - * Returns zero on success; non-zero otherwise - */ -int ecryptfs_read_header_region(char *data, struct dentry *dentry, - struct vfsmount *mnt) -{ - struct file *file; - mm_segment_t oldfs; - int rc; - - mnt = mntget(mnt); - file = dentry_open(dentry, mnt, O_RDONLY); - if (IS_ERR(file)) { - ecryptfs_printk(KERN_DEBUG, "Error opening file to " - "read header region\n"); - mntput(mnt); - rc = PTR_ERR(file); - goto out; - } - file->f_pos = 0; - oldfs = get_fs(); - set_fs(get_ds()); - /* For releases 0.1 and 0.2, all of the header information - * fits in the first data extent-sized region. */ - rc = file->f_op->read(file, (char __user *)data, - ECRYPTFS_DEFAULT_EXTENT_SIZE, &file->f_pos); - set_fs(oldfs); - fput(file); - rc = 0; -out: - return rc; -} - -static void -write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat, - size_t *written) -{ - u32 header_extent_size; - u16 num_header_extents_at_front; - - header_extent_size = (u32)crypt_stat->header_extent_size; - num_header_extents_at_front = - (u16)crypt_stat->num_header_extents_at_front; - header_extent_size = cpu_to_be32(header_extent_size); - memcpy(virt, &header_extent_size, 4); - virt += 4; - num_header_extents_at_front = cpu_to_be16(num_header_extents_at_front); - memcpy(virt, &num_header_extents_at_front, 2); - (*written) = 6; -} - -struct kmem_cache *ecryptfs_header_cache_0; -struct kmem_cache *ecryptfs_header_cache_1; -struct kmem_cache *ecryptfs_header_cache_2; - -/** - * ecryptfs_write_headers_virt - * @page_virt - * @crypt_stat - * @ecryptfs_dentry - * - * Format version: 1 - * - * Header Extent: - * Octets 0-7: Unencrypted file size (big-endian) - * Octets 8-15: eCryptfs special marker - * Octets 16-19: Flags - * Octet 16: File format version number (between 0 and 255) - * Octets 17-18: Reserved - * Octet 19: Bit 1 (lsb): Reserved - * Bit 2: Encrypted? - * Bits 3-8: Reserved - * Octets 20-23: Header extent size (big-endian) - * Octets 24-25: Number of header extents at front of file - * (big-endian) - * Octet 26: Begin RFC 2440 authentication token packet set - * Data Extent 0: - * Lower data (CBC encrypted) - * Data Extent 1: - * Lower data (CBC encrypted) - * ... - * - * Returns zero on success - */ -int ecryptfs_write_headers_virt(char *page_virt, - struct ecryptfs_crypt_stat *crypt_stat, - struct dentry *ecryptfs_dentry) -{ - int rc; - size_t written; - size_t offset; - - offset = ECRYPTFS_FILE_SIZE_BYTES; - write_ecryptfs_marker((page_virt + offset), &written); - offset += written; - write_ecryptfs_flags((page_virt + offset), crypt_stat, &written); - offset += written; - write_header_metadata((page_virt + offset), crypt_stat, &written); - offset += written; - rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stat, - ecryptfs_dentry, &written, - PAGE_CACHE_SIZE - offset); - if (rc) - ecryptfs_printk(KERN_WARNING, "Error generating key packet " - "set; rc = [%d]\n", rc); - return rc; -} - -/** - * ecryptfs_write_headers - * @lower_file: The lower file struct, which was returned from dentry_open - * - * Write the file headers out. This will likely involve a userspace - * callout, in which the session key is encrypted with one or more - * public keys and/or the passphrase necessary to do the encryption is - * retrieved via a prompt. Exactly what happens at this point should - * be policy-dependent. - * - * Returns zero on success; non-zero on error - */ -int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, - struct file *lower_file) -{ - mm_segment_t oldfs; - struct ecryptfs_crypt_stat *crypt_stat; - char *page_virt; - int current_header_page; - int header_pages; - int rc = 0; - - crypt_stat = &ecryptfs_inode_to_private( - ecryptfs_dentry->d_inode)->crypt_stat; - if (likely(ECRYPTFS_CHECK_FLAG(crypt_stat->flags, - ECRYPTFS_ENCRYPTED))) { - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, - ECRYPTFS_KEY_VALID)) { - ecryptfs_printk(KERN_DEBUG, "Key is " - "invalid; bailing out\n"); - rc = -EINVAL; - goto out; - } - } else { - rc = -EINVAL; - ecryptfs_printk(KERN_WARNING, - "Called with crypt_stat->encrypted == 0\n"); - goto out; - } - /* Released in this function */ - page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER); - if (!page_virt) { - ecryptfs_printk(KERN_ERR, "Out of memory\n"); - rc = -ENOMEM; - goto out; - } - memset(page_virt, 0, PAGE_CACHE_SIZE); - rc = ecryptfs_write_headers_virt(page_virt, crypt_stat, - ecryptfs_dentry); - if (unlikely(rc)) { - ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n"); - memset(page_virt, 0, PAGE_CACHE_SIZE); - goto out_free; - } - ecryptfs_printk(KERN_DEBUG, - "Writing key packet set to underlying file\n"); - lower_file->f_pos = 0; - oldfs = get_fs(); - set_fs(get_ds()); - ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->" - "write() w/ header page; lower_file->f_pos = " - "[0x%.16x]\n", lower_file->f_pos); - lower_file->f_op->write(lower_file, (char __user *)page_virt, - PAGE_CACHE_SIZE, &lower_file->f_pos); - header_pages = ((crypt_stat->header_extent_size - * crypt_stat->num_header_extents_at_front) - / PAGE_CACHE_SIZE); - memset(page_virt, 0, PAGE_CACHE_SIZE); - current_header_page = 1; - while (current_header_page < header_pages) { - ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->" - "write() w/ zero'd page; lower_file->f_pos = " - "[0x%.16x]\n", lower_file->f_pos); - lower_file->f_op->write(lower_file, (char __user *)page_virt, - PAGE_CACHE_SIZE, &lower_file->f_pos); - current_header_page++; - } - set_fs(oldfs); - ecryptfs_printk(KERN_DEBUG, - "Done writing key packet set to underlying file.\n"); -out_free: - kmem_cache_free(ecryptfs_header_cache_0, page_virt); -out: - return rc; -} - -static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat, - char *virt, int *bytes_read) -{ - int rc = 0; - u32 header_extent_size; - u16 num_header_extents_at_front; - - memcpy(&header_extent_size, virt, 4); - header_extent_size = be32_to_cpu(header_extent_size); - virt += 4; - memcpy(&num_header_extents_at_front, virt, 2); - num_header_extents_at_front = be16_to_cpu(num_header_extents_at_front); - crypt_stat->header_extent_size = (int)header_extent_size; - crypt_stat->num_header_extents_at_front = - (int)num_header_extents_at_front; - (*bytes_read) = 6; - if ((crypt_stat->header_extent_size - * crypt_stat->num_header_extents_at_front) - < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { - rc = -EINVAL; - ecryptfs_printk(KERN_WARNING, "Invalid header extent size: " - "[%d]\n", crypt_stat->header_extent_size); - } - return rc; -} - -/** - * set_default_header_data - * - * For version 0 file format; this function is only for backwards - * compatibility for files created with the prior versions of - * eCryptfs. - */ -static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat) -{ - crypt_stat->header_extent_size = 4096; - crypt_stat->num_header_extents_at_front = 1; -} - -/** - * ecryptfs_read_headers_virt - * - * Read/parse the header data. The header format is detailed in the - * comment block for the ecryptfs_write_headers_virt() function. - * - * Returns zero on success - */ -static int ecryptfs_read_headers_virt(char *page_virt, - struct ecryptfs_crypt_stat *crypt_stat, - struct dentry *ecryptfs_dentry) -{ - int rc = 0; - int offset; - int bytes_read; - - ecryptfs_set_default_sizes(crypt_stat); - crypt_stat->mount_crypt_stat = &ecryptfs_superblock_to_private( - ecryptfs_dentry->d_sb)->mount_crypt_stat; - offset = ECRYPTFS_FILE_SIZE_BYTES; - rc = contains_ecryptfs_marker(page_virt + offset); - if (rc == 0) { - rc = -EINVAL; - goto out; - } - offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; - rc = ecryptfs_process_flags(crypt_stat, (page_virt + offset), - &bytes_read); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error processing flags\n"); - goto out; - } - if (crypt_stat->file_version > ECRYPTFS_SUPPORTED_FILE_VERSION) { - ecryptfs_printk(KERN_WARNING, "File version is [%d]; only " - "file version [%d] is supported by this " - "version of eCryptfs\n", - crypt_stat->file_version, - ECRYPTFS_SUPPORTED_FILE_VERSION); - rc = -EINVAL; - goto out; - } - offset += bytes_read; - if (crypt_stat->file_version >= 1) { - rc = parse_header_metadata(crypt_stat, (page_virt + offset), - &bytes_read); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error reading header " - "metadata; rc = [%d]\n", rc); - } - offset += bytes_read; - } else - set_default_header_data(crypt_stat); - rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset), - ecryptfs_dentry); -out: - return rc; -} - -/** - * ecryptfs_read_headers - * - * Returns zero if valid headers found and parsed; non-zero otherwise - */ -int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, - struct file *lower_file) -{ - int rc = 0; - char *page_virt = NULL; - mm_segment_t oldfs; - ssize_t bytes_read; - struct ecryptfs_crypt_stat *crypt_stat = - &ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat; - - /* Read the first page from the underlying file */ - page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER); - if (!page_virt) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Unable to allocate page_virt\n"); - goto out; - } - lower_file->f_pos = 0; - oldfs = get_fs(); - set_fs(get_ds()); - bytes_read = lower_file->f_op->read(lower_file, - (char __user *)page_virt, - ECRYPTFS_DEFAULT_EXTENT_SIZE, - &lower_file->f_pos); - set_fs(oldfs); - if (bytes_read != ECRYPTFS_DEFAULT_EXTENT_SIZE) { - rc = -EINVAL; - goto out; - } - rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, - ecryptfs_dentry); - if (rc) { - ecryptfs_printk(KERN_DEBUG, "Valid eCryptfs headers not " - "found\n"); - rc = -EINVAL; - } -out: - if (page_virt) { - memset(page_virt, 0, PAGE_CACHE_SIZE); - kmem_cache_free(ecryptfs_header_cache_1, page_virt); - } - return rc; -} - -/** - * ecryptfs_encode_filename - converts a plaintext file name to cipher text - * @crypt_stat: The crypt_stat struct associated with the file anem to encode - * @name: The plaintext name - * @length: The length of the plaintext - * @encoded_name: The encypted name - * - * Encrypts and encodes a filename into something that constitutes a - * valid filename for a filesystem, with printable characters. - * - * We assume that we have a properly initialized crypto context, - * pointed to by crypt_stat->tfm. - * - * TODO: Implement filename decoding and decryption here, in place of - * memcpy. We are keeping the framework around for now to (1) - * facilitate testing of the components needed to implement filename - * encryption and (2) to provide a code base from which other - * developers in the community can easily implement this feature. - * - * Returns the length of encoded filename; negative if error - */ -int -ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat, - const char *name, int length, char **encoded_name) -{ - int error = 0; - - (*encoded_name) = kmalloc(length + 2, GFP_KERNEL); - if (!(*encoded_name)) { - error = -ENOMEM; - goto out; - } - /* TODO: Filename encryption is a scheduled feature for a - * future version of eCryptfs. This function is here only for - * the purpose of providing a framework for other developers - * to easily implement filename encryption. Hint: Replace this - * memcpy() with a call to encrypt and encode the - * filename, the set the length accordingly. */ - memcpy((void *)(*encoded_name), (void *)name, length); - (*encoded_name)[length] = '\0'; - error = length + 1; -out: - return error; -} - -/** - * ecryptfs_decode_filename - converts the cipher text name to plaintext - * @crypt_stat: The crypt_stat struct associated with the file - * @name: The filename in cipher text - * @length: The length of the cipher text name - * @decrypted_name: The plaintext name - * - * Decodes and decrypts the filename. - * - * We assume that we have a properly initialized crypto context, - * pointed to by crypt_stat->tfm. - * - * TODO: Implement filename decoding and decryption here, in place of - * memcpy. We are keeping the framework around for now to (1) - * facilitate testing of the components needed to implement filename - * encryption and (2) to provide a code base from which other - * developers in the community can easily implement this feature. - * - * Returns the length of decoded filename; negative if error - */ -int -ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat, - const char *name, int length, char **decrypted_name) -{ - int error = 0; - - (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL); - if (!(*decrypted_name)) { - error = -ENOMEM; - goto out; - } - /* TODO: Filename encryption is a scheduled feature for a - * future version of eCryptfs. This function is here only for - * the purpose of providing a framework for other developers - * to easily implement filename encryption. Hint: Replace this - * memcpy() with a call to decode and decrypt the - * filename, the set the length accordingly. */ - memcpy((void *)(*decrypted_name), (void *)name, length); - (*decrypted_name)[length + 1] = '\0'; /* Only for convenience - * in printing out the - * string in debug - * messages */ - error = length; -out: - return error; -} - -/** - * ecryptfs_process_cipher - Perform cipher initialization. - * @tfm: Crypto context set by this function - * @key_tfm: Crypto context for key material, set by this function - * @cipher_name: Name of the cipher. - * @key_size: Size of the key in bytes. - * - * Returns zero on success. Any crypto_tfm structs allocated here - * should be released by other functions, such as on a superblock put - * event, regardless of whether this function succeeds for fails. - */ -int -ecryptfs_process_cipher(struct crypto_tfm **tfm, struct crypto_tfm **key_tfm, - char *cipher_name, size_t key_size) -{ - char dummy_key[ECRYPTFS_MAX_KEY_BYTES]; - int rc; - - *tfm = *key_tfm = NULL; - if (key_size > ECRYPTFS_MAX_KEY_BYTES) { - rc = -EINVAL; - printk(KERN_ERR "Requested key size is [%Zd] bytes; maximum " - "allowable is [%d]\n", key_size, ECRYPTFS_MAX_KEY_BYTES); - goto out; - } - *tfm = crypto_alloc_tfm(cipher_name, (ECRYPTFS_DEFAULT_CHAINING_MODE - | CRYPTO_TFM_REQ_WEAK_KEY)); - if (!(*tfm)) { - rc = -EINVAL; - printk(KERN_ERR "Unable to allocate crypto cipher with name " - "[%s]\n", cipher_name); - goto out; - } - *key_tfm = crypto_alloc_tfm(cipher_name, CRYPTO_TFM_REQ_WEAK_KEY); - if (!(*key_tfm)) { - rc = -EINVAL; - printk(KERN_ERR "Unable to allocate crypto cipher with name " - "[%s]\n", cipher_name); - goto out; - } - if (key_size < crypto_tfm_alg_min_keysize(*tfm)) { - rc = -EINVAL; - printk(KERN_ERR "Request key size is [%Zd]; minimum key size " - "supported by cipher [%s] is [%d]\n", key_size, - cipher_name, crypto_tfm_alg_min_keysize(*tfm)); - goto out; - } - if (key_size < crypto_tfm_alg_min_keysize(*key_tfm)) { - rc = -EINVAL; - printk(KERN_ERR "Request key size is [%Zd]; minimum key size " - "supported by cipher [%s] is [%d]\n", key_size, - cipher_name, crypto_tfm_alg_min_keysize(*key_tfm)); - goto out; - } - if (key_size > crypto_tfm_alg_max_keysize(*tfm)) { - rc = -EINVAL; - printk(KERN_ERR "Request key size is [%Zd]; maximum key size " - "supported by cipher [%s] is [%d]\n", key_size, - cipher_name, crypto_tfm_alg_min_keysize(*tfm)); - goto out; - } - if (key_size > crypto_tfm_alg_max_keysize(*key_tfm)) { - rc = -EINVAL; - printk(KERN_ERR "Request key size is [%Zd]; maximum key size " - "supported by cipher [%s] is [%d]\n", key_size, - cipher_name, crypto_tfm_alg_min_keysize(*key_tfm)); - goto out; - } - get_random_bytes(dummy_key, key_size); - rc = crypto_cipher_setkey(*tfm, dummy_key, key_size); - if (rc) { - printk(KERN_ERR "Error attempting to set key of size [%Zd] for " - "cipher [%s]; rc = [%d]\n", key_size, cipher_name, rc); - rc = -EINVAL; - goto out; - } - rc = crypto_cipher_setkey(*key_tfm, dummy_key, key_size); - if (rc) { - printk(KERN_ERR "Error attempting to set key of size [%Zd] for " - "cipher [%s]; rc = [%d]\n", key_size, cipher_name, rc); - rc = -EINVAL; - goto out; - } -out: - return rc; -} diff --git a/trunk/fs/ecryptfs/debug.c b/trunk/fs/ecryptfs/debug.c deleted file mode 100644 index 61f8e894284f..000000000000 --- a/trunk/fs/ecryptfs/debug.c +++ /dev/null @@ -1,123 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * Functions only useful for debugging. - * - * Copyright (C) 2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * - * 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 "ecryptfs_kernel.h" - -/** - * ecryptfs_dump_auth_tok - debug function to print auth toks - * - * This function will print the contents of an ecryptfs authentication - * token. - */ -void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok) -{ - char salt[ECRYPTFS_SALT_SIZE * 2 + 1]; - char sig[ECRYPTFS_SIG_SIZE_HEX + 1]; - - ecryptfs_printk(KERN_DEBUG, "Auth tok at mem loc [%p]:\n", - auth_tok); - if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PRIVATE_KEY)) { - ecryptfs_printk(KERN_DEBUG, " * private key type\n"); - ecryptfs_printk(KERN_DEBUG, " * (NO PRIVATE KEY SUPPORT " - "IN ECRYPTFS VERSION 0.1)\n"); - } else { - ecryptfs_printk(KERN_DEBUG, " * passphrase type\n"); - ecryptfs_to_hex(salt, auth_tok->token.password.salt, - ECRYPTFS_SALT_SIZE); - salt[ECRYPTFS_SALT_SIZE * 2] = '\0'; - ecryptfs_printk(KERN_DEBUG, " * salt = [%s]\n", salt); - if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags, - ECRYPTFS_PERSISTENT_PASSWORD)) { - ecryptfs_printk(KERN_DEBUG, " * persistent\n"); - } - memcpy(sig, auth_tok->token.password.signature, - ECRYPTFS_SIG_SIZE_HEX); - sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; - ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig); - } - ecryptfs_printk(KERN_DEBUG, " * session_key.flags = [0x%x]\n", - auth_tok->session_key.flags); - if (auth_tok->session_key.flags - & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT) - ecryptfs_printk(KERN_DEBUG, - " * Userspace decrypt request set\n"); - if (auth_tok->session_key.flags - & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT) - ecryptfs_printk(KERN_DEBUG, - " * Userspace encrypt request set\n"); - if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) { - ecryptfs_printk(KERN_DEBUG, " * Contains decrypted key\n"); - ecryptfs_printk(KERN_DEBUG, - " * session_key.decrypted_key_size = [0x%x]\n", - auth_tok->session_key.decrypted_key_size); - ecryptfs_printk(KERN_DEBUG, " * Decrypted session key " - "dump:\n"); - if (ecryptfs_verbosity > 0) - ecryptfs_dump_hex(auth_tok->session_key.decrypted_key, - ECRYPTFS_DEFAULT_KEY_BYTES); - } - if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_ENCRYPTED_KEY) { - ecryptfs_printk(KERN_DEBUG, " * Contains encrypted key\n"); - ecryptfs_printk(KERN_DEBUG, - " * session_key.encrypted_key_size = [0x%x]\n", - auth_tok->session_key.encrypted_key_size); - ecryptfs_printk(KERN_DEBUG, " * Encrypted session key " - "dump:\n"); - if (ecryptfs_verbosity > 0) - ecryptfs_dump_hex(auth_tok->session_key.encrypted_key, - auth_tok->session_key. - encrypted_key_size); - } -} - -/** - * ecryptfs_dump_hex - debug hex printer - * @data: string of bytes to be printed - * @bytes: number of bytes to print - * - * Dump hexadecimal representation of char array - */ -void ecryptfs_dump_hex(char *data, int bytes) -{ - int i = 0; - int add_newline = 1; - - if (ecryptfs_verbosity < 1) - return; - if (bytes != 0) { - printk(KERN_DEBUG "0x%.2x.", (unsigned char)data[i]); - i++; - } - while (i < bytes) { - printk("0x%.2x.", (unsigned char)data[i]); - i++; - if (i % 16 == 0) { - printk("\n"); - add_newline = 0; - } else - add_newline = 1; - } - if (add_newline) - printk("\n"); -} - diff --git a/trunk/fs/ecryptfs/dentry.c b/trunk/fs/ecryptfs/dentry.c deleted file mode 100644 index f0d2a433242b..000000000000 --- a/trunk/fs/ecryptfs/dentry.c +++ /dev/null @@ -1,87 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * - * Copyright (C) 1997-2003 Erez Zadok - * Copyright (C) 2001-2003 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * - * 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 "ecryptfs_kernel.h" - -/** - * ecryptfs_d_revalidate - revalidate an ecryptfs dentry - * @dentry: The ecryptfs dentry - * @nd: The associated nameidata - * - * Called when the VFS needs to revalidate a dentry. This - * is called whenever a name lookup finds a dentry in the - * dcache. Most filesystems leave this as NULL, because all their - * dentries in the dcache are valid. - * - * Returns 1 if valid, 0 otherwise. - * - */ -static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) -{ - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); - struct dentry *dentry_save; - struct vfsmount *vfsmount_save; - int rc = 1; - - if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) - goto out; - dentry_save = nd->dentry; - vfsmount_save = nd->mnt; - nd->dentry = lower_dentry; - nd->mnt = lower_mnt; - rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd); - nd->dentry = dentry_save; - nd->mnt = vfsmount_save; -out: - return rc; -} - -struct kmem_cache *ecryptfs_dentry_info_cache; - -/** - * ecryptfs_d_release - * @dentry: The ecryptfs dentry - * - * Called when a dentry is really deallocated. - */ -static void ecryptfs_d_release(struct dentry *dentry) -{ - struct dentry *lower_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (ecryptfs_dentry_to_private(dentry)) - kmem_cache_free(ecryptfs_dentry_info_cache, - ecryptfs_dentry_to_private(dentry)); - if (lower_dentry) - dput(lower_dentry); - return; -} - -struct dentry_operations ecryptfs_dops = { - .d_revalidate = ecryptfs_d_revalidate, - .d_release = ecryptfs_d_release, -}; diff --git a/trunk/fs/ecryptfs/ecryptfs_kernel.h b/trunk/fs/ecryptfs/ecryptfs_kernel.h deleted file mode 100644 index 872c9958531a..000000000000 --- a/trunk/fs/ecryptfs/ecryptfs_kernel.h +++ /dev/null @@ -1,482 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * Kernel declarations. - * - * Copyright (C) 1997-2003 Erez Zadok - * Copyright (C) 2001-2003 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#ifndef ECRYPTFS_KERNEL_H -#define ECRYPTFS_KERNEL_H - -#include -#include -#include - -/* Version verification for shared data structures w/ userspace */ -#define ECRYPTFS_VERSION_MAJOR 0x00 -#define ECRYPTFS_VERSION_MINOR 0x04 -#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x01 -/* These flags indicate which features are supported by the kernel - * module; userspace tools such as the mount helper read - * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine - * how to behave. */ -#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001 -#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002 -#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004 -#define ECRYPTFS_VERSIONING_POLICY 0x00000008 -#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \ - | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH) - -#define ECRYPTFS_MAX_PASSWORD_LENGTH 64 -#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH -#define ECRYPTFS_SALT_SIZE 8 -#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2) -/* The original signature size is only for what is stored on disk; all - * in-memory representations are expanded hex, so it better adapted to - * be passed around or referenced on the command line */ -#define ECRYPTFS_SIG_SIZE 8 -#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2) -#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX -#define ECRYPTFS_MAX_KEY_BYTES 64 -#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512 -#define ECRYPTFS_DEFAULT_IV_BYTES 16 -#define ECRYPTFS_FILE_VERSION 0x01 -#define ECRYPTFS_DEFAULT_HEADER_EXTENT_SIZE 8192 -#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096 -#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192 - -#define RFC2440_CIPHER_DES3_EDE 0x02 -#define RFC2440_CIPHER_CAST_5 0x03 -#define RFC2440_CIPHER_BLOWFISH 0x04 -#define RFC2440_CIPHER_AES_128 0x07 -#define RFC2440_CIPHER_AES_192 0x08 -#define RFC2440_CIPHER_AES_256 0x09 -#define RFC2440_CIPHER_TWOFISH 0x0a -#define RFC2440_CIPHER_CAST_6 0x0b - -#define ECRYPTFS_SET_FLAG(flag_bit_vector, flag) (flag_bit_vector |= (flag)) -#define ECRYPTFS_CLEAR_FLAG(flag_bit_vector, flag) (flag_bit_vector &= ~(flag)) -#define ECRYPTFS_CHECK_FLAG(flag_bit_vector, flag) (flag_bit_vector & (flag)) - -/** - * For convenience, we may need to pass around the encrypted session - * key between kernel and userspace because the authentication token - * may not be extractable. For example, the TPM may not release the - * private key, instead requiring the encrypted data and returning the - * decrypted data. - */ -struct ecryptfs_session_key { -#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001 -#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002 -#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004 -#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008 - u32 flags; - u32 encrypted_key_size; - u32 decrypted_key_size; - u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; - u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES]; -}; - -struct ecryptfs_password { - u32 password_bytes; - s32 hash_algo; - u32 hash_iterations; - u32 session_key_encryption_key_bytes; -#define ECRYPTFS_PERSISTENT_PASSWORD 0x01 -#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02 - u32 flags; - /* Iterated-hash concatenation of salt and passphrase */ - u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; - u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; - /* Always in expanded hex */ - u8 salt[ECRYPTFS_SALT_SIZE]; -}; - -enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY}; - -/* May be a password or a private key */ -struct ecryptfs_auth_tok { - u16 version; /* 8-bit major and 8-bit minor */ - u16 token_type; - u32 flags; - struct ecryptfs_session_key session_key; - u8 reserved[32]; - union { - struct ecryptfs_password password; - /* Private key is in future eCryptfs releases */ - } token; -} __attribute__ ((packed)); - -void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok); -extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size); -extern void ecryptfs_from_hex(char *dst, char *src, int dst_size); - -struct ecryptfs_key_record { - unsigned char type; - size_t enc_key_size; - unsigned char sig[ECRYPTFS_SIG_SIZE]; - unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; -}; - -struct ecryptfs_auth_tok_list { - struct ecryptfs_auth_tok *auth_tok; - struct list_head list; -}; - -struct ecryptfs_crypt_stat; -struct ecryptfs_mount_crypt_stat; - -struct ecryptfs_page_crypt_context { - struct page *page; -#define ECRYPTFS_PREPARE_COMMIT_MODE 0 -#define ECRYPTFS_WRITEPAGE_MODE 1 - unsigned int mode; - union { - struct file *lower_file; - struct writeback_control *wbc; - } param; -}; - -static inline struct ecryptfs_auth_tok * -ecryptfs_get_key_payload_data(struct key *key) -{ - return (struct ecryptfs_auth_tok *) - (((struct user_key_payload*)key->payload.data)->data); -} - -#define ECRYPTFS_SUPER_MAGIC 0xf15f -#define ECRYPTFS_MAX_KEYSET_SIZE 1024 -#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32 -#define ECRYPTFS_MAX_NUM_ENC_KEYS 64 -#define ECRYPTFS_MAX_NUM_KEYSIGS 2 /* TODO: Make this a linked list */ -#define ECRYPTFS_MAX_IV_BYTES 16 /* 128 bits */ -#define ECRYPTFS_SALT_BYTES 2 -#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5 -#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8 /* 4*2 */ -#define ECRYPTFS_FILE_SIZE_BYTES 8 -#define ECRYPTFS_DEFAULT_CIPHER "aes" -#define ECRYPTFS_DEFAULT_KEY_BYTES 16 -#define ECRYPTFS_DEFAULT_CHAINING_MODE CRYPTO_TFM_MODE_CBC -#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C -#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED -#define MD5_DIGEST_SIZE 16 - -/** - * This is the primary struct associated with each encrypted file. - * - * TODO: cache align/pack? - */ -struct ecryptfs_crypt_stat { -#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001 -#define ECRYPTFS_POLICY_APPLIED 0x00000002 -#define ECRYPTFS_NEW_FILE 0x00000004 -#define ECRYPTFS_ENCRYPTED 0x00000008 -#define ECRYPTFS_SECURITY_WARNING 0x00000010 -#define ECRYPTFS_ENABLE_HMAC 0x00000020 -#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040 -#define ECRYPTFS_KEY_VALID 0x00000080 - u32 flags; - unsigned int file_version; - size_t iv_bytes; - size_t num_keysigs; - size_t header_extent_size; - size_t num_header_extents_at_front; - size_t extent_size; /* Data extent size; default is 4096 */ - size_t key_size; - size_t extent_shift; - unsigned int extent_mask; - struct ecryptfs_mount_crypt_stat *mount_crypt_stat; - struct crypto_tfm *tfm; - struct crypto_tfm *md5_tfm; /* Crypto context for generating - * the initialization vectors */ - unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE]; - unsigned char key[ECRYPTFS_MAX_KEY_BYTES]; - unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES]; - unsigned char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX]; - struct mutex cs_tfm_mutex; - struct mutex cs_md5_tfm_mutex; - struct mutex cs_mutex; -}; - -/* inode private data. */ -struct ecryptfs_inode_info { - struct inode vfs_inode; - struct inode *wii_inode; - struct ecryptfs_crypt_stat crypt_stat; -}; - -/* dentry private data. Each dentry must keep track of a lower - * vfsmount too. */ -struct ecryptfs_dentry_info { - struct dentry *wdi_dentry; - struct vfsmount *lower_mnt; - struct ecryptfs_crypt_stat *crypt_stat; -}; - -/** - * This struct is to enable a mount-wide passphrase/salt combo. This - * is more or less a stopgap to provide similar functionality to other - * crypto filesystems like EncFS or CFS until full policy support is - * implemented in eCryptfs. - */ -struct ecryptfs_mount_crypt_stat { - /* Pointers to memory we do not own, do not free these */ -#define ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED 0x00000001 - u32 flags; - struct ecryptfs_auth_tok *global_auth_tok; - struct key *global_auth_tok_key; - size_t global_default_cipher_key_size; - struct crypto_tfm *global_key_tfm; - struct mutex global_key_tfm_mutex; - unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE - + 1]; - unsigned char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1]; -}; - -/* superblock private data. */ -struct ecryptfs_sb_info { - struct super_block *wsi_sb; - struct ecryptfs_mount_crypt_stat mount_crypt_stat; -}; - -/* file private data. */ -struct ecryptfs_file_info { - struct file *wfi_file; - struct ecryptfs_crypt_stat *crypt_stat; -}; - -/* auth_tok <=> encrypted_session_key mappings */ -struct ecryptfs_auth_tok_list_item { - unsigned char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES]; - struct list_head list; - struct ecryptfs_auth_tok auth_tok; -}; - -static inline struct ecryptfs_file_info * -ecryptfs_file_to_private(struct file *file) -{ - return (struct ecryptfs_file_info *)file->private_data; -} - -static inline void -ecryptfs_set_file_private(struct file *file, - struct ecryptfs_file_info *file_info) -{ - file->private_data = file_info; -} - -static inline struct file *ecryptfs_file_to_lower(struct file *file) -{ - return ((struct ecryptfs_file_info *)file->private_data)->wfi_file; -} - -static inline void -ecryptfs_set_file_lower(struct file *file, struct file *lower_file) -{ - ((struct ecryptfs_file_info *)file->private_data)->wfi_file = - lower_file; -} - -static inline struct ecryptfs_inode_info * -ecryptfs_inode_to_private(struct inode *inode) -{ - return container_of(inode, struct ecryptfs_inode_info, vfs_inode); -} - -static inline struct inode *ecryptfs_inode_to_lower(struct inode *inode) -{ - return ecryptfs_inode_to_private(inode)->wii_inode; -} - -static inline void -ecryptfs_set_inode_lower(struct inode *inode, struct inode *lower_inode) -{ - ecryptfs_inode_to_private(inode)->wii_inode = lower_inode; -} - -static inline struct ecryptfs_sb_info * -ecryptfs_superblock_to_private(struct super_block *sb) -{ - return (struct ecryptfs_sb_info *)sb->s_fs_info; -} - -static inline void -ecryptfs_set_superblock_private(struct super_block *sb, - struct ecryptfs_sb_info *sb_info) -{ - sb->s_fs_info = sb_info; -} - -static inline struct super_block * -ecryptfs_superblock_to_lower(struct super_block *sb) -{ - return ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb; -} - -static inline void -ecryptfs_set_superblock_lower(struct super_block *sb, - struct super_block *lower_sb) -{ - ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb = lower_sb; -} - -static inline struct ecryptfs_dentry_info * -ecryptfs_dentry_to_private(struct dentry *dentry) -{ - return (struct ecryptfs_dentry_info *)dentry->d_fsdata; -} - -static inline void -ecryptfs_set_dentry_private(struct dentry *dentry, - struct ecryptfs_dentry_info *dentry_info) -{ - dentry->d_fsdata = dentry_info; -} - -static inline struct dentry * -ecryptfs_dentry_to_lower(struct dentry *dentry) -{ - return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry; -} - -static inline void -ecryptfs_set_dentry_lower(struct dentry *dentry, struct dentry *lower_dentry) -{ - ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry = - lower_dentry; -} - -static inline struct vfsmount * -ecryptfs_dentry_to_lower_mnt(struct dentry *dentry) -{ - return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt; -} - -static inline void -ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt) -{ - ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt = - lower_mnt; -} - -#define ecryptfs_printk(type, fmt, arg...) \ - __ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg); -void __ecryptfs_printk(const char *fmt, ...); - -extern const struct file_operations ecryptfs_main_fops; -extern const struct file_operations ecryptfs_dir_fops; -extern struct inode_operations ecryptfs_main_iops; -extern struct inode_operations ecryptfs_dir_iops; -extern struct inode_operations ecryptfs_symlink_iops; -extern struct super_operations ecryptfs_sops; -extern struct dentry_operations ecryptfs_dops; -extern struct address_space_operations ecryptfs_aops; -extern int ecryptfs_verbosity; - -extern struct kmem_cache *ecryptfs_auth_tok_list_item_cache; -extern struct kmem_cache *ecryptfs_file_info_cache; -extern struct kmem_cache *ecryptfs_dentry_info_cache; -extern struct kmem_cache *ecryptfs_inode_info_cache; -extern struct kmem_cache *ecryptfs_sb_info_cache; -extern struct kmem_cache *ecryptfs_header_cache_0; -extern struct kmem_cache *ecryptfs_header_cache_1; -extern struct kmem_cache *ecryptfs_header_cache_2; -extern struct kmem_cache *ecryptfs_lower_page_cache; - -int ecryptfs_interpose(struct dentry *hidden_dentry, - struct dentry *this_dentry, struct super_block *sb, - int flag); -int ecryptfs_fill_zeros(struct file *file, loff_t new_length); -int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat, - const char *name, int length, - char **decrypted_name); -int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat, - const char *name, int length, - char **encoded_name); -struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry); -void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src); -void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src); -void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src); -void ecryptfs_dump_hex(char *data, int bytes); -int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, - int sg_size); -int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat); -void ecryptfs_rotate_iv(unsigned char *iv); -void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat); -void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat); -void ecryptfs_destruct_mount_crypt_stat( - struct ecryptfs_mount_crypt_stat *mount_crypt_stat); -int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat); -int ecryptfs_write_inode_size_to_header(struct file *lower_file, - struct inode *lower_inode, - struct inode *inode); -int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode, - struct file *lower_file, - unsigned long lower_page_index, int byte_offset, - int region_bytes); -int -ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode, - struct file *lower_file, int byte_offset, - int region_size); -int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode, - struct file *lower_file); -int ecryptfs_do_readpage(struct file *file, struct page *page, - pgoff_t lower_page_index); -int ecryptfs_grab_and_map_lower_page(struct page **lower_page, - char **lower_virt, - struct inode *lower_inode, - unsigned long lower_page_index); -int ecryptfs_writepage_and_release_lower_page(struct page *lower_page, - struct inode *lower_inode, - struct writeback_control *wbc); -int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx); -int ecryptfs_decrypt_page(struct file *file, struct page *page); -int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, - struct file *lower_file); -int ecryptfs_write_headers_virt(char *page_virt, - struct ecryptfs_crypt_stat *crypt_stat, - struct dentry *ecryptfs_dentry); -int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, - struct file *lower_file); -int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry); -int contains_ecryptfs_marker(char *data); -int ecryptfs_read_header_region(char *data, struct dentry *dentry, - struct vfsmount *mnt); -u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat); -int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code); -void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat); -int ecryptfs_generate_key_packet_set(char *dest_base, - struct ecryptfs_crypt_stat *crypt_stat, - struct dentry *ecryptfs_dentry, - size_t *len, size_t max); -int process_request_key_err(long err_code); -int -ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, - unsigned char *src, struct dentry *ecryptfs_dentry); -int ecryptfs_truncate(struct dentry *dentry, loff_t new_length); -int -ecryptfs_process_cipher(struct crypto_tfm **tfm, struct crypto_tfm **key_tfm, - char *cipher_name, size_t key_size); -int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode); -int ecryptfs_inode_set(struct inode *inode, void *lower_inode); -void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode); - -#endif /* #ifndef ECRYPTFS_KERNEL_H */ diff --git a/trunk/fs/ecryptfs/file.c b/trunk/fs/ecryptfs/file.c deleted file mode 100644 index c8550c9f9cd2..000000000000 --- a/trunk/fs/ecryptfs/file.c +++ /dev/null @@ -1,440 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * - * Copyright (C) 1997-2004 Erez Zadok - * Copyright (C) 2001-2004 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * Michael C. Thompson - * - * 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 "ecryptfs_kernel.h" - -/** - * ecryptfs_llseek - * @file: File we are seeking in - * @offset: The offset to seek to - * @origin: 2 - offset from i_size; 1 - offset from f_pos - * - * Returns the position we have seeked to, or negative on error - */ -static loff_t ecryptfs_llseek(struct file *file, loff_t offset, int origin) -{ - loff_t rv; - loff_t new_end_pos; - int rc; - int expanding_file = 0; - struct inode *inode = file->f_mapping->host; - - /* If our offset is past the end of our file, we're going to - * need to grow it so we have a valid length of 0's */ - new_end_pos = offset; - switch (origin) { - case 2: - new_end_pos += i_size_read(inode); - expanding_file = 1; - break; - case 1: - new_end_pos += file->f_pos; - if (new_end_pos > i_size_read(inode)) { - ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) " - "> i_size_read(inode)(=[0x%.16x])\n", - new_end_pos, i_size_read(inode)); - expanding_file = 1; - } - break; - default: - if (new_end_pos > i_size_read(inode)) { - ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) " - "> i_size_read(inode)(=[0x%.16x])\n", - new_end_pos, i_size_read(inode)); - expanding_file = 1; - } - } - ecryptfs_printk(KERN_DEBUG, "new_end_pos = [0x%.16x]\n", new_end_pos); - if (expanding_file) { - rc = ecryptfs_truncate(file->f_dentry, new_end_pos); - if (rc) { - rv = rc; - ecryptfs_printk(KERN_ERR, "Error on attempt to " - "truncate to (higher) offset [0x%.16x];" - " rc = [%d]\n", new_end_pos, rc); - goto out; - } - } - rv = generic_file_llseek(file, offset, origin); -out: - return rv; -} - -/** - * ecryptfs_read_update_atime - * - * generic_file_read updates the atime of upper layer inode. But, it - * doesn't give us a chance to update the atime of the lower layer - * inode. This function is a wrapper to generic_file_read. It - * updates the atime of the lower level inode if generic_file_read - * returns without any errors. This is to be used only for file reads. - * The function to be used for directory reads is ecryptfs_read. - */ -static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, - const struct iovec *iov, - unsigned long nr_segs, loff_t pos) -{ - int rc; - struct dentry *lower_dentry; - struct vfsmount *lower_vfsmount; - struct file *file = iocb->ki_filp; - - rc = generic_file_aio_read(iocb, iov, nr_segs, pos); - /* - * Even though this is a async interface, we need to wait - * for IO to finish to update atime - */ - if (-EIOCBQUEUED == rc) - rc = wait_on_sync_kiocb(iocb); - if (rc >= 0) { - lower_dentry = ecryptfs_dentry_to_lower(file->f_dentry); - lower_vfsmount = ecryptfs_dentry_to_lower_mnt(file->f_dentry); - touch_atime(lower_vfsmount, lower_dentry); - } - return rc; -} - -struct ecryptfs_getdents_callback { - void *dirent; - struct dentry *dentry; - filldir_t filldir; - int err; - int filldir_called; - int entries_written; -}; - -/* Inspired by generic filldir in fs/readir.c */ -static int -ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset, - u64 ino, unsigned int d_type) -{ - struct ecryptfs_crypt_stat *crypt_stat; - struct ecryptfs_getdents_callback *buf = - (struct ecryptfs_getdents_callback *)dirent; - int rc; - int decoded_length; - char *decoded_name; - - crypt_stat = ecryptfs_dentry_to_private(buf->dentry)->crypt_stat; - buf->filldir_called++; - decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen, - &decoded_name); - if (decoded_length < 0) { - rc = decoded_length; - goto out; - } - rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset, - ino, d_type); - kfree(decoded_name); - if (rc >= 0) - buf->entries_written++; -out: - return rc; -} - -/** - * ecryptfs_readdir - * @file: The ecryptfs file struct - * @dirent: Directory entry - * @filldir: The filldir callback function - */ -static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir) -{ - int rc; - struct file *lower_file; - struct inode *inode; - struct ecryptfs_getdents_callback buf; - - lower_file = ecryptfs_file_to_lower(file); - lower_file->f_pos = file->f_pos; - inode = file->f_dentry->d_inode; - memset(&buf, 0, sizeof(buf)); - buf.dirent = dirent; - buf.dentry = file->f_dentry; - buf.filldir = filldir; -retry: - buf.filldir_called = 0; - buf.entries_written = 0; - buf.err = 0; - rc = vfs_readdir(lower_file, ecryptfs_filldir, (void *)&buf); - if (buf.err) - rc = buf.err; - if (buf.filldir_called && !buf.entries_written) - goto retry; - file->f_pos = lower_file->f_pos; - if (rc >= 0) - ecryptfs_copy_attr_atime(inode, lower_file->f_dentry->d_inode); - return rc; -} - -struct kmem_cache *ecryptfs_file_info_cache; - -/** - * ecryptfs_open - * @inode: inode speciying file to open - * @file: Structure to return filled in - * - * Opens the file specified by inode. - * - * Returns zero on success; non-zero otherwise - */ -static int ecryptfs_open(struct inode *inode, struct file *file) -{ - int rc = 0; - struct ecryptfs_crypt_stat *crypt_stat = NULL; - struct ecryptfs_mount_crypt_stat *mount_crypt_stat; - struct dentry *ecryptfs_dentry = file->f_dentry; - /* Private value of ecryptfs_dentry allocated in - * ecryptfs_lookup() */ - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); - struct inode *lower_inode = NULL; - struct file *lower_file = NULL; - struct vfsmount *lower_mnt; - struct ecryptfs_file_info *file_info; - int lower_flags; - - /* Released in ecryptfs_release or end of function if failure */ - file_info = kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL); - ecryptfs_set_file_private(file, file_info); - if (!file_info) { - ecryptfs_printk(KERN_ERR, - "Error attempting to allocate memory\n"); - rc = -ENOMEM; - goto out; - } - memset(file_info, 0, sizeof(*file_info)); - lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); - crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - mount_crypt_stat = &ecryptfs_superblock_to_private( - ecryptfs_dentry->d_sb)->mount_crypt_stat; - mutex_lock(&crypt_stat->cs_mutex); - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) { - ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n"); - /* Policy code enabled in future release */ - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED); - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); - } - mutex_unlock(&crypt_stat->cs_mutex); - /* This mntget & dget is undone via fput when the file is released */ - dget(lower_dentry); - lower_flags = file->f_flags; - if ((lower_flags & O_ACCMODE) == O_WRONLY) - lower_flags = (lower_flags & O_ACCMODE) | O_RDWR; - if (file->f_flags & O_APPEND) - lower_flags &= ~O_APPEND; - lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); - mntget(lower_mnt); - /* Corresponding fput() in ecryptfs_release() */ - lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags); - if (IS_ERR(lower_file)) { - rc = PTR_ERR(lower_file); - ecryptfs_printk(KERN_ERR, "Error opening lower file\n"); - goto out_puts; - } - ecryptfs_set_file_lower(file, lower_file); - /* Isn't this check the same as the one in lookup? */ - lower_inode = lower_dentry->d_inode; - if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { - ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); - rc = 0; - goto out; - } - mutex_lock(&crypt_stat->cs_mutex); - if (i_size_read(lower_inode) < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { - if (!(mount_crypt_stat->flags - & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { - rc = -EIO; - printk(KERN_WARNING "Attempt to read file that is " - "not in a valid eCryptfs format, and plaintext " - "passthrough mode is not enabled; returning " - "-EIO\n"); - mutex_unlock(&crypt_stat->cs_mutex); - goto out_puts; - } - crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); - rc = 0; - mutex_unlock(&crypt_stat->cs_mutex); - goto out; - } else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, - ECRYPTFS_POLICY_APPLIED) - || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, - ECRYPTFS_KEY_VALID)) { - rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file); - if (rc) { - ecryptfs_printk(KERN_DEBUG, - "Valid headers not found\n"); - if (!(mount_crypt_stat->flags - & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { - rc = -EIO; - printk(KERN_WARNING "Attempt to read file that " - "is not in a valid eCryptfs format, " - "and plaintext passthrough mode is not " - "enabled; returning -EIO\n"); - mutex_unlock(&crypt_stat->cs_mutex); - goto out_puts; - } - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, - ECRYPTFS_ENCRYPTED); - rc = 0; - mutex_unlock(&crypt_stat->cs_mutex); - goto out; - } - } - mutex_unlock(&crypt_stat->cs_mutex); - ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] " - "size: [0x%.16x]\n", inode, inode->i_ino, - i_size_read(inode)); - ecryptfs_set_file_lower(file, lower_file); - goto out; -out_puts: - mntput(lower_mnt); - dput(lower_dentry); - kmem_cache_free(ecryptfs_file_info_cache, - ecryptfs_file_to_private(file)); -out: - return rc; -} - -static int ecryptfs_flush(struct file *file, fl_owner_t td) -{ - int rc = 0; - struct file *lower_file = NULL; - - lower_file = ecryptfs_file_to_lower(file); - if (lower_file->f_op && lower_file->f_op->flush) - rc = lower_file->f_op->flush(lower_file, td); - return rc; -} - -static int ecryptfs_release(struct inode *inode, struct file *file) -{ - struct file *lower_file = ecryptfs_file_to_lower(file); - struct ecryptfs_file_info *file_info = ecryptfs_file_to_private(file); - struct inode *lower_inode = ecryptfs_inode_to_lower(inode); - - fput(lower_file); - inode->i_blocks = lower_inode->i_blocks; - kmem_cache_free(ecryptfs_file_info_cache, file_info); - return 0; -} - -static int -ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync) -{ - struct file *lower_file = ecryptfs_file_to_lower(file); - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - struct inode *lower_inode = lower_dentry->d_inode; - int rc = -EINVAL; - - if (lower_inode->i_fop->fsync) { - mutex_lock(&lower_inode->i_mutex); - rc = lower_inode->i_fop->fsync(lower_file, lower_dentry, - datasync); - mutex_unlock(&lower_inode->i_mutex); - } - return rc; -} - -static int ecryptfs_fasync(int fd, struct file *file, int flag) -{ - int rc = 0; - struct file *lower_file = NULL; - - lower_file = ecryptfs_file_to_lower(file); - if (lower_file->f_op && lower_file->f_op->fasync) - rc = lower_file->f_op->fasync(fd, lower_file, flag); - return rc; -} - -static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos, - size_t count, read_actor_t actor, void *target) -{ - struct file *lower_file = NULL; - int rc = -EINVAL; - - lower_file = ecryptfs_file_to_lower(file); - if (lower_file->f_op && lower_file->f_op->sendfile) - rc = lower_file->f_op->sendfile(lower_file, ppos, count, - actor, target); - - return rc; -} - -static int ecryptfs_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); - -const struct file_operations ecryptfs_dir_fops = { - .readdir = ecryptfs_readdir, - .ioctl = ecryptfs_ioctl, - .mmap = generic_file_mmap, - .open = ecryptfs_open, - .flush = ecryptfs_flush, - .release = ecryptfs_release, - .fsync = ecryptfs_fsync, - .fasync = ecryptfs_fasync, - .sendfile = ecryptfs_sendfile, -}; - -const struct file_operations ecryptfs_main_fops = { - .llseek = ecryptfs_llseek, - .read = do_sync_read, - .aio_read = ecryptfs_read_update_atime, - .write = do_sync_write, - .aio_write = generic_file_aio_write, - .readdir = ecryptfs_readdir, - .ioctl = ecryptfs_ioctl, - .mmap = generic_file_mmap, - .open = ecryptfs_open, - .flush = ecryptfs_flush, - .release = ecryptfs_release, - .fsync = ecryptfs_fsync, - .fasync = ecryptfs_fasync, - .sendfile = ecryptfs_sendfile, -}; - -static int -ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int rc = 0; - struct file *lower_file = NULL; - - if (ecryptfs_file_to_private(file)) - lower_file = ecryptfs_file_to_lower(file); - if (lower_file && lower_file->f_op && lower_file->f_op->ioctl) - rc = lower_file->f_op->ioctl(ecryptfs_inode_to_lower(inode), - lower_file, cmd, arg); - else - rc = -ENOTTY; - return rc; -} diff --git a/trunk/fs/ecryptfs/inode.c b/trunk/fs/ecryptfs/inode.c deleted file mode 100644 index efdd2b7b62d7..000000000000 --- a/trunk/fs/ecryptfs/inode.c +++ /dev/null @@ -1,1079 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * - * Copyright (C) 1997-2004 Erez Zadok - * Copyright (C) 2001-2004 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * Michael C. Thompsion - * - * 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 "ecryptfs_kernel.h" - -static struct dentry *lock_parent(struct dentry *dentry) -{ - struct dentry *dir; - - dir = dget(dentry->d_parent); - mutex_lock(&(dir->d_inode->i_mutex)); - return dir; -} - -static void unlock_parent(struct dentry *dentry) -{ - mutex_unlock(&(dentry->d_parent->d_inode->i_mutex)); - dput(dentry->d_parent); -} - -static void unlock_dir(struct dentry *dir) -{ - mutex_unlock(&dir->d_inode->i_mutex); - dput(dir); -} - -void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src) -{ - i_size_write(dst, i_size_read((struct inode *)src)); - dst->i_blocks = src->i_blocks; -} - -void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src) -{ - dest->i_atime = src->i_atime; -} - -static void ecryptfs_copy_attr_times(struct inode *dest, - const struct inode *src) -{ - dest->i_atime = src->i_atime; - dest->i_mtime = src->i_mtime; - dest->i_ctime = src->i_ctime; -} - -static void ecryptfs_copy_attr_timesizes(struct inode *dest, - const struct inode *src) -{ - dest->i_atime = src->i_atime; - dest->i_mtime = src->i_mtime; - dest->i_ctime = src->i_ctime; - ecryptfs_copy_inode_size(dest, src); -} - -void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src) -{ - dest->i_mode = src->i_mode; - dest->i_nlink = src->i_nlink; - dest->i_uid = src->i_uid; - dest->i_gid = src->i_gid; - dest->i_rdev = src->i_rdev; - dest->i_atime = src->i_atime; - dest->i_mtime = src->i_mtime; - dest->i_ctime = src->i_ctime; - dest->i_blkbits = src->i_blkbits; - dest->i_flags = src->i_flags; -} - -/** - * ecryptfs_create_underlying_file - * @lower_dir_inode: inode of the parent in the lower fs of the new file - * @lower_dentry: New file's dentry in the lower fs - * @ecryptfs_dentry: New file's dentry in ecryptfs - * @mode: The mode of the new file - * @nd: nameidata of ecryptfs' parent's dentry & vfsmount - * - * Creates the file in the lower file system. - * - * Returns zero on success; non-zero on error condition - */ -static int -ecryptfs_create_underlying_file(struct inode *lower_dir_inode, - struct dentry *dentry, int mode, - struct nameidata *nd) -{ - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); - struct dentry *dentry_save; - struct vfsmount *vfsmount_save; - int rc; - - dentry_save = nd->dentry; - vfsmount_save = nd->mnt; - nd->dentry = lower_dentry; - nd->mnt = lower_mnt; - rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); - nd->dentry = dentry_save; - nd->mnt = vfsmount_save; - return rc; -} - -/** - * ecryptfs_do_create - * @directory_inode: inode of the new file's dentry's parent in ecryptfs - * @ecryptfs_dentry: New file's dentry in ecryptfs - * @mode: The mode of the new file - * @nd: nameidata of ecryptfs' parent's dentry & vfsmount - * - * Creates the underlying file and the eCryptfs inode which will link to - * it. It will also update the eCryptfs directory inode to mimic the - * stat of the lower directory inode. - * - * Returns zero on success; non-zero on error condition - */ -static int -ecryptfs_do_create(struct inode *directory_inode, - struct dentry *ecryptfs_dentry, int mode, - struct nameidata *nd) -{ - int rc; - struct dentry *lower_dentry; - struct dentry *lower_dir_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); - lower_dir_dentry = lock_parent(lower_dentry); - if (unlikely(IS_ERR(lower_dir_dentry))) { - ecryptfs_printk(KERN_ERR, "Error locking directory of " - "dentry\n"); - rc = PTR_ERR(lower_dir_dentry); - goto out; - } - rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode, - ecryptfs_dentry, mode, nd); - if (unlikely(rc)) { - ecryptfs_printk(KERN_ERR, - "Failure to create underlying file\n"); - goto out_lock; - } - rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, - directory_inode->i_sb, 0); - if (rc) { - ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n"); - goto out_lock; - } - ecryptfs_copy_attr_timesizes(directory_inode, - lower_dir_dentry->d_inode); -out_lock: - unlock_dir(lower_dir_dentry); -out: - return rc; -} - -/** - * grow_file - * @ecryptfs_dentry: the ecryptfs dentry - * @lower_file: The lower file - * @inode: The ecryptfs inode - * @lower_inode: The lower inode - * - * This is the code which will grow the file to its correct size. - */ -static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file, - struct inode *inode, struct inode *lower_inode) -{ - int rc = 0; - struct file fake_file; - struct ecryptfs_file_info tmp_file_info; - - memset(&fake_file, 0, sizeof(fake_file)); - fake_file.f_dentry = ecryptfs_dentry; - memset(&tmp_file_info, 0, sizeof(tmp_file_info)); - ecryptfs_set_file_private(&fake_file, &tmp_file_info); - ecryptfs_set_file_lower(&fake_file, lower_file); - rc = ecryptfs_fill_zeros(&fake_file, 1); - if (rc) { - ECRYPTFS_SET_FLAG( - ecryptfs_inode_to_private(inode)->crypt_stat.flags, - ECRYPTFS_SECURITY_WARNING); - ecryptfs_printk(KERN_WARNING, "Error attempting to fill zeros " - "in file; rc = [%d]\n", rc); - goto out; - } - i_size_write(inode, 0); - ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode); - ECRYPTFS_SET_FLAG(ecryptfs_inode_to_private(inode)->crypt_stat.flags, - ECRYPTFS_NEW_FILE); -out: - return rc; -} - -/** - * ecryptfs_initialize_file - * - * Cause the file to be changed from a basic empty file to an ecryptfs - * file with a header and first data page. - * - * Returns zero on success - */ -static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry) -{ - int rc = 0; - int lower_flags; - struct ecryptfs_crypt_stat *crypt_stat; - struct dentry *lower_dentry; - struct dentry *tlower_dentry = NULL; - struct file *lower_file; - struct inode *inode, *lower_inode; - struct vfsmount *lower_mnt; - - lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); - ecryptfs_printk(KERN_DEBUG, "lower_dentry->d_name.name = [%s]\n", - lower_dentry->d_name.name); - inode = ecryptfs_dentry->d_inode; - crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - tlower_dentry = dget(lower_dentry); - if (!tlower_dentry) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry\n"); - goto out; - } - lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR; -#if BITS_PER_LONG != 32 - lower_flags |= O_LARGEFILE; -#endif - lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); - mntget(lower_mnt); - /* Corresponding fput() at end of this function */ - lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags); - if (IS_ERR(lower_file)) { - rc = PTR_ERR(lower_file); - ecryptfs_printk(KERN_ERR, - "Error opening dentry; rc = [%i]\n", rc); - goto out; - } - /* fput(lower_file) should handle the puts if we do this */ - lower_file->f_dentry = tlower_dentry; - lower_file->f_vfsmnt = lower_mnt; - lower_inode = tlower_dentry->d_inode; - if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { - ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); - goto out_fput; - } - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE); - ecryptfs_printk(KERN_DEBUG, "Initializing crypto context\n"); - rc = ecryptfs_new_file_context(ecryptfs_dentry); - if (rc) { - ecryptfs_printk(KERN_DEBUG, "Error creating new file " - "context\n"); - goto out_fput; - } - rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file); - if (rc) { - ecryptfs_printk(KERN_DEBUG, "Error writing headers\n"); - goto out_fput; - } - rc = grow_file(ecryptfs_dentry, lower_file, inode, lower_inode); -out_fput: - fput(lower_file); -out: - return rc; -} - -/** - * ecryptfs_create - * @dir: The inode of the directory in which to create the file. - * @dentry: The eCryptfs dentry - * @mode: The mode of the new file. - * @nd: nameidata - * - * Creates a new file. - * - * Returns zero on success; non-zero on error condition - */ -static int -ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, - int mode, struct nameidata *nd) -{ - int rc; - - rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd); - if (unlikely(rc)) { - ecryptfs_printk(KERN_WARNING, "Failed to create file in" - "lower filesystem\n"); - goto out; - } - /* At this point, a file exists on "disk"; we need to make sure - * that this on disk file is prepared to be an ecryptfs file */ - rc = ecryptfs_initialize_file(ecryptfs_dentry); -out: - return rc; -} - -/** - * ecryptfs_lookup - * @dir: inode - * @dentry: The dentry - * @nd: nameidata, may be NULL - * - * Find a file on disk. If the file does not exist, then we'll add it to the - * dentry cache and continue on to read it from the disk. - */ -static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - int rc = 0; - struct dentry *lower_dir_dentry; - struct dentry *lower_dentry; - struct vfsmount *lower_mnt; - struct dentry *tlower_dentry = NULL; - char *encoded_name; - unsigned int encoded_namelen; - struct ecryptfs_crypt_stat *crypt_stat = NULL; - char *page_virt = NULL; - struct inode *lower_inode; - u64 file_size; - - lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent); - dentry->d_op = &ecryptfs_dops; - if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, ".")) - || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, ".."))) - goto out_drop; - encoded_namelen = ecryptfs_encode_filename(crypt_stat, - dentry->d_name.name, - dentry->d_name.len, - &encoded_name); - if (encoded_namelen < 0) { - rc = encoded_namelen; - goto out_drop; - } - ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen " - "= [%d]\n", encoded_name, encoded_namelen); - lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, - encoded_namelen - 1); - kfree(encoded_name); - lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); - if (IS_ERR(lower_dentry)) { - ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n"); - rc = PTR_ERR(lower_dentry); - goto out_drop; - } - ecryptfs_printk(KERN_DEBUG, "lower_dentry = [%p]; lower_dentry->" - "d_name.name = [%s]\n", lower_dentry, - lower_dentry->d_name.name); - lower_inode = lower_dentry->d_inode; - ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode); - BUG_ON(!atomic_read(&lower_dentry->d_count)); - ecryptfs_set_dentry_private(dentry, - kmem_cache_alloc(ecryptfs_dentry_info_cache, - SLAB_KERNEL)); - if (!ecryptfs_dentry_to_private(dentry)) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting " - "to allocate ecryptfs_dentry_info struct\n"); - goto out_dput; - } - ecryptfs_set_dentry_lower(dentry, lower_dentry); - ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt); - if (!lower_dentry->d_inode) { - /* We want to add because we couldn't find in lower */ - d_add(dentry, NULL); - goto out; - } - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error interposing\n"); - goto out_dput; - } - if (S_ISDIR(lower_inode->i_mode)) { - ecryptfs_printk(KERN_DEBUG, "Is a directory; returning\n"); - goto out; - } - if (S_ISLNK(lower_inode->i_mode)) { - ecryptfs_printk(KERN_DEBUG, "Is a symlink; returning\n"); - goto out; - } - if (!nd) { - ecryptfs_printk(KERN_DEBUG, "We have a NULL nd, just leave" - "as we *think* we are about to unlink\n"); - goto out; - } - tlower_dentry = dget(lower_dentry); - if (!tlower_dentry || IS_ERR(tlower_dentry)) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Cannot dget lower_dentry\n"); - goto out_dput; - } - /* Released in this function */ - page_virt = - (char *)kmem_cache_alloc(ecryptfs_header_cache_2, - SLAB_USER); - if (!page_virt) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, - "Cannot ecryptfs_kmalloc a page\n"); - goto out_dput; - } - memset(page_virt, 0, PAGE_CACHE_SIZE); - rc = ecryptfs_read_header_region(page_virt, tlower_dentry, nd->mnt); - crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) - ecryptfs_set_default_sizes(crypt_stat); - if (rc) { - rc = 0; - ecryptfs_printk(KERN_WARNING, "Error reading header region;" - " assuming unencrypted\n"); - } else { - if (!contains_ecryptfs_marker(page_virt - + ECRYPTFS_FILE_SIZE_BYTES)) { - kmem_cache_free(ecryptfs_header_cache_2, page_virt); - goto out; - } - memcpy(&file_size, page_virt, sizeof(file_size)); - file_size = be64_to_cpu(file_size); - i_size_write(dentry->d_inode, (loff_t)file_size); - } - kmem_cache_free(ecryptfs_header_cache_2, page_virt); - goto out; - -out_dput: - dput(lower_dentry); - if (tlower_dentry) - dput(tlower_dentry); -out_drop: - d_drop(dentry); -out: - return ERR_PTR(rc); -} - -static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - struct dentry *lower_old_dentry; - struct dentry *lower_new_dentry; - struct dentry *lower_dir_dentry; - u64 file_size_save; - int rc; - - file_size_save = i_size_read(old_dentry->d_inode); - lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); - lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); - dget(lower_old_dentry); - dget(lower_new_dentry); - lower_dir_dentry = lock_parent(lower_new_dentry); - rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, - lower_new_dentry); - if (rc || !lower_new_dentry->d_inode) - goto out_lock; - rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); - if (rc) - goto out_lock; - ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode); - old_dentry->d_inode->i_nlink = - ecryptfs_inode_to_lower(old_dentry->d_inode)->i_nlink; - i_size_write(new_dentry->d_inode, file_size_save); -out_lock: - unlock_dir(lower_dir_dentry); - dput(lower_new_dentry); - dput(lower_old_dentry); - if (!new_dentry->d_inode) - d_drop(new_dentry); - return rc; -} - -static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) -{ - int rc = 0; - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); - - lock_parent(lower_dentry); - rc = vfs_unlink(lower_dir_inode, lower_dentry); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error in vfs_unlink\n"); - goto out_unlock; - } - ecryptfs_copy_attr_times(dir, lower_dir_inode); - dentry->d_inode->i_nlink = - ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink; - dentry->d_inode->i_ctime = dir->i_ctime; -out_unlock: - unlock_parent(lower_dentry); - return rc; -} - -static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int rc; - struct dentry *lower_dentry; - struct dentry *lower_dir_dentry; - umode_t mode; - char *encoded_symname; - unsigned int encoded_symlen; - struct ecryptfs_crypt_stat *crypt_stat = NULL; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - dget(lower_dentry); - lower_dir_dentry = lock_parent(lower_dentry); - mode = S_IALLUGO; - encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, - strlen(symname), - &encoded_symname); - if (encoded_symlen < 0) { - rc = encoded_symlen; - goto out_lock; - } - rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, - encoded_symname, mode); - kfree(encoded_symname); - if (rc || !lower_dentry->d_inode) - goto out_lock; - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); - if (rc) - goto out_lock; - ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); -out_lock: - unlock_dir(lower_dir_dentry); - dput(lower_dentry); - if (!dentry->d_inode) - d_drop(dentry); - return rc; -} - -static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - int rc; - struct dentry *lower_dentry; - struct dentry *lower_dir_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - lower_dir_dentry = lock_parent(lower_dentry); - rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); - if (rc || !lower_dentry->d_inode) - goto out; - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); - if (rc) - goto out; - ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); - dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; -out: - unlock_dir(lower_dir_dentry); - if (!dentry->d_inode) - d_drop(dentry); - return rc; -} - -static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - int rc = 0; - struct dentry *tdentry = NULL; - struct dentry *lower_dentry; - struct dentry *tlower_dentry = NULL; - struct dentry *lower_dir_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!(tdentry = dget(dentry))) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "Error dget'ing dentry [%p]\n", - dentry); - goto out; - } - lower_dir_dentry = lock_parent(lower_dentry); - if (!(tlower_dentry = dget(lower_dentry))) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry " - "[%p]\n", lower_dentry); - goto out; - } - rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); - if (!rc) { - d_delete(tlower_dentry); - tlower_dentry = NULL; - } - ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode); - dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; - unlock_dir(lower_dir_dentry); - if (!rc) - d_drop(dentry); -out: - if (tdentry) - dput(tdentry); - if (tlower_dentry) - dput(tlower_dentry); - return rc; -} - -static int -ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) -{ - int rc; - struct dentry *lower_dentry; - struct dentry *lower_dir_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - lower_dir_dentry = lock_parent(lower_dentry); - rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev); - if (rc || !lower_dentry->d_inode) - goto out; - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); - if (rc) - goto out; - ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); -out: - unlock_dir(lower_dir_dentry); - if (!dentry->d_inode) - d_drop(dentry); - return rc; -} - -static int -ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int rc; - struct dentry *lower_old_dentry; - struct dentry *lower_new_dentry; - struct dentry *lower_old_dir_dentry; - struct dentry *lower_new_dir_dentry; - - lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); - lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); - dget(lower_old_dentry); - dget(lower_new_dentry); - lower_old_dir_dentry = dget_parent(lower_old_dentry); - lower_new_dir_dentry = dget_parent(lower_new_dentry); - lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); - rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); - if (rc) - goto out_lock; - ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); - if (new_dir != old_dir) - ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); -out_lock: - unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); - dput(lower_new_dentry); - dput(lower_old_dentry); - return rc; -} - -static int -ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz) -{ - int rc; - struct dentry *lower_dentry; - char *decoded_name; - char *lower_buf; - mm_segment_t old_fs; - struct ecryptfs_crypt_stat *crypt_stat; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!lower_dentry->d_inode->i_op || - !lower_dentry->d_inode->i_op->readlink) { - rc = -EINVAL; - goto out; - } - /* Released in this function */ - lower_buf = kmalloc(bufsiz, GFP_KERNEL); - if (lower_buf == NULL) { - ecryptfs_printk(KERN_ERR, "Out of memory\n"); - rc = -ENOMEM; - goto out; - } - old_fs = get_fs(); - set_fs(get_ds()); - ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ " - "lower_dentry->d_name.name = [%s]\n", - lower_dentry->d_name.name); - rc = lower_dentry->d_inode->i_op->readlink(lower_dentry, - (char __user *)lower_buf, - bufsiz); - set_fs(old_fs); - if (rc >= 0) { - crypt_stat = NULL; - rc = ecryptfs_decode_filename(crypt_stat, lower_buf, rc, - &decoded_name); - if (rc == -ENOMEM) - goto out_free_lower_buf; - if (rc > 0) { - ecryptfs_printk(KERN_DEBUG, "Copying [%d] bytes " - "to userspace: [%*s]\n", rc, - decoded_name); - if (copy_to_user(buf, decoded_name, rc)) - rc = -EFAULT; - } - kfree(decoded_name); - ecryptfs_copy_attr_atime(dentry->d_inode, - lower_dentry->d_inode); - } -out_free_lower_buf: - kfree(lower_buf); -out: - return rc; -} - -static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *buf; - int len = PAGE_SIZE, rc; - mm_segment_t old_fs; - - /* Released in ecryptfs_put_link(); only release here on error */ - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - rc = -ENOMEM; - goto out; - } - old_fs = get_fs(); - set_fs(get_ds()); - ecryptfs_printk(KERN_DEBUG, "Calling readlink w/ " - "dentry->d_name.name = [%s]\n", dentry->d_name.name); - rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len); - buf[rc] = '\0'; - set_fs(old_fs); - if (rc < 0) - goto out_free; - rc = 0; - nd_set_link(nd, buf); - goto out; -out_free: - kfree(buf); -out: - return ERR_PTR(rc); -} - -static void -ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr) -{ - /* Free the char* */ - kfree(nd_get_link(nd)); -} - -/** - * upper_size_to_lower_size - * @crypt_stat: Crypt_stat associated with file - * @upper_size: Size of the upper file - * - * Calculate the requried size of the lower file based on the - * specified size of the upper file. This calculation is based on the - * number of headers in the underlying file and the extent size. - * - * Returns Calculated size of the lower file. - */ -static loff_t -upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, - loff_t upper_size) -{ - loff_t lower_size; - - lower_size = ( crypt_stat->header_extent_size - * crypt_stat->num_header_extents_at_front ); - if (upper_size != 0) { - loff_t num_extents; - - num_extents = upper_size >> crypt_stat->extent_shift; - if (upper_size & ~crypt_stat->extent_mask) - num_extents++; - lower_size += (num_extents * crypt_stat->extent_size); - } - return lower_size; -} - -/** - * ecryptfs_truncate - * @dentry: The ecryptfs layer dentry - * @new_length: The length to expand the file to - * - * Function to handle truncations modifying the size of the file. Note - * that the file sizes are interpolated. When expanding, we are simply - * writing strings of 0's out. When truncating, we need to modify the - * underlying file size according to the page index interpolations. - * - * Returns zero on success; non-zero otherwise - */ -int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) -{ - int rc = 0; - struct inode *inode = dentry->d_inode; - struct dentry *lower_dentry; - struct vfsmount *lower_mnt; - struct file fake_ecryptfs_file, *lower_file = NULL; - struct ecryptfs_crypt_stat *crypt_stat; - loff_t i_size = i_size_read(inode); - loff_t lower_size_before_truncate; - loff_t lower_size_after_truncate; - - if (unlikely((new_length == i_size))) - goto out; - crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; - /* Set up a fake ecryptfs file, this is used to interface with - * the file in the underlying filesystem so that the - * truncation has an effect there as well. */ - memset(&fake_ecryptfs_file, 0, sizeof(fake_ecryptfs_file)); - fake_ecryptfs_file.f_dentry = dentry; - /* Released at out_free: label */ - ecryptfs_set_file_private(&fake_ecryptfs_file, - kmem_cache_alloc(ecryptfs_file_info_cache, - SLAB_KERNEL)); - if (unlikely(!ecryptfs_file_to_private(&fake_ecryptfs_file))) { - rc = -ENOMEM; - goto out; - } - lower_dentry = ecryptfs_dentry_to_lower(dentry); - /* This dget & mntget is released through fput at out_fput: */ - dget(lower_dentry); - lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); - mntget(lower_mnt); - lower_file = dentry_open(lower_dentry, lower_mnt, O_RDWR); - if (unlikely(IS_ERR(lower_file))) { - rc = PTR_ERR(lower_file); - goto out_free; - } - ecryptfs_set_file_lower(&fake_ecryptfs_file, lower_file); - /* Switch on growing or shrinking file */ - if (new_length > i_size) { - rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length); - if (rc) { - ecryptfs_printk(KERN_ERR, - "Problem with fill_zeros\n"); - goto out_fput; - } - i_size_write(inode, new_length); - rc = ecryptfs_write_inode_size_to_header(lower_file, - lower_dentry->d_inode, - inode); - if (rc) { - ecryptfs_printk(KERN_ERR, - "Problem with ecryptfs_write" - "_inode_size\n"); - goto out_fput; - } - } else { /* new_length < i_size_read(inode) */ - vmtruncate(inode, new_length); - ecryptfs_write_inode_size_to_header(lower_file, - lower_dentry->d_inode, - inode); - /* We are reducing the size of the ecryptfs file, and need to - * know if we need to reduce the size of the lower file. */ - lower_size_before_truncate = - upper_size_to_lower_size(crypt_stat, i_size); - lower_size_after_truncate = - upper_size_to_lower_size(crypt_stat, new_length); - if (lower_size_after_truncate < lower_size_before_truncate) - vmtruncate(lower_dentry->d_inode, - lower_size_after_truncate); - } - /* Update the access times */ - lower_dentry->d_inode->i_mtime = lower_dentry->d_inode->i_ctime - = CURRENT_TIME; - mark_inode_dirty_sync(inode); -out_fput: - fput(lower_file); -out_free: - if (ecryptfs_file_to_private(&fake_ecryptfs_file)) - kmem_cache_free(ecryptfs_file_info_cache, - ecryptfs_file_to_private(&fake_ecryptfs_file)); -out: - return rc; -} - -static int -ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) -{ - int rc; - - if (nd) { - struct vfsmount *vfsmnt_save = nd->mnt; - struct dentry *dentry_save = nd->dentry; - - nd->mnt = ecryptfs_dentry_to_lower_mnt(nd->dentry); - nd->dentry = ecryptfs_dentry_to_lower(nd->dentry); - rc = permission(ecryptfs_inode_to_lower(inode), mask, nd); - nd->mnt = vfsmnt_save; - nd->dentry = dentry_save; - } else - rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL); - return rc; -} - -/** - * ecryptfs_setattr - * @dentry: dentry handle to the inode to modify - * @ia: Structure with flags of what to change and values - * - * Updates the metadata of an inode. If the update is to the size - * i.e. truncation, then ecryptfs_truncate will handle the size modification - * of both the ecryptfs inode and the lower inode. - * - * All other metadata changes will be passed right to the lower filesystem, - * and we will just update our inode to look like the lower. - */ -static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) -{ - int rc = 0; - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; - struct ecryptfs_crypt_stat *crypt_stat; - - crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; - lower_dentry = ecryptfs_dentry_to_lower(dentry); - inode = dentry->d_inode; - lower_inode = ecryptfs_inode_to_lower(inode); - if (ia->ia_valid & ATTR_SIZE) { - ecryptfs_printk(KERN_DEBUG, - "ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n", - ia->ia_valid, ATTR_SIZE); - rc = ecryptfs_truncate(dentry, ia->ia_size); - /* ecryptfs_truncate handles resizing of the lower file */ - ia->ia_valid &= ~ATTR_SIZE; - ecryptfs_printk(KERN_DEBUG, "ia->ia_valid = [%x]\n", - ia->ia_valid); - if (rc < 0) - goto out; - } - rc = notify_change(lower_dentry, ia); -out: - ecryptfs_copy_attr_all(inode, lower_inode); - return rc; -} - -static int -ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags) -{ - int rc = 0; - struct dentry *lower_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!lower_dentry->d_inode->i_op->setxattr) { - rc = -ENOSYS; - goto out; - } - mutex_lock(&lower_dentry->d_inode->i_mutex); - rc = lower_dentry->d_inode->i_op->setxattr(lower_dentry, name, value, - size, flags); - mutex_unlock(&lower_dentry->d_inode->i_mutex); -out: - return rc; -} - -static ssize_t -ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value, - size_t size) -{ - int rc = 0; - struct dentry *lower_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!lower_dentry->d_inode->i_op->getxattr) { - rc = -ENOSYS; - goto out; - } - mutex_lock(&lower_dentry->d_inode->i_mutex); - rc = lower_dentry->d_inode->i_op->getxattr(lower_dentry, name, value, - size); - mutex_unlock(&lower_dentry->d_inode->i_mutex); -out: - return rc; -} - -static ssize_t -ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size) -{ - int rc = 0; - struct dentry *lower_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!lower_dentry->d_inode->i_op->listxattr) { - rc = -ENOSYS; - goto out; - } - mutex_lock(&lower_dentry->d_inode->i_mutex); - rc = lower_dentry->d_inode->i_op->listxattr(lower_dentry, list, size); - mutex_unlock(&lower_dentry->d_inode->i_mutex); -out: - return rc; -} - -static int ecryptfs_removexattr(struct dentry *dentry, const char *name) -{ - int rc = 0; - struct dentry *lower_dentry; - - lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!lower_dentry->d_inode->i_op->removexattr) { - rc = -ENOSYS; - goto out; - } - mutex_lock(&lower_dentry->d_inode->i_mutex); - rc = lower_dentry->d_inode->i_op->removexattr(lower_dentry, name); - mutex_unlock(&lower_dentry->d_inode->i_mutex); -out: - return rc; -} - -int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode) -{ - if ((ecryptfs_inode_to_lower(inode) - == (struct inode *)candidate_lower_inode)) - return 1; - else - return 0; -} - -int ecryptfs_inode_set(struct inode *inode, void *lower_inode) -{ - ecryptfs_init_inode(inode, (struct inode *)lower_inode); - return 0; -} - -struct inode_operations ecryptfs_symlink_iops = { - .readlink = ecryptfs_readlink, - .follow_link = ecryptfs_follow_link, - .put_link = ecryptfs_put_link, - .permission = ecryptfs_permission, - .setattr = ecryptfs_setattr, - .setxattr = ecryptfs_setxattr, - .getxattr = ecryptfs_getxattr, - .listxattr = ecryptfs_listxattr, - .removexattr = ecryptfs_removexattr -}; - -struct inode_operations ecryptfs_dir_iops = { - .create = ecryptfs_create, - .lookup = ecryptfs_lookup, - .link = ecryptfs_link, - .unlink = ecryptfs_unlink, - .symlink = ecryptfs_symlink, - .mkdir = ecryptfs_mkdir, - .rmdir = ecryptfs_rmdir, - .mknod = ecryptfs_mknod, - .rename = ecryptfs_rename, - .permission = ecryptfs_permission, - .setattr = ecryptfs_setattr, - .setxattr = ecryptfs_setxattr, - .getxattr = ecryptfs_getxattr, - .listxattr = ecryptfs_listxattr, - .removexattr = ecryptfs_removexattr -}; - -struct inode_operations ecryptfs_main_iops = { - .permission = ecryptfs_permission, - .setattr = ecryptfs_setattr, - .setxattr = ecryptfs_setxattr, - .getxattr = ecryptfs_getxattr, - .listxattr = ecryptfs_listxattr, - .removexattr = ecryptfs_removexattr -}; diff --git a/trunk/fs/ecryptfs/keystore.c b/trunk/fs/ecryptfs/keystore.c deleted file mode 100644 index ba454785a0c5..000000000000 --- a/trunk/fs/ecryptfs/keystore.c +++ /dev/null @@ -1,1061 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * In-kernel key management code. Includes functions to parse and - * write authentication token-related packets with the underlying - * file. - * - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * Michael C. Thompson - * - * 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 "ecryptfs_kernel.h" - -/** - * request_key returned an error instead of a valid key address; - * determine the type of error, make appropriate log entries, and - * return an error code. - */ -int process_request_key_err(long err_code) -{ - int rc = 0; - - switch (err_code) { - case ENOKEY: - ecryptfs_printk(KERN_WARNING, "No key\n"); - rc = -ENOENT; - break; - case EKEYEXPIRED: - ecryptfs_printk(KERN_WARNING, "Key expired\n"); - rc = -ETIME; - break; - case EKEYREVOKED: - ecryptfs_printk(KERN_WARNING, "Key revoked\n"); - rc = -EINVAL; - break; - default: - ecryptfs_printk(KERN_WARNING, "Unknown error code: " - "[0x%.16x]\n", err_code); - rc = -EINVAL; - } - return rc; -} - -static void wipe_auth_tok_list(struct list_head *auth_tok_list_head) -{ - struct list_head *walker; - struct ecryptfs_auth_tok_list_item *auth_tok_list_item; - - walker = auth_tok_list_head->next; - while (walker != auth_tok_list_head) { - auth_tok_list_item = - list_entry(walker, struct ecryptfs_auth_tok_list_item, - list); - walker = auth_tok_list_item->list.next; - memset(auth_tok_list_item, 0, - sizeof(struct ecryptfs_auth_tok_list_item)); - kmem_cache_free(ecryptfs_auth_tok_list_item_cache, - auth_tok_list_item); - } -} - -struct kmem_cache *ecryptfs_auth_tok_list_item_cache; - -/** - * parse_packet_length - * @data: Pointer to memory containing length at offset - * @size: This function writes the decoded size to this memory - * address; zero on error - * @length_size: The number of bytes occupied by the encoded length - * - * Returns Zero on success - */ -static int parse_packet_length(unsigned char *data, size_t *size, - size_t *length_size) -{ - int rc = 0; - - (*length_size) = 0; - (*size) = 0; - if (data[0] < 192) { - /* One-byte length */ - (*size) = data[0]; - (*length_size) = 1; - } else if (data[0] < 224) { - /* Two-byte length */ - (*size) = ((data[0] - 192) * 256); - (*size) += (data[1] + 192); - (*length_size) = 2; - } else if (data[0] == 255) { - /* Five-byte length; we're not supposed to see this */ - ecryptfs_printk(KERN_ERR, "Five-byte packet length not " - "supported\n"); - rc = -EINVAL; - goto out; - } else { - ecryptfs_printk(KERN_ERR, "Error parsing packet length\n"); - rc = -EINVAL; - goto out; - } -out: - return rc; -} - -/** - * write_packet_length - * @dest: The byte array target into which to write the - * length. Must have at least 5 bytes allocated. - * @size: The length to write. - * @packet_size_length: The number of bytes used to encode the - * packet length is written to this address. - * - * Returns zero on success; non-zero on error. - */ -static int write_packet_length(char *dest, size_t size, - size_t *packet_size_length) -{ - int rc = 0; - - if (size < 192) { - dest[0] = size; - (*packet_size_length) = 1; - } else if (size < 65536) { - dest[0] = (((size - 192) / 256) + 192); - dest[1] = ((size - 192) % 256); - (*packet_size_length) = 2; - } else { - rc = -EINVAL; - ecryptfs_printk(KERN_WARNING, - "Unsupported packet size: [%d]\n", size); - } - return rc; -} - -/** - * parse_tag_3_packet - * @crypt_stat: The cryptographic context to modify based on packet - * contents. - * @data: The raw bytes of the packet. - * @auth_tok_list: eCryptfs parses packets into authentication tokens; - * a new authentication token will be placed at the end - * of this list for this packet. - * @new_auth_tok: Pointer to a pointer to memory that this function - * allocates; sets the memory address of the pointer to - * NULL on error. This object is added to the - * auth_tok_list. - * @packet_size: This function writes the size of the parsed packet - * into this memory location; zero on error. - * @max_packet_size: maximum number of bytes to parse - * - * Returns zero on success; non-zero on error. - */ -static int -parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat, - unsigned char *data, struct list_head *auth_tok_list, - struct ecryptfs_auth_tok **new_auth_tok, - size_t *packet_size, size_t max_packet_size) -{ - int rc = 0; - size_t body_size; - struct ecryptfs_auth_tok_list_item *auth_tok_list_item; - size_t length_size; - - (*packet_size) = 0; - (*new_auth_tok) = NULL; - - /* we check that: - * one byte for the Tag 3 ID flag - * two bytes for the body size - * do not exceed the maximum_packet_size - */ - if (unlikely((*packet_size) + 3 > max_packet_size)) { - ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); - rc = -EINVAL; - goto out; - } - - /* check for Tag 3 identifyer - one byte */ - if (data[(*packet_size)++] != ECRYPTFS_TAG_3_PACKET_TYPE) { - ecryptfs_printk(KERN_ERR, "Enter w/ first byte != 0x%.2x\n", - ECRYPTFS_TAG_3_PACKET_TYPE); - rc = -EINVAL; - goto out; - } - /* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or - * at end of function upon failure */ - auth_tok_list_item = - kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache, SLAB_KERNEL); - if (!auth_tok_list_item) { - ecryptfs_printk(KERN_ERR, "Unable to allocate memory\n"); - rc = -ENOMEM; - goto out; - } - memset(auth_tok_list_item, 0, - sizeof(struct ecryptfs_auth_tok_list_item)); - (*new_auth_tok) = &auth_tok_list_item->auth_tok; - - /* check for body size - one to two bytes */ - rc = parse_packet_length(&data[(*packet_size)], &body_size, - &length_size); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error parsing packet length; " - "rc = [%d]\n", rc); - goto out_free; - } - if (unlikely(body_size < (0x05 + ECRYPTFS_SALT_SIZE))) { - ecryptfs_printk(KERN_WARNING, "Invalid body size ([%d])\n", - body_size); - rc = -EINVAL; - goto out_free; - } - (*packet_size) += length_size; - - /* now we know the length of the remainting Tag 3 packet size: - * 5 fix bytes for: version string, cipher, S2K ID, hash algo, - * number of hash iterations - * ECRYPTFS_SALT_SIZE bytes for salt - * body_size bytes minus the stuff above is the encrypted key size - */ - if (unlikely((*packet_size) + body_size > max_packet_size)) { - ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); - rc = -EINVAL; - goto out_free; - } - - /* There are 5 characters of additional information in the - * packet */ - (*new_auth_tok)->session_key.encrypted_key_size = - body_size - (0x05 + ECRYPTFS_SALT_SIZE); - ecryptfs_printk(KERN_DEBUG, "Encrypted key size = [%d]\n", - (*new_auth_tok)->session_key.encrypted_key_size); - - /* Version 4 (from RFC2440) - one byte */ - if (unlikely(data[(*packet_size)++] != 0x04)) { - ecryptfs_printk(KERN_DEBUG, "Unknown version number " - "[%d]\n", data[(*packet_size) - 1]); - rc = -EINVAL; - goto out_free; - } - - /* cipher - one byte */ - ecryptfs_cipher_code_to_string(crypt_stat->cipher, - (u16)data[(*packet_size)]); - /* A little extra work to differentiate among the AES key - * sizes; see RFC2440 */ - switch(data[(*packet_size)++]) { - case RFC2440_CIPHER_AES_192: - crypt_stat->key_size = 24; - break; - default: - crypt_stat->key_size = - (*new_auth_tok)->session_key.encrypted_key_size; - } - ecryptfs_init_crypt_ctx(crypt_stat); - /* S2K identifier 3 (from RFC2440) */ - if (unlikely(data[(*packet_size)++] != 0x03)) { - ecryptfs_printk(KERN_ERR, "Only S2K ID 3 is currently " - "supported\n"); - rc = -ENOSYS; - goto out_free; - } - - /* TODO: finish the hash mapping */ - /* hash algorithm - one byte */ - switch (data[(*packet_size)++]) { - case 0x01: /* See RFC2440 for these numbers and their mappings */ - /* Choose MD5 */ - /* salt - ECRYPTFS_SALT_SIZE bytes */ - memcpy((*new_auth_tok)->token.password.salt, - &data[(*packet_size)], ECRYPTFS_SALT_SIZE); - (*packet_size) += ECRYPTFS_SALT_SIZE; - - /* This conversion was taken straight from RFC2440 */ - /* number of hash iterations - one byte */ - (*new_auth_tok)->token.password.hash_iterations = - ((u32) 16 + (data[(*packet_size)] & 15)) - << ((data[(*packet_size)] >> 4) + 6); - (*packet_size)++; - - /* encrypted session key - - * (body_size-5-ECRYPTFS_SALT_SIZE) bytes */ - memcpy((*new_auth_tok)->session_key.encrypted_key, - &data[(*packet_size)], - (*new_auth_tok)->session_key.encrypted_key_size); - (*packet_size) += - (*new_auth_tok)->session_key.encrypted_key_size; - (*new_auth_tok)->session_key.flags &= - ~ECRYPTFS_CONTAINS_DECRYPTED_KEY; - (*new_auth_tok)->session_key.flags |= - ECRYPTFS_CONTAINS_ENCRYPTED_KEY; - (*new_auth_tok)->token.password.hash_algo = 0x01; - break; - default: - ecryptfs_printk(KERN_ERR, "Unsupported hash algorithm: " - "[%d]\n", data[(*packet_size) - 1]); - rc = -ENOSYS; - goto out_free; - } - (*new_auth_tok)->token_type = ECRYPTFS_PASSWORD; - /* TODO: Parametarize; we might actually want userspace to - * decrypt the session key. */ - ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags, - ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT); - ECRYPTFS_CLEAR_FLAG((*new_auth_tok)->session_key.flags, - ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT); - list_add(&auth_tok_list_item->list, auth_tok_list); - goto out; -out_free: - (*new_auth_tok) = NULL; - memset(auth_tok_list_item, 0, - sizeof(struct ecryptfs_auth_tok_list_item)); - kmem_cache_free(ecryptfs_auth_tok_list_item_cache, - auth_tok_list_item); -out: - if (rc) - (*packet_size) = 0; - return rc; -} - -/** - * parse_tag_11_packet - * @data: The raw bytes of the packet - * @contents: This function writes the data contents of the literal - * packet into this memory location - * @max_contents_bytes: The maximum number of bytes that this function - * is allowed to write into contents - * @tag_11_contents_size: This function writes the size of the parsed - * contents into this memory location; zero on - * error - * @packet_size: This function writes the size of the parsed packet - * into this memory location; zero on error - * @max_packet_size: maximum number of bytes to parse - * - * Returns zero on success; non-zero on error. - */ -static int -parse_tag_11_packet(unsigned char *data, unsigned char *contents, - size_t max_contents_bytes, size_t *tag_11_contents_size, - size_t *packet_size, size_t max_packet_size) -{ - int rc = 0; - size_t body_size; - size_t length_size; - - (*packet_size) = 0; - (*tag_11_contents_size) = 0; - - /* check that: - * one byte for the Tag 11 ID flag - * two bytes for the Tag 11 length - * do not exceed the maximum_packet_size - */ - if (unlikely((*packet_size) + 3 > max_packet_size)) { - ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); - rc = -EINVAL; - goto out; - } - - /* check for Tag 11 identifyer - one byte */ - if (data[(*packet_size)++] != ECRYPTFS_TAG_11_PACKET_TYPE) { - ecryptfs_printk(KERN_WARNING, - "Invalid tag 11 packet format\n"); - rc = -EINVAL; - goto out; - } - - /* get Tag 11 content length - one or two bytes */ - rc = parse_packet_length(&data[(*packet_size)], &body_size, - &length_size); - if (rc) { - ecryptfs_printk(KERN_WARNING, - "Invalid tag 11 packet format\n"); - goto out; - } - (*packet_size) += length_size; - - if (body_size < 13) { - ecryptfs_printk(KERN_WARNING, "Invalid body size ([%d])\n", - body_size); - rc = -EINVAL; - goto out; - } - /* We have 13 bytes of surrounding packet values */ - (*tag_11_contents_size) = (body_size - 13); - - /* now we know the length of the remainting Tag 11 packet size: - * 14 fix bytes for: special flag one, special flag two, - * 12 skipped bytes - * body_size bytes minus the stuff above is the Tag 11 content - */ - /* FIXME why is the body size one byte smaller than the actual - * size of the body? - * this seems to be an error here as well as in - * write_tag_11_packet() */ - if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) { - ecryptfs_printk(KERN_ERR, "Packet size exceeds max\n"); - rc = -EINVAL; - goto out; - } - - /* special flag one - one byte */ - if (data[(*packet_size)++] != 0x62) { - ecryptfs_printk(KERN_WARNING, "Unrecognizable packet\n"); - rc = -EINVAL; - goto out; - } - - /* special flag two - one byte */ - if (data[(*packet_size)++] != 0x08) { - ecryptfs_printk(KERN_WARNING, "Unrecognizable packet\n"); - rc = -EINVAL; - goto out; - } - - /* skip the next 12 bytes */ - (*packet_size) += 12; /* We don't care about the filename or - * the timestamp */ - - /* get the Tag 11 contents - tag_11_contents_size bytes */ - memcpy(contents, &data[(*packet_size)], (*tag_11_contents_size)); - (*packet_size) += (*tag_11_contents_size); - -out: - if (rc) { - (*packet_size) = 0; - (*tag_11_contents_size) = 0; - } - return rc; -} - -/** - * decrypt_session_key - Decrypt the session key with the given auth_tok. - * - * Returns Zero on success; non-zero error otherwise. - */ -static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok, - struct ecryptfs_crypt_stat *crypt_stat) -{ - int rc = 0; - struct ecryptfs_password *password_s_ptr; - struct crypto_tfm *tfm = NULL; - struct scatterlist src_sg[2], dst_sg[2]; - struct mutex *tfm_mutex = NULL; - /* TODO: Use virt_to_scatterlist for these */ - char *encrypted_session_key; - char *session_key; - - password_s_ptr = &auth_tok->token.password; - if (ECRYPTFS_CHECK_FLAG(password_s_ptr->flags, - ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)) - ecryptfs_printk(KERN_DEBUG, "Session key encryption key " - "set; skipping key generation\n"); - ecryptfs_printk(KERN_DEBUG, "Session key encryption key (size [%d])" - ":\n", - password_s_ptr->session_key_encryption_key_bytes); - if (ecryptfs_verbosity > 0) - ecryptfs_dump_hex(password_s_ptr->session_key_encryption_key, - password_s_ptr-> - session_key_encryption_key_bytes); - if (!strcmp(crypt_stat->cipher, - crypt_stat->mount_crypt_stat->global_default_cipher_name) - && crypt_stat->mount_crypt_stat->global_key_tfm) { - tfm = crypt_stat->mount_crypt_stat->global_key_tfm; - tfm_mutex = &crypt_stat->mount_crypt_stat->global_key_tfm_mutex; - } else { - tfm = crypto_alloc_tfm(crypt_stat->cipher, - CRYPTO_TFM_REQ_WEAK_KEY); - if (!tfm) { - printk(KERN_ERR "Error allocating crypto context\n"); - rc = -ENOMEM; - goto out; - } - } - if (password_s_ptr->session_key_encryption_key_bytes - < crypto_tfm_alg_min_keysize(tfm)) { - printk(KERN_WARNING "Session key encryption key is [%d] bytes; " - "minimum keysize for selected cipher is [%d] bytes.\n", - password_s_ptr->session_key_encryption_key_bytes, - crypto_tfm_alg_min_keysize(tfm)); - rc = -EINVAL; - goto out; - } - if (tfm_mutex) - mutex_lock(tfm_mutex); - crypto_cipher_setkey(tfm, password_s_ptr->session_key_encryption_key, - crypt_stat->key_size); - /* TODO: virt_to_scatterlist */ - encrypted_session_key = (char *)__get_free_page(GFP_KERNEL); - if (!encrypted_session_key) { - ecryptfs_printk(KERN_ERR, "Out of memory\n"); - rc = -ENOMEM; - goto out_free_tfm; - } - session_key = (char *)__get_free_page(GFP_KERNEL); - if (!session_key) { - kfree(encrypted_session_key); - ecryptfs_printk(KERN_ERR, "Out of memory\n"); - rc = -ENOMEM; - goto out_free_tfm; - } - memcpy(encrypted_session_key, auth_tok->session_key.encrypted_key, - auth_tok->session_key.encrypted_key_size); - src_sg[0].page = virt_to_page(encrypted_session_key); - src_sg[0].offset = 0; - BUG_ON(auth_tok->session_key.encrypted_key_size > PAGE_CACHE_SIZE); - src_sg[0].length = auth_tok->session_key.encrypted_key_size; - dst_sg[0].page = virt_to_page(session_key); - dst_sg[0].offset = 0; - auth_tok->session_key.decrypted_key_size = - auth_tok->session_key.encrypted_key_size; - dst_sg[0].length = auth_tok->session_key.encrypted_key_size; - /* TODO: Handle error condition */ - crypto_cipher_decrypt(tfm, dst_sg, src_sg, - auth_tok->session_key.encrypted_key_size); - auth_tok->session_key.decrypted_key_size = - auth_tok->session_key.encrypted_key_size; - memcpy(auth_tok->session_key.decrypted_key, session_key, - auth_tok->session_key.decrypted_key_size); - auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY; - memcpy(crypt_stat->key, auth_tok->session_key.decrypted_key, - auth_tok->session_key.decrypted_key_size); - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_KEY_VALID); - ecryptfs_printk(KERN_DEBUG, "Decrypted session key:\n"); - if (ecryptfs_verbosity > 0) - ecryptfs_dump_hex(crypt_stat->key, - crypt_stat->key_size); - memset(encrypted_session_key, 0, PAGE_CACHE_SIZE); - free_page((unsigned long)encrypted_session_key); - memset(session_key, 0, PAGE_CACHE_SIZE); - free_page((unsigned long)session_key); -out_free_tfm: - if (tfm_mutex) - mutex_unlock(tfm_mutex); - else - crypto_free_tfm(tfm); -out: - return rc; -} - -/** - * ecryptfs_parse_packet_set - * @dest: The header page in memory - * @version: Version of file format, to guide parsing behavior - * - * Get crypt_stat to have the file's session key if the requisite key - * is available to decrypt the session key. - * - * Returns Zero if a valid authentication token was retrieved and - * processed; negative value for file not encrypted or for error - * conditions. - */ -int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, - unsigned char *src, - struct dentry *ecryptfs_dentry) -{ - size_t i = 0; - int rc = 0; - size_t found_auth_tok = 0; - size_t next_packet_is_auth_tok_packet; - char sig[ECRYPTFS_SIG_SIZE_HEX]; - struct list_head auth_tok_list; - struct list_head *walker; - struct ecryptfs_auth_tok *chosen_auth_tok = NULL; - struct ecryptfs_mount_crypt_stat *mount_crypt_stat = - &ecryptfs_superblock_to_private( - ecryptfs_dentry->d_sb)->mount_crypt_stat; - struct ecryptfs_auth_tok *candidate_auth_tok = NULL; - size_t packet_size; - struct ecryptfs_auth_tok *new_auth_tok; - unsigned char sig_tmp_space[ECRYPTFS_SIG_SIZE]; - size_t tag_11_contents_size; - size_t tag_11_packet_size; - - INIT_LIST_HEAD(&auth_tok_list); - /* Parse the header to find as many packets as we can, these will be - * added the our &auth_tok_list */ - next_packet_is_auth_tok_packet = 1; - while (next_packet_is_auth_tok_packet) { - size_t max_packet_size = ((PAGE_CACHE_SIZE - 8) - i); - - switch (src[i]) { - case ECRYPTFS_TAG_3_PACKET_TYPE: - rc = parse_tag_3_packet(crypt_stat, - (unsigned char *)&src[i], - &auth_tok_list, &new_auth_tok, - &packet_size, max_packet_size); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error parsing " - "tag 3 packet\n"); - rc = -EIO; - goto out_wipe_list; - } - i += packet_size; - rc = parse_tag_11_packet((unsigned char *)&src[i], - sig_tmp_space, - ECRYPTFS_SIG_SIZE, - &tag_11_contents_size, - &tag_11_packet_size, - max_packet_size); - if (rc) { - ecryptfs_printk(KERN_ERR, "No valid " - "(ecryptfs-specific) literal " - "packet containing " - "authentication token " - "signature found after " - "tag 3 packet\n"); - rc = -EIO; - goto out_wipe_list; - } - i += tag_11_packet_size; - if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) { - ecryptfs_printk(KERN_ERR, "Expected " - "signature of size [%d]; " - "read size [%d]\n", - ECRYPTFS_SIG_SIZE, - tag_11_contents_size); - rc = -EIO; - goto out_wipe_list; - } - ecryptfs_to_hex(new_auth_tok->token.password.signature, - sig_tmp_space, tag_11_contents_size); - new_auth_tok->token.password.signature[ - ECRYPTFS_PASSWORD_SIG_SIZE] = '\0'; - ECRYPTFS_SET_FLAG(crypt_stat->flags, - ECRYPTFS_ENCRYPTED); - break; - case ECRYPTFS_TAG_11_PACKET_TYPE: - ecryptfs_printk(KERN_WARNING, "Invalid packet set " - "(Tag 11 not allowed by itself)\n"); - rc = -EIO; - goto out_wipe_list; - break; - default: - ecryptfs_printk(KERN_DEBUG, "No packet at offset " - "[%d] of the file header; hex value of " - "character is [0x%.2x]\n", i, src[i]); - next_packet_is_auth_tok_packet = 0; - } - } - if (list_empty(&auth_tok_list)) { - rc = -EINVAL; /* Do not support non-encrypted files in - * the 0.1 release */ - goto out; - } - /* If we have a global auth tok, then we should try to use - * it */ - if (mount_crypt_stat->global_auth_tok) { - memcpy(sig, mount_crypt_stat->global_auth_tok_sig, - ECRYPTFS_SIG_SIZE_HEX); - chosen_auth_tok = mount_crypt_stat->global_auth_tok; - } else - BUG(); /* We should always have a global auth tok in - * the 0.1 release */ - /* Scan list to see if our chosen_auth_tok works */ - list_for_each(walker, &auth_tok_list) { - struct ecryptfs_auth_tok_list_item *auth_tok_list_item; - auth_tok_list_item = - list_entry(walker, struct ecryptfs_auth_tok_list_item, - list); - candidate_auth_tok = &auth_tok_list_item->auth_tok; - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, - "Considering cadidate auth tok:\n"); - ecryptfs_dump_auth_tok(candidate_auth_tok); - } - /* TODO: Replace ECRYPTFS_SIG_SIZE_HEX w/ dynamic value */ - if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD - && !strncmp(candidate_auth_tok->token.password.signature, - sig, ECRYPTFS_SIG_SIZE_HEX)) { - found_auth_tok = 1; - goto leave_list; - /* TODO: Transfer the common salt into the - * crypt_stat salt */ - } - } -leave_list: - if (!found_auth_tok) { - ecryptfs_printk(KERN_ERR, "Could not find authentication " - "token on temporary list for sig [%.*s]\n", - ECRYPTFS_SIG_SIZE_HEX, sig); - rc = -EIO; - goto out_wipe_list; - } else { - memcpy(&(candidate_auth_tok->token.password), - &(chosen_auth_tok->token.password), - sizeof(struct ecryptfs_password)); - rc = decrypt_session_key(candidate_auth_tok, crypt_stat); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error decrypting the " - "session key\n"); - goto out_wipe_list; - } - rc = ecryptfs_compute_root_iv(crypt_stat); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error computing " - "the root IV\n"); - goto out_wipe_list; - } - } - rc = ecryptfs_init_crypt_ctx(crypt_stat); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error initializing crypto " - "context for cipher [%s]; rc = [%d]\n", - crypt_stat->cipher, rc); - } -out_wipe_list: - wipe_auth_tok_list(&auth_tok_list); -out: - return rc; -} - -/** - * write_tag_11_packet - * @dest: Target into which Tag 11 packet is to be written - * @max: Maximum packet length - * @contents: Byte array of contents to copy in - * @contents_length: Number of bytes in contents - * @packet_length: Length of the Tag 11 packet written; zero on error - * - * Returns zero on success; non-zero on error. - */ -static int -write_tag_11_packet(char *dest, int max, char *contents, size_t contents_length, - size_t *packet_length) -{ - int rc = 0; - size_t packet_size_length; - - (*packet_length) = 0; - if ((13 + contents_length) > max) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "Packet length larger than " - "maximum allowable\n"); - goto out; - } - /* General packet header */ - /* Packet tag */ - dest[(*packet_length)++] = ECRYPTFS_TAG_11_PACKET_TYPE; - /* Packet length */ - rc = write_packet_length(&dest[(*packet_length)], - (13 + contents_length), &packet_size_length); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error generating tag 11 packet " - "header; cannot generate packet length\n"); - goto out; - } - (*packet_length) += packet_size_length; - /* Tag 11 specific */ - /* One-octet field that describes how the data is formatted */ - dest[(*packet_length)++] = 0x62; /* binary data */ - /* One-octet filename length followed by filename */ - dest[(*packet_length)++] = 8; - memcpy(&dest[(*packet_length)], "_CONSOLE", 8); - (*packet_length) += 8; - /* Four-octet number indicating modification date */ - memset(&dest[(*packet_length)], 0x00, 4); - (*packet_length) += 4; - /* Remainder is literal data */ - memcpy(&dest[(*packet_length)], contents, contents_length); - (*packet_length) += contents_length; - out: - if (rc) - (*packet_length) = 0; - return rc; -} - -/** - * write_tag_3_packet - * @dest: Buffer into which to write the packet - * @max: Maximum number of bytes that can be written - * @auth_tok: Authentication token - * @crypt_stat: The cryptographic context - * @key_rec: encrypted key - * @packet_size: This function will write the number of bytes that end - * up constituting the packet; set to zero on error - * - * Returns zero on success; non-zero on error. - */ -static int -write_tag_3_packet(char *dest, size_t max, struct ecryptfs_auth_tok *auth_tok, - struct ecryptfs_crypt_stat *crypt_stat, - struct ecryptfs_key_record *key_rec, size_t *packet_size) -{ - int rc = 0; - - size_t i; - size_t signature_is_valid = 0; - size_t encrypted_session_key_valid = 0; - char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; - struct scatterlist dest_sg[2]; - struct scatterlist src_sg[2]; - struct crypto_tfm *tfm = NULL; - struct mutex *tfm_mutex = NULL; - size_t key_rec_size; - size_t packet_size_length; - size_t cipher_code; - - (*packet_size) = 0; - /* Check for a valid signature on the auth_tok */ - for (i = 0; i < ECRYPTFS_SIG_SIZE_HEX; i++) - signature_is_valid |= auth_tok->token.password.signature[i]; - if (!signature_is_valid) - BUG(); - ecryptfs_from_hex((*key_rec).sig, auth_tok->token.password.signature, - ECRYPTFS_SIG_SIZE); - encrypted_session_key_valid = 0; - for (i = 0; i < crypt_stat->key_size; i++) - encrypted_session_key_valid |= - auth_tok->session_key.encrypted_key[i]; - if (encrypted_session_key_valid) { - memcpy((*key_rec).enc_key, - auth_tok->session_key.encrypted_key, - auth_tok->session_key.encrypted_key_size); - goto encrypted_session_key_set; - } - if (auth_tok->session_key.encrypted_key_size == 0) - auth_tok->session_key.encrypted_key_size = - crypt_stat->key_size; - if (crypt_stat->key_size == 24 - && strcmp("aes", crypt_stat->cipher) == 0) { - memset((crypt_stat->key + 24), 0, 8); - auth_tok->session_key.encrypted_key_size = 32; - } - (*key_rec).enc_key_size = - auth_tok->session_key.encrypted_key_size; - if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags, - ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET)) { - ecryptfs_printk(KERN_DEBUG, "Using previously generated " - "session key encryption key of size [%d]\n", - auth_tok->token.password. - session_key_encryption_key_bytes); - memcpy(session_key_encryption_key, - auth_tok->token.password.session_key_encryption_key, - crypt_stat->key_size); - ecryptfs_printk(KERN_DEBUG, - "Cached session key " "encryption key: \n"); - if (ecryptfs_verbosity > 0) - ecryptfs_dump_hex(session_key_encryption_key, 16); - } - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Session key encryption key:\n"); - ecryptfs_dump_hex(session_key_encryption_key, 16); - } - rc = virt_to_scatterlist(crypt_stat->key, - (*key_rec).enc_key_size, src_sg, 2); - if (!rc) { - ecryptfs_printk(KERN_ERR, "Error generating scatterlist " - "for crypt_stat session key\n"); - rc = -ENOMEM; - goto out; - } - rc = virt_to_scatterlist((*key_rec).enc_key, - (*key_rec).enc_key_size, dest_sg, 2); - if (!rc) { - ecryptfs_printk(KERN_ERR, "Error generating scatterlist " - "for crypt_stat encrypted session key\n"); - rc = -ENOMEM; - goto out; - } - if (!strcmp(crypt_stat->cipher, - crypt_stat->mount_crypt_stat->global_default_cipher_name) - && crypt_stat->mount_crypt_stat->global_key_tfm) { - tfm = crypt_stat->mount_crypt_stat->global_key_tfm; - tfm_mutex = &crypt_stat->mount_crypt_stat->global_key_tfm_mutex; - } else - tfm = crypto_alloc_tfm(crypt_stat->cipher, 0); - if (!tfm) { - ecryptfs_printk(KERN_ERR, "Could not initialize crypto " - "context for cipher [%s]\n", - crypt_stat->cipher); - rc = -EINVAL; - goto out; - } - if (tfm_mutex) - mutex_lock(tfm_mutex); - rc = crypto_cipher_setkey(tfm, session_key_encryption_key, - crypt_stat->key_size); - if (rc < 0) { - if (tfm_mutex) - mutex_unlock(tfm_mutex); - ecryptfs_printk(KERN_ERR, "Error setting key for crypto " - "context\n"); - goto out; - } - rc = 0; - ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes of the key\n", - crypt_stat->key_size); - crypto_cipher_encrypt(tfm, dest_sg, src_sg, - (*key_rec).enc_key_size); - if (tfm_mutex) - mutex_unlock(tfm_mutex); - ecryptfs_printk(KERN_DEBUG, "This should be the encrypted key:\n"); - if (ecryptfs_verbosity > 0) - ecryptfs_dump_hex((*key_rec).enc_key, - (*key_rec).enc_key_size); -encrypted_session_key_set: - /* Now we have a valid key_rec. Append it to the - * key_rec set. */ - key_rec_size = (sizeof(struct ecryptfs_key_record) - - ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES - + ((*key_rec).enc_key_size)); - /* TODO: Include a packet size limit as a parameter to this - * function once we have multi-packet headers (for versions - * later than 0.1 */ - if (key_rec_size >= ECRYPTFS_MAX_KEYSET_SIZE) { - ecryptfs_printk(KERN_ERR, "Keyset too large\n"); - rc = -EINVAL; - goto out; - } - /* TODO: Packet size limit */ - /* We have 5 bytes of surrounding packet data */ - if ((0x05 + ECRYPTFS_SALT_SIZE - + (*key_rec).enc_key_size) >= max) { - ecryptfs_printk(KERN_ERR, "Authentication token is too " - "large\n"); - rc = -EINVAL; - goto out; - } - /* This format is inspired by OpenPGP; see RFC 2440 - * packet tag 3 */ - dest[(*packet_size)++] = ECRYPTFS_TAG_3_PACKET_TYPE; - /* ver+cipher+s2k+hash+salt+iter+enc_key */ - rc = write_packet_length(&dest[(*packet_size)], - (0x05 + ECRYPTFS_SALT_SIZE - + (*key_rec).enc_key_size), - &packet_size_length); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error generating tag 3 packet " - "header; cannot generate packet length\n"); - goto out; - } - (*packet_size) += packet_size_length; - dest[(*packet_size)++] = 0x04; /* version 4 */ - cipher_code = ecryptfs_code_for_cipher_string(crypt_stat); - if (cipher_code == 0) { - ecryptfs_printk(KERN_WARNING, "Unable to generate code for " - "cipher [%s]\n", crypt_stat->cipher); - rc = -EINVAL; - goto out; - } - dest[(*packet_size)++] = cipher_code; - dest[(*packet_size)++] = 0x03; /* S2K */ - dest[(*packet_size)++] = 0x01; /* MD5 (TODO: parameterize) */ - memcpy(&dest[(*packet_size)], auth_tok->token.password.salt, - ECRYPTFS_SALT_SIZE); - (*packet_size) += ECRYPTFS_SALT_SIZE; /* salt */ - dest[(*packet_size)++] = 0x60; /* hash iterations (65536) */ - memcpy(&dest[(*packet_size)], (*key_rec).enc_key, - (*key_rec).enc_key_size); - (*packet_size) += (*key_rec).enc_key_size; -out: - if (tfm && !tfm_mutex) - crypto_free_tfm(tfm); - if (rc) - (*packet_size) = 0; - return rc; -} - -/** - * ecryptfs_generate_key_packet_set - * @dest: Virtual address from which to write the key record set - * @crypt_stat: The cryptographic context from which the - * authentication tokens will be retrieved - * @ecryptfs_dentry: The dentry, used to retrieve the mount crypt stat - * for the global parameters - * @len: The amount written - * @max: The maximum amount of data allowed to be written - * - * Generates a key packet set and writes it to the virtual address - * passed in. - * - * Returns zero on success; non-zero on error. - */ -int -ecryptfs_generate_key_packet_set(char *dest_base, - struct ecryptfs_crypt_stat *crypt_stat, - struct dentry *ecryptfs_dentry, size_t *len, - size_t max) -{ - int rc = 0; - struct ecryptfs_auth_tok *auth_tok; - struct ecryptfs_mount_crypt_stat *mount_crypt_stat = - &ecryptfs_superblock_to_private( - ecryptfs_dentry->d_sb)->mount_crypt_stat; - size_t written; - struct ecryptfs_key_record key_rec; - - (*len) = 0; - if (mount_crypt_stat->global_auth_tok) { - auth_tok = mount_crypt_stat->global_auth_tok; - if (auth_tok->token_type == ECRYPTFS_PASSWORD) { - rc = write_tag_3_packet((dest_base + (*len)), - max, auth_tok, - crypt_stat, &key_rec, - &written); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error " - "writing tag 3 packet\n"); - goto out; - } - (*len) += written; - /* Write auth tok signature packet */ - rc = write_tag_11_packet( - (dest_base + (*len)), - (max - (*len)), - key_rec.sig, ECRYPTFS_SIG_SIZE, &written); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error writing " - "auth tok signature packet\n"); - goto out; - } - (*len) += written; - } else { - ecryptfs_printk(KERN_WARNING, "Unsupported " - "authentication token type\n"); - rc = -EINVAL; - goto out; - } - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error writing " - "authentication token packet with sig " - "= [%s]\n", - mount_crypt_stat->global_auth_tok_sig); - rc = -EIO; - goto out; - } - } else - BUG(); - if (likely((max - (*len)) > 0)) { - dest_base[(*len)] = 0x00; - } else { - ecryptfs_printk(KERN_ERR, "Error writing boundary byte\n"); - rc = -EIO; - } -out: - if (rc) - (*len) = 0; - return rc; -} diff --git a/trunk/fs/ecryptfs/main.c b/trunk/fs/ecryptfs/main.c deleted file mode 100644 index 7a11b8ae6644..000000000000 --- a/trunk/fs/ecryptfs/main.c +++ /dev/null @@ -1,831 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * - * Copyright (C) 1997-2003 Erez Zadok - * Copyright (C) 2001-2003 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * Michael C. Thompson - * - * 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 "ecryptfs_kernel.h" - -/** - * Module parameter that defines the ecryptfs_verbosity level. - */ -int ecryptfs_verbosity = 0; - -module_param(ecryptfs_verbosity, int, 0); -MODULE_PARM_DESC(ecryptfs_verbosity, - "Initial verbosity level (0 or 1; defaults to " - "0, which is Quiet)"); - -void __ecryptfs_printk(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - if (fmt[1] == '7') { /* KERN_DEBUG */ - if (ecryptfs_verbosity >= 1) - vprintk(fmt, args); - } else - vprintk(fmt, args); - va_end(args); -} - -/** - * ecryptfs_interpose - * @lower_dentry: Existing dentry in the lower filesystem - * @dentry: ecryptfs' dentry - * @sb: ecryptfs's super_block - * @flag: If set to true, then d_add is called, else d_instantiate is called - * - * Interposes upper and lower dentries. - * - * Returns zero on success; non-zero otherwise - */ -int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry, - struct super_block *sb, int flag) -{ - struct inode *lower_inode; - struct inode *inode; - int rc = 0; - - lower_inode = lower_dentry->d_inode; - if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) { - rc = -EXDEV; - goto out; - } - if (!igrab(lower_inode)) { - rc = -ESTALE; - goto out; - } - inode = iget5_locked(sb, (unsigned long)lower_inode, - ecryptfs_inode_test, ecryptfs_inode_set, - lower_inode); - if (!inode) { - rc = -EACCES; - iput(lower_inode); - goto out; - } - if (inode->i_state & I_NEW) - unlock_new_inode(inode); - else - iput(lower_inode); - if (S_ISLNK(lower_inode->i_mode)) - inode->i_op = &ecryptfs_symlink_iops; - else if (S_ISDIR(lower_inode->i_mode)) - inode->i_op = &ecryptfs_dir_iops; - if (S_ISDIR(lower_inode->i_mode)) - inode->i_fop = &ecryptfs_dir_fops; - /* TODO: Is there a better way to identify if the inode is - * special? */ - if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || - S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) - init_special_inode(inode, lower_inode->i_mode, - lower_inode->i_rdev); - dentry->d_op = &ecryptfs_dops; - if (flag) - d_add(dentry, inode); - else - d_instantiate(dentry, inode); - ecryptfs_copy_attr_all(inode, lower_inode); - /* This size will be overwritten for real files w/ headers and - * other metadata */ - ecryptfs_copy_inode_size(inode, lower_inode); -out: - return rc; -} - -enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_debug, - ecryptfs_opt_ecryptfs_debug, ecryptfs_opt_cipher, - ecryptfs_opt_ecryptfs_cipher, ecryptfs_opt_ecryptfs_key_bytes, - ecryptfs_opt_passthrough, ecryptfs_opt_err }; - -static match_table_t tokens = { - {ecryptfs_opt_sig, "sig=%s"}, - {ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"}, - {ecryptfs_opt_debug, "debug=%u"}, - {ecryptfs_opt_ecryptfs_debug, "ecryptfs_debug=%u"}, - {ecryptfs_opt_cipher, "cipher=%s"}, - {ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"}, - {ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"}, - {ecryptfs_opt_passthrough, "ecryptfs_passthrough"}, - {ecryptfs_opt_err, NULL} -}; - -/** - * ecryptfs_verify_version - * @version: The version number to confirm - * - * Returns zero on good version; non-zero otherwise - */ -static int ecryptfs_verify_version(u16 version) -{ - int rc = 0; - unsigned char major; - unsigned char minor; - - major = ((version >> 8) & 0xFF); - minor = (version & 0xFF); - if (major != ECRYPTFS_VERSION_MAJOR) { - ecryptfs_printk(KERN_ERR, "Major version number mismatch. " - "Expected [%d]; got [%d]\n", - ECRYPTFS_VERSION_MAJOR, major); - rc = -EINVAL; - goto out; - } - if (minor != ECRYPTFS_VERSION_MINOR) { - ecryptfs_printk(KERN_ERR, "Minor version number mismatch. " - "Expected [%d]; got [%d]\n", - ECRYPTFS_VERSION_MINOR, minor); - rc = -EINVAL; - goto out; - } -out: - return rc; -} - -/** - * ecryptfs_parse_options - * @sb: The ecryptfs super block - * @options: The options pased to the kernel - * - * Parse mount options: - * debug=N - ecryptfs_verbosity level for debug output - * sig=XXX - description(signature) of the key to use - * - * Returns the dentry object of the lower-level (lower/interposed) - * directory; We want to mount our stackable file system on top of - * that lower directory. - * - * The signature of the key to use must be the description of a key - * already in the keyring. Mounting will fail if the key can not be - * found. - * - * Returns zero on success; non-zero on error - */ -static int ecryptfs_parse_options(struct super_block *sb, char *options) -{ - char *p; - int rc = 0; - int sig_set = 0; - int cipher_name_set = 0; - int cipher_key_bytes; - int cipher_key_bytes_set = 0; - struct key *auth_tok_key = NULL; - struct ecryptfs_auth_tok *auth_tok = NULL; - struct ecryptfs_mount_crypt_stat *mount_crypt_stat = - &ecryptfs_superblock_to_private(sb)->mount_crypt_stat; - substring_t args[MAX_OPT_ARGS]; - int token; - char *sig_src; - char *sig_dst; - char *debug_src; - char *cipher_name_dst; - char *cipher_name_src; - char *cipher_key_bytes_src; - struct crypto_tfm *tmp_tfm; - int cipher_name_len; - - if (!options) { - rc = -EINVAL; - goto out; - } - while ((p = strsep(&options, ",")) != NULL) { - if (!*p) - continue; - token = match_token(p, tokens, args); - switch (token) { - case ecryptfs_opt_sig: - case ecryptfs_opt_ecryptfs_sig: - sig_src = args[0].from; - sig_dst = - mount_crypt_stat->global_auth_tok_sig; - memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX); - sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0'; - ecryptfs_printk(KERN_DEBUG, - "The mount_crypt_stat " - "global_auth_tok_sig set to: " - "[%s]\n", sig_dst); - sig_set = 1; - break; - case ecryptfs_opt_debug: - case ecryptfs_opt_ecryptfs_debug: - debug_src = args[0].from; - ecryptfs_verbosity = - (int)simple_strtol(debug_src, &debug_src, - 0); - ecryptfs_printk(KERN_DEBUG, - "Verbosity set to [%d]" "\n", - ecryptfs_verbosity); - break; - case ecryptfs_opt_cipher: - case ecryptfs_opt_ecryptfs_cipher: - cipher_name_src = args[0].from; - cipher_name_dst = - mount_crypt_stat-> - global_default_cipher_name; - strncpy(cipher_name_dst, cipher_name_src, - ECRYPTFS_MAX_CIPHER_NAME_SIZE); - ecryptfs_printk(KERN_DEBUG, - "The mount_crypt_stat " - "global_default_cipher_name set to: " - "[%s]\n", cipher_name_dst); - cipher_name_set = 1; - break; - case ecryptfs_opt_ecryptfs_key_bytes: - cipher_key_bytes_src = args[0].from; - cipher_key_bytes = - (int)simple_strtol(cipher_key_bytes_src, - &cipher_key_bytes_src, 0); - mount_crypt_stat->global_default_cipher_key_size = - cipher_key_bytes; - ecryptfs_printk(KERN_DEBUG, - "The mount_crypt_stat " - "global_default_cipher_key_size " - "set to: [%d]\n", mount_crypt_stat-> - global_default_cipher_key_size); - cipher_key_bytes_set = 1; - break; - case ecryptfs_opt_passthrough: - mount_crypt_stat->flags |= - ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED; - break; - case ecryptfs_opt_err: - default: - ecryptfs_printk(KERN_WARNING, - "eCryptfs: unrecognized option '%s'\n", - p); - } - } - /* Do not support lack of mount-wide signature in 0.1 - * release */ - if (!sig_set) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "You must supply a valid " - "passphrase auth tok signature as a mount " - "parameter; see the eCryptfs README\n"); - goto out; - } - if (!cipher_name_set) { - cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER); - if (unlikely(cipher_name_len - >= ECRYPTFS_MAX_CIPHER_NAME_SIZE)) { - rc = -EINVAL; - BUG(); - goto out; - } - memcpy(mount_crypt_stat->global_default_cipher_name, - ECRYPTFS_DEFAULT_CIPHER, cipher_name_len); - mount_crypt_stat->global_default_cipher_name[cipher_name_len] - = '\0'; - } - if (!cipher_key_bytes_set) { - mount_crypt_stat->global_default_cipher_key_size = - ECRYPTFS_DEFAULT_KEY_BYTES; - ecryptfs_printk(KERN_DEBUG, "Cipher key size was not " - "specified. Defaulting to [%d]\n", - mount_crypt_stat-> - global_default_cipher_key_size); - } - rc = ecryptfs_process_cipher( - &tmp_tfm, - &mount_crypt_stat->global_key_tfm, - mount_crypt_stat->global_default_cipher_name, - mount_crypt_stat->global_default_cipher_key_size); - if (tmp_tfm) - crypto_free_tfm(tmp_tfm); - if (rc) { - printk(KERN_ERR "Error attempting to initialize cipher [%s] " - "with key size [%Zd] bytes; rc = [%d]\n", - mount_crypt_stat->global_default_cipher_name, - mount_crypt_stat->global_default_cipher_key_size, rc); - rc = -EINVAL; - goto out; - } - mutex_init(&mount_crypt_stat->global_key_tfm_mutex); - ecryptfs_printk(KERN_DEBUG, "Requesting the key with description: " - "[%s]\n", mount_crypt_stat->global_auth_tok_sig); - /* The reference to this key is held until umount is done The - * call to key_put is done in ecryptfs_put_super() */ - auth_tok_key = request_key(&key_type_user, - mount_crypt_stat->global_auth_tok_sig, - NULL); - if (!auth_tok_key || IS_ERR(auth_tok_key)) { - ecryptfs_printk(KERN_ERR, "Could not find key with " - "description: [%s]\n", - mount_crypt_stat->global_auth_tok_sig); - process_request_key_err(PTR_ERR(auth_tok_key)); - rc = -EINVAL; - goto out; - } - auth_tok = ecryptfs_get_key_payload_data(auth_tok_key); - if (ecryptfs_verify_version(auth_tok->version)) { - ecryptfs_printk(KERN_ERR, "Data structure version mismatch. " - "Userspace tools must match eCryptfs kernel " - "module with major version [%d] and minor " - "version [%d]\n", ECRYPTFS_VERSION_MAJOR, - ECRYPTFS_VERSION_MINOR); - rc = -EINVAL; - goto out; - } - if (auth_tok->token_type != ECRYPTFS_PASSWORD) { - ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure " - "returned from key\n"); - rc = -EINVAL; - goto out; - } - mount_crypt_stat->global_auth_tok_key = auth_tok_key; - mount_crypt_stat->global_auth_tok = auth_tok; -out: - return rc; -} - -struct kmem_cache *ecryptfs_sb_info_cache; - -/** - * ecryptfs_fill_super - * @sb: The ecryptfs super block - * @raw_data: The options passed to mount - * @silent: Not used but required by function prototype - * - * Sets up what we can of the sb, rest is done in ecryptfs_read_super - * - * Returns zero on success; non-zero otherwise - */ -static int -ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent) -{ - int rc = 0; - - /* Released in ecryptfs_put_super() */ - ecryptfs_set_superblock_private(sb, - kmem_cache_alloc(ecryptfs_sb_info_cache, - SLAB_KERNEL)); - if (!ecryptfs_superblock_to_private(sb)) { - ecryptfs_printk(KERN_WARNING, "Out of memory\n"); - rc = -ENOMEM; - goto out; - } - memset(ecryptfs_superblock_to_private(sb), 0, - sizeof(struct ecryptfs_sb_info)); - sb->s_op = &ecryptfs_sops; - /* Released through deactivate_super(sb) from get_sb_nodev */ - sb->s_root = d_alloc(NULL, &(const struct qstr) { - .hash = 0,.name = "/",.len = 1}); - if (!sb->s_root) { - ecryptfs_printk(KERN_ERR, "d_alloc failed\n"); - rc = -ENOMEM; - goto out; - } - sb->s_root->d_op = &ecryptfs_dops; - sb->s_root->d_sb = sb; - sb->s_root->d_parent = sb->s_root; - /* Released in d_release when dput(sb->s_root) is called */ - /* through deactivate_super(sb) from get_sb_nodev() */ - ecryptfs_set_dentry_private(sb->s_root, - kmem_cache_alloc(ecryptfs_dentry_info_cache, - SLAB_KERNEL)); - if (!ecryptfs_dentry_to_private(sb->s_root)) { - ecryptfs_printk(KERN_ERR, - "dentry_info_cache alloc failed\n"); - rc = -ENOMEM; - goto out; - } - memset(ecryptfs_dentry_to_private(sb->s_root), 0, - sizeof(struct ecryptfs_dentry_info)); - rc = 0; -out: - /* Should be able to rely on deactivate_super called from - * get_sb_nodev */ - return rc; -} - -/** - * ecryptfs_read_super - * @sb: The ecryptfs super block - * @dev_name: The path to mount over - * - * Read the super block of the lower filesystem, and use - * ecryptfs_interpose to create our initial inode and super block - * struct. - */ -static int ecryptfs_read_super(struct super_block *sb, const char *dev_name) -{ - int rc; - struct nameidata nd; - struct dentry *lower_root; - struct vfsmount *lower_mnt; - - memset(&nd, 0, sizeof(struct nameidata)); - rc = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); - if (rc) { - ecryptfs_printk(KERN_WARNING, "path_lookup() failed\n"); - goto out_free; - } - lower_root = nd.dentry; - if (!lower_root->d_inode) { - ecryptfs_printk(KERN_WARNING, - "No directory to interpose on\n"); - rc = -ENOENT; - goto out_free; - } - lower_mnt = nd.mnt; - ecryptfs_set_superblock_lower(sb, lower_root->d_sb); - sb->s_maxbytes = lower_root->d_sb->s_maxbytes; - ecryptfs_set_dentry_lower(sb->s_root, lower_root); - ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt); - if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0))) - goto out_free; - rc = 0; - goto out; -out_free: - path_release(&nd); -out: - return rc; -} - -/** - * ecryptfs_get_sb - * @fs_type - * @flags - * @dev_name: The path to mount over - * @raw_data: The options passed into the kernel - * - * The whole ecryptfs_get_sb process is broken into 4 functions: - * ecryptfs_parse_options(): handle options passed to ecryptfs, if any - * ecryptfs_fill_super(): used by get_sb_nodev, fills out the super_block - * with as much information as it can before needing - * the lower filesystem. - * ecryptfs_read_super(): this accesses the lower filesystem and uses - * ecryptfs_interpolate to perform most of the linking - * ecryptfs_interpolate(): links the lower filesystem into ecryptfs - */ -static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data, - struct vfsmount *mnt) -{ - int rc; - struct super_block *sb; - - rc = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super, mnt); - if (rc < 0) { - printk(KERN_ERR "Getting sb failed; rc = [%d]\n", rc); - goto out; - } - sb = mnt->mnt_sb; - rc = ecryptfs_parse_options(sb, raw_data); - if (rc) { - printk(KERN_ERR "Error parsing options; rc = [%d]\n", rc); - goto out_abort; - } - rc = ecryptfs_read_super(sb, dev_name); - if (rc) { - printk(KERN_ERR "Reading sb failed; rc = [%d]\n", rc); - goto out_abort; - } - goto out; -out_abort: - dput(sb->s_root); - up_write(&sb->s_umount); - deactivate_super(sb); -out: - return rc; -} - -/** - * ecryptfs_kill_block_super - * @sb: The ecryptfs super block - * - * Used to bring the superblock down and free the private data. - * Private data is free'd in ecryptfs_put_super() - */ -static void ecryptfs_kill_block_super(struct super_block *sb) -{ - generic_shutdown_super(sb); -} - -static struct file_system_type ecryptfs_fs_type = { - .owner = THIS_MODULE, - .name = "ecryptfs", - .get_sb = ecryptfs_get_sb, - .kill_sb = ecryptfs_kill_block_super, - .fs_flags = 0 -}; - -/** - * inode_info_init_once - * - * Initializes the ecryptfs_inode_info_cache when it is created - */ -static void -inode_info_init_once(void *vptr, struct kmem_cache *cachep, unsigned long flags) -{ - struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr; - - if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) - inode_init_once(&ei->vfs_inode); -} - -static struct ecryptfs_cache_info { - kmem_cache_t **cache; - const char *name; - size_t size; - void (*ctor)(void*, struct kmem_cache *, unsigned long); -} ecryptfs_cache_infos[] = { - { - .cache = &ecryptfs_auth_tok_list_item_cache, - .name = "ecryptfs_auth_tok_list_item", - .size = sizeof(struct ecryptfs_auth_tok_list_item), - }, - { - .cache = &ecryptfs_file_info_cache, - .name = "ecryptfs_file_cache", - .size = sizeof(struct ecryptfs_file_info), - }, - { - .cache = &ecryptfs_dentry_info_cache, - .name = "ecryptfs_dentry_info_cache", - .size = sizeof(struct ecryptfs_dentry_info), - }, - { - .cache = &ecryptfs_inode_info_cache, - .name = "ecryptfs_inode_cache", - .size = sizeof(struct ecryptfs_inode_info), - .ctor = inode_info_init_once, - }, - { - .cache = &ecryptfs_sb_info_cache, - .name = "ecryptfs_sb_cache", - .size = sizeof(struct ecryptfs_sb_info), - }, - { - .cache = &ecryptfs_header_cache_0, - .name = "ecryptfs_headers_0", - .size = PAGE_CACHE_SIZE, - }, - { - .cache = &ecryptfs_header_cache_1, - .name = "ecryptfs_headers_1", - .size = PAGE_CACHE_SIZE, - }, - { - .cache = &ecryptfs_header_cache_2, - .name = "ecryptfs_headers_2", - .size = PAGE_CACHE_SIZE, - }, - { - .cache = &ecryptfs_lower_page_cache, - .name = "ecryptfs_lower_page_cache", - .size = PAGE_CACHE_SIZE, - }, -}; - -static void ecryptfs_free_kmem_caches(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) { - struct ecryptfs_cache_info *info; - - info = &ecryptfs_cache_infos[i]; - if (*(info->cache)) - kmem_cache_destroy(*(info->cache)); - } -} - -/** - * ecryptfs_init_kmem_caches - * - * Returns zero on success; non-zero otherwise - */ -static int ecryptfs_init_kmem_caches(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) { - struct ecryptfs_cache_info *info; - - info = &ecryptfs_cache_infos[i]; - *(info->cache) = kmem_cache_create(info->name, info->size, - 0, SLAB_HWCACHE_ALIGN, info->ctor, NULL); - if (!*(info->cache)) { - ecryptfs_free_kmem_caches(); - ecryptfs_printk(KERN_WARNING, "%s: " - "kmem_cache_create failed\n", - info->name); - return -ENOMEM; - } - } - return 0; -} - -struct ecryptfs_obj { - char *name; - struct list_head slot_list; - struct kobject kobj; -}; - -struct ecryptfs_attribute { - struct attribute attr; - ssize_t(*show) (struct ecryptfs_obj *, char *); - ssize_t(*store) (struct ecryptfs_obj *, const char *, size_t); -}; - -static ssize_t -ecryptfs_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t len) -{ - struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj, - kobj); - struct ecryptfs_attribute *attribute = - container_of(attr, struct ecryptfs_attribute, attr); - - return (attribute->store ? attribute->store(obj, buf, len) : 0); -} - -static ssize_t -ecryptfs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) -{ - struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj, - kobj); - struct ecryptfs_attribute *attribute = - container_of(attr, struct ecryptfs_attribute, attr); - - return (attribute->show ? attribute->show(obj, buf) : 0); -} - -static struct sysfs_ops ecryptfs_sysfs_ops = { - .show = ecryptfs_attr_show, - .store = ecryptfs_attr_store -}; - -static struct kobj_type ecryptfs_ktype = { - .sysfs_ops = &ecryptfs_sysfs_ops -}; - -static decl_subsys(ecryptfs, &ecryptfs_ktype, NULL); - -static ssize_t version_show(struct ecryptfs_obj *obj, char *buff) -{ - return snprintf(buff, PAGE_SIZE, "%d\n", ECRYPTFS_VERSIONING_MASK); -} - -static struct ecryptfs_attribute sysfs_attr_version = __ATTR_RO(version); - -struct ecryptfs_version_str_map_elem { - u32 flag; - char *str; -} ecryptfs_version_str_map[] = { - {ECRYPTFS_VERSIONING_PASSPHRASE, "passphrase"}, - {ECRYPTFS_VERSIONING_PUBKEY, "pubkey"}, - {ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH, "plaintext passthrough"}, - {ECRYPTFS_VERSIONING_POLICY, "policy"} -}; - -static ssize_t version_str_show(struct ecryptfs_obj *obj, char *buff) -{ - int i; - int remaining = PAGE_SIZE; - int total_written = 0; - - buff[0] = '\0'; - for (i = 0; i < ARRAY_SIZE(ecryptfs_version_str_map); i++) { - int entry_size; - - if (!(ECRYPTFS_VERSIONING_MASK - & ecryptfs_version_str_map[i].flag)) - continue; - entry_size = strlen(ecryptfs_version_str_map[i].str); - if ((entry_size + 2) > remaining) - goto out; - memcpy(buff, ecryptfs_version_str_map[i].str, entry_size); - buff[entry_size++] = '\n'; - buff[entry_size] = '\0'; - buff += entry_size; - total_written += entry_size; - remaining -= entry_size; - } -out: - return total_written; -} - -static struct ecryptfs_attribute sysfs_attr_version_str = __ATTR_RO(version_str); - -static int do_sysfs_registration(void) -{ - int rc; - - if ((rc = subsystem_register(&ecryptfs_subsys))) { - printk(KERN_ERR - "Unable to register ecryptfs sysfs subsystem\n"); - goto out; - } - rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj, - &sysfs_attr_version.attr); - if (rc) { - printk(KERN_ERR - "Unable to create ecryptfs version attribute\n"); - subsystem_unregister(&ecryptfs_subsys); - goto out; - } - rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj, - &sysfs_attr_version_str.attr); - if (rc) { - printk(KERN_ERR - "Unable to create ecryptfs version_str attribute\n"); - sysfs_remove_file(&ecryptfs_subsys.kset.kobj, - &sysfs_attr_version.attr); - subsystem_unregister(&ecryptfs_subsys); - goto out; - } -out: - return rc; -} - -static int __init ecryptfs_init(void) -{ - int rc; - - if (ECRYPTFS_DEFAULT_EXTENT_SIZE > PAGE_CACHE_SIZE) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "The eCryptfs extent size is " - "larger than the host's page size, and so " - "eCryptfs cannot run on this system. The " - "default eCryptfs extent size is [%d] bytes; " - "the page size is [%d] bytes.\n", - ECRYPTFS_DEFAULT_EXTENT_SIZE, PAGE_CACHE_SIZE); - goto out; - } - rc = ecryptfs_init_kmem_caches(); - if (rc) { - printk(KERN_ERR - "Failed to allocate one or more kmem_cache objects\n"); - goto out; - } - rc = register_filesystem(&ecryptfs_fs_type); - if (rc) { - printk(KERN_ERR "Failed to register filesystem\n"); - ecryptfs_free_kmem_caches(); - goto out; - } - kset_set_kset_s(&ecryptfs_subsys, fs_subsys); - sysfs_attr_version.attr.owner = THIS_MODULE; - sysfs_attr_version_str.attr.owner = THIS_MODULE; - rc = do_sysfs_registration(); - if (rc) { - printk(KERN_ERR "sysfs registration failed\n"); - unregister_filesystem(&ecryptfs_fs_type); - ecryptfs_free_kmem_caches(); - goto out; - } -out: - return rc; -} - -static void __exit ecryptfs_exit(void) -{ - sysfs_remove_file(&ecryptfs_subsys.kset.kobj, - &sysfs_attr_version.attr); - sysfs_remove_file(&ecryptfs_subsys.kset.kobj, - &sysfs_attr_version_str.attr); - subsystem_unregister(&ecryptfs_subsys); - unregister_filesystem(&ecryptfs_fs_type); - ecryptfs_free_kmem_caches(); -} - -MODULE_AUTHOR("Michael A. Halcrow "); -MODULE_DESCRIPTION("eCryptfs"); - -MODULE_LICENSE("GPL"); - -module_init(ecryptfs_init) -module_exit(ecryptfs_exit) diff --git a/trunk/fs/ecryptfs/mmap.c b/trunk/fs/ecryptfs/mmap.c deleted file mode 100644 index 924dd90a4cf5..000000000000 --- a/trunk/fs/ecryptfs/mmap.c +++ /dev/null @@ -1,788 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * This is where eCryptfs coordinates the symmetric encryption and - * decryption of the file data as it passes between the lower - * encrypted file and the upper decrypted file. - * - * Copyright (C) 1997-2003 Erez Zadok - * Copyright (C) 2001-2003 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * - * 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 "ecryptfs_kernel.h" - -struct kmem_cache *ecryptfs_lower_page_cache; - -/** - * ecryptfs_get1page - * - * Get one page from cache or lower f/s, return error otherwise. - * - * Returns unlocked and up-to-date page (if ok), with increased - * refcnt. - */ -static struct page *ecryptfs_get1page(struct file *file, int index) -{ - struct page *page; - struct dentry *dentry; - struct inode *inode; - struct address_space *mapping; - - dentry = file->f_dentry; - inode = dentry->d_inode; - mapping = inode->i_mapping; - page = read_cache_page(mapping, index, - (filler_t *)mapping->a_ops->readpage, - (void *)file); - if (IS_ERR(page)) - goto out; - wait_on_page_locked(page); -out: - return page; -} - -static -int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros); - -/** - * ecryptfs_fill_zeros - * @file: The ecryptfs file - * @new_length: The new length of the data in the underlying file; - * everything between the prior end of the file and the - * new end of the file will be filled with zero's. - * new_length must be greater than current length - * - * Function for handling lseek-ing past the end of the file. - * - * This function does not support shrinking, only growing a file. - * - * Returns zero on success; non-zero otherwise. - */ -int ecryptfs_fill_zeros(struct file *file, loff_t new_length) -{ - int rc = 0; - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - pgoff_t old_end_page_index = 0; - pgoff_t index = old_end_page_index; - int old_end_pos_in_page = -1; - pgoff_t new_end_page_index; - int new_end_pos_in_page; - loff_t cur_length = i_size_read(inode); - - if (cur_length != 0) { - index = old_end_page_index = - ((cur_length - 1) >> PAGE_CACHE_SHIFT); - old_end_pos_in_page = ((cur_length - 1) & ~PAGE_CACHE_MASK); - } - new_end_page_index = ((new_length - 1) >> PAGE_CACHE_SHIFT); - new_end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK); - ecryptfs_printk(KERN_DEBUG, "old_end_page_index = [0x%.16x]; " - "old_end_pos_in_page = [%d]; " - "new_end_page_index = [0x%.16x]; " - "new_end_pos_in_page = [%d]\n", - old_end_page_index, old_end_pos_in_page, - new_end_page_index, new_end_pos_in_page); - if (old_end_page_index == new_end_page_index) { - /* Start and end are in the same page; we just need to - * set a portion of the existing page to zero's */ - rc = write_zeros(file, index, (old_end_pos_in_page + 1), - (new_end_pos_in_page - old_end_pos_in_page)); - if (rc) - ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " - "index=[0x%.16x], " - "old_end_pos_in_page=[d], " - "(PAGE_CACHE_SIZE - new_end_pos_in_page" - "=[%d]" - ")=[d]) returned [%d]\n", file, index, - old_end_pos_in_page, - new_end_pos_in_page, - (PAGE_CACHE_SIZE - new_end_pos_in_page), - rc); - goto out; - } - /* Fill the remainder of the previous last page with zeros */ - rc = write_zeros(file, index, (old_end_pos_in_page + 1), - ((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page)); - if (rc) { - ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " - "index=[0x%.16x], old_end_pos_in_page=[d], " - "(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) " - "returned [%d]\n", file, index, - old_end_pos_in_page, - (PAGE_CACHE_SIZE - old_end_pos_in_page), rc); - goto out; - } - index++; - while (index < new_end_page_index) { - /* Fill all intermediate pages with zeros */ - rc = write_zeros(file, index, 0, PAGE_CACHE_SIZE); - if (rc) { - ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], " - "index=[0x%.16x], " - "old_end_pos_in_page=[d], " - "(PAGE_CACHE_SIZE - new_end_pos_in_page" - "=[%d]" - ")=[d]) returned [%d]\n", file, index, - old_end_pos_in_page, - new_end_pos_in_page, - (PAGE_CACHE_SIZE - new_end_pos_in_page), - rc); - goto out; - } - index++; - } - /* Fill the portion at the beginning of the last new page with - * zero's */ - rc = write_zeros(file, index, 0, (new_end_pos_in_page + 1)); - if (rc) { - ecryptfs_printk(KERN_ERR, "write_zeros(file=" - "[%p], index=[0x%.16x], 0, " - "new_end_pos_in_page=[%d]" - "returned [%d]\n", file, index, - new_end_pos_in_page, rc); - goto out; - } -out: - return rc; -} - -/** - * ecryptfs_writepage - * @page: Page that is locked before this call is made - * - * Returns zero on success; non-zero otherwise - */ -static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc) -{ - struct ecryptfs_page_crypt_context ctx; - int rc; - - ctx.page = page; - ctx.mode = ECRYPTFS_WRITEPAGE_MODE; - ctx.param.wbc = wbc; - rc = ecryptfs_encrypt_page(&ctx); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error encrypting " - "page (upper index [0x%.16x])\n", page->index); - ClearPageUptodate(page); - goto out; - } - SetPageUptodate(page); - unlock_page(page); -out: - return rc; -} - -/** - * Reads the data from the lower file file at index lower_page_index - * and copies that data into page. - * - * @param page Page to fill - * @param lower_page_index Index of the page in the lower file to get - */ -int ecryptfs_do_readpage(struct file *file, struct page *page, - pgoff_t lower_page_index) -{ - int rc; - struct dentry *dentry; - struct file *lower_file; - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; - char *page_data; - struct page *lower_page = NULL; - char *lower_page_data; - const struct address_space_operations *lower_a_ops; - - dentry = file->f_dentry; - lower_file = ecryptfs_file_to_lower(file); - lower_dentry = ecryptfs_dentry_to_lower(dentry); - inode = dentry->d_inode; - lower_inode = ecryptfs_inode_to_lower(inode); - lower_a_ops = lower_inode->i_mapping->a_ops; - lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index, - (filler_t *)lower_a_ops->readpage, - (void *)lower_file); - if (IS_ERR(lower_page)) { - rc = PTR_ERR(lower_page); - lower_page = NULL; - ecryptfs_printk(KERN_ERR, "Error reading from page cache\n"); - goto out; - } - wait_on_page_locked(lower_page); - page_data = (char *)kmap(page); - if (!page_data) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Error mapping page\n"); - goto out; - } - lower_page_data = (char *)kmap(lower_page); - if (!lower_page_data) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Error mapping page\n"); - kunmap(page); - goto out; - } - memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE); - kunmap(lower_page); - kunmap(page); - rc = 0; -out: - if (likely(lower_page)) - page_cache_release(lower_page); - if (rc == 0) - SetPageUptodate(page); - else - ClearPageUptodate(page); - return rc; -} - -/** - * ecryptfs_readpage - * @file: This is an ecryptfs file - * @page: ecryptfs associated page to stick the read data into - * - * Read in a page, decrypting if necessary. - * - * Returns zero on success; non-zero on error. - */ -static int ecryptfs_readpage(struct file *file, struct page *page) -{ - int rc = 0; - struct ecryptfs_crypt_stat *crypt_stat; - - BUG_ON(!(file && file->f_dentry && file->f_dentry->d_inode)); - crypt_stat = - &ecryptfs_inode_to_private(file->f_dentry->d_inode)->crypt_stat; - if (!crypt_stat - || !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED) - || ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) { - ecryptfs_printk(KERN_DEBUG, - "Passing through unencrypted page\n"); - rc = ecryptfs_do_readpage(file, page, page->index); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error reading page; rc = " - "[%d]\n", rc); - goto out; - } - } else { - rc = ecryptfs_decrypt_page(file, page); - if (rc) { - - ecryptfs_printk(KERN_ERR, "Error decrypting page; " - "rc = [%d]\n", rc); - goto out; - } - } - SetPageUptodate(page); -out: - if (rc) - ClearPageUptodate(page); - ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n", - page->index); - unlock_page(page); - return rc; -} - -static int fill_zeros_to_end_of_page(struct page *page, unsigned int to) -{ - struct inode *inode = page->mapping->host; - int end_byte_in_page; - int rc = 0; - char *page_virt; - - if ((i_size_read(inode) / PAGE_CACHE_SIZE) == page->index) { - end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE; - if (to > end_byte_in_page) - end_byte_in_page = to; - page_virt = kmap(page); - if (!page_virt) { - rc = -ENOMEM; - ecryptfs_printk(KERN_WARNING, - "Could not map page\n"); - goto out; - } - memset((page_virt + end_byte_in_page), 0, - (PAGE_CACHE_SIZE - end_byte_in_page)); - kunmap(page); - } -out: - return rc; -} - -static int ecryptfs_prepare_write(struct file *file, struct page *page, - unsigned from, unsigned to) -{ - int rc = 0; - - kmap(page); - if (from == 0 && to == PAGE_CACHE_SIZE) - goto out; /* If we are writing a full page, it will be - up to date. */ - if (!PageUptodate(page)) - rc = ecryptfs_do_readpage(file, page, page->index); -out: - return rc; -} - -int ecryptfs_grab_and_map_lower_page(struct page **lower_page, - char **lower_virt, - struct inode *lower_inode, - unsigned long lower_page_index) -{ - int rc = 0; - - (*lower_page) = grab_cache_page(lower_inode->i_mapping, - lower_page_index); - if (!(*lower_page)) { - ecryptfs_printk(KERN_ERR, "grab_cache_page for " - "lower_page_index = [0x%.16x] failed\n", - lower_page_index); - rc = -EINVAL; - goto out; - } - if (lower_virt) - (*lower_virt) = kmap((*lower_page)); - else - kmap((*lower_page)); -out: - return rc; -} - -int ecryptfs_writepage_and_release_lower_page(struct page *lower_page, - struct inode *lower_inode, - struct writeback_control *wbc) -{ - int rc = 0; - - rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error calling lower writepage(); " - "rc = [%d]\n", rc); - goto out; - } - lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; - page_cache_release(lower_page); -out: - return rc; -} - -static void ecryptfs_unmap_and_release_lower_page(struct page *lower_page) -{ - kunmap(lower_page); - ecryptfs_printk(KERN_DEBUG, "Unlocking lower page with index = " - "[0x%.16x]\n", lower_page->index); - unlock_page(lower_page); - page_cache_release(lower_page); -} - -/** - * ecryptfs_write_inode_size_to_header - * - * Writes the lower file size to the first 8 bytes of the header. - * - * Returns zero on success; non-zero on error. - */ -int -ecryptfs_write_inode_size_to_header(struct file *lower_file, - struct inode *lower_inode, - struct inode *inode) -{ - int rc = 0; - struct page *header_page; - char *header_virt; - const struct address_space_operations *lower_a_ops; - u64 file_size; - - rc = ecryptfs_grab_and_map_lower_page(&header_page, &header_virt, - lower_inode, 0); - if (rc) { - ecryptfs_printk(KERN_ERR, "grab_cache_page for header page " - "failed\n"); - goto out; - } - lower_a_ops = lower_inode->i_mapping->a_ops; - rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8); - file_size = (u64)i_size_read(inode); - ecryptfs_printk(KERN_DEBUG, "Writing size: [0x%.16x]\n", file_size); - file_size = cpu_to_be64(file_size); - memcpy(header_virt, &file_size, sizeof(u64)); - rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8); - if (rc < 0) - ecryptfs_printk(KERN_ERR, "Error commiting header page " - "write\n"); - ecryptfs_unmap_and_release_lower_page(header_page); - lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(inode); -out: - return rc; -} - -int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode, - struct file *lower_file, - unsigned long lower_page_index, int byte_offset, - int region_bytes) -{ - int rc = 0; - - rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, lower_inode, - lower_page_index); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting to grab and map " - "lower page with index [0x%.16x]\n", - lower_page_index); - goto out; - } - rc = lower_inode->i_mapping->a_ops->prepare_write(lower_file, - (*lower_page), - byte_offset, - region_bytes); - if (rc) { - ecryptfs_printk(KERN_ERR, "prepare_write for " - "lower_page_index = [0x%.16x] failed; rc = " - "[%d]\n", lower_page_index, rc); - } -out: - if (rc && (*lower_page)) { - ecryptfs_unmap_and_release_lower_page(*lower_page); - (*lower_page) = NULL; - } - return rc; -} - -/** - * ecryptfs_commit_lower_page - * - * Returns zero on success; non-zero on error - */ -int -ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode, - struct file *lower_file, int byte_offset, - int region_size) -{ - int rc = 0; - - rc = lower_inode->i_mapping->a_ops->commit_write( - lower_file, lower_page, byte_offset, region_size); - if (rc < 0) { - ecryptfs_printk(KERN_ERR, - "Error committing write; rc = [%d]\n", rc); - } else - rc = 0; - ecryptfs_unmap_and_release_lower_page(lower_page); - return rc; -} - -/** - * ecryptfs_copy_page_to_lower - * - * Used for plaintext pass-through; no page index interpolation - * required. - */ -int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode, - struct file *lower_file) -{ - int rc = 0; - struct page *lower_page; - - rc = ecryptfs_get_lower_page(&lower_page, lower_inode, lower_file, - page->index, 0, PAGE_CACHE_SIZE); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting to get page " - "at index [0x%.16x]\n", page->index); - goto out; - } - /* TODO: aops */ - memcpy((char *)page_address(lower_page), page_address(page), - PAGE_CACHE_SIZE); - rc = ecryptfs_commit_lower_page(lower_page, lower_inode, lower_file, - 0, PAGE_CACHE_SIZE); - if (rc) - ecryptfs_printk(KERN_ERR, "Error attempting to commit page " - "at index [0x%.16x]\n", page->index); -out: - return rc; -} - -static int -process_new_file(struct ecryptfs_crypt_stat *crypt_stat, - struct file *file, struct inode *inode) -{ - struct page *header_page; - const struct address_space_operations *lower_a_ops; - struct inode *lower_inode; - struct file *lower_file; - char *header_virt; - int rc = 0; - int current_header_page = 0; - int header_pages; - int more_header_data_to_be_written = 1; - - lower_inode = ecryptfs_inode_to_lower(inode); - lower_file = ecryptfs_file_to_lower(file); - lower_a_ops = lower_inode->i_mapping->a_ops; - header_pages = ((crypt_stat->header_extent_size - * crypt_stat->num_header_extents_at_front) - / PAGE_CACHE_SIZE); - BUG_ON(header_pages < 1); - while (current_header_page < header_pages) { - rc = ecryptfs_grab_and_map_lower_page(&header_page, - &header_virt, - lower_inode, - current_header_page); - if (rc) { - ecryptfs_printk(KERN_ERR, "grab_cache_page for " - "header page [%d] failed; rc = [%d]\n", - current_header_page, rc); - goto out; - } - rc = lower_a_ops->prepare_write(lower_file, header_page, 0, - PAGE_CACHE_SIZE); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error preparing to write " - "header page out; rc = [%d]\n", rc); - goto out; - } - memset(header_virt, 0, PAGE_CACHE_SIZE); - if (more_header_data_to_be_written) { - rc = ecryptfs_write_headers_virt(header_virt, - crypt_stat, - file->f_dentry); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error " - "generating header; rc = " - "[%d]\n", rc); - rc = -EIO; - memset(header_virt, 0, PAGE_CACHE_SIZE); - ecryptfs_unmap_and_release_lower_page( - header_page); - goto out; - } - if (current_header_page == 0) - memset(header_virt, 0, 8); - more_header_data_to_be_written = 0; - } - rc = lower_a_ops->commit_write(lower_file, header_page, 0, - PAGE_CACHE_SIZE); - ecryptfs_unmap_and_release_lower_page(header_page); - if (rc < 0) { - ecryptfs_printk(KERN_ERR, - "Error commiting header page write; " - "rc = [%d]\n", rc); - break; - } - current_header_page++; - } - if (rc >= 0) { - rc = 0; - ecryptfs_printk(KERN_DEBUG, "lower_inode->i_blocks = " - "[0x%.16x]\n", lower_inode->i_blocks); - i_size_write(inode, 0); - lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(inode); - } - ecryptfs_printk(KERN_DEBUG, "Clearing ECRYPTFS_NEW_FILE flag in " - "crypt_stat at memory location [%p]\n", crypt_stat); - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE); -out: - return rc; -} - -/** - * ecryptfs_commit_write - * @file: The eCryptfs file object - * @page: The eCryptfs page - * @from: Ignored (we rotate the page IV on each write) - * @to: Ignored - * - * This is where we encrypt the data and pass the encrypted data to - * the lower filesystem. In OpenPGP-compatible mode, we operate on - * entire underlying packets. - */ -static int ecryptfs_commit_write(struct file *file, struct page *page, - unsigned from, unsigned to) -{ - struct ecryptfs_page_crypt_context ctx; - loff_t pos; - struct inode *inode; - struct inode *lower_inode; - struct file *lower_file; - struct ecryptfs_crypt_stat *crypt_stat; - int rc; - - inode = page->mapping->host; - lower_inode = ecryptfs_inode_to_lower(inode); - lower_file = ecryptfs_file_to_lower(file); - mutex_lock(&lower_inode->i_mutex); - crypt_stat = - &ecryptfs_inode_to_private(file->f_dentry->d_inode)->crypt_stat; - if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) { - ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_NEW_FILE flag set in " - "crypt_stat at memory location [%p]\n", crypt_stat); - rc = process_new_file(crypt_stat, file, inode); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error processing new " - "file; rc = [%d]\n", rc); - goto out; - } - } else - ecryptfs_printk(KERN_DEBUG, "Not a new file\n"); - ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page" - "(page w/ index = [0x%.16x], to = [%d])\n", page->index, - to); - rc = fill_zeros_to_end_of_page(page, to); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error attempting to fill " - "zeros in page with index = [0x%.16x]\n", - page->index); - goto out; - } - ctx.page = page; - ctx.mode = ECRYPTFS_PREPARE_COMMIT_MODE; - ctx.param.lower_file = lower_file; - rc = ecryptfs_encrypt_page(&ctx); - if (rc) { - ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper " - "index [0x%.16x])\n", page->index); - goto out; - } - rc = 0; - inode->i_blocks = lower_inode->i_blocks; - pos = (page->index << PAGE_CACHE_SHIFT) + to; - if (pos > i_size_read(inode)) { - i_size_write(inode, pos); - ecryptfs_printk(KERN_DEBUG, "Expanded file size to " - "[0x%.16x]\n", i_size_read(inode)); - } - ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode); - lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty_sync(inode); -out: - kunmap(page); /* mapped in prior call (prepare_write) */ - if (rc < 0) - ClearPageUptodate(page); - else - SetPageUptodate(page); - mutex_unlock(&lower_inode->i_mutex); - return rc; -} - -/** - * write_zeros - * @file: The ecryptfs file - * @index: The index in which we are writing - * @start: The position after the last block of data - * @num_zeros: The number of zeros to write - * - * Write a specified number of zero's to a page. - * - * (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE - */ -static -int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros) -{ - int rc = 0; - struct page *tmp_page; - - tmp_page = ecryptfs_get1page(file, index); - if (IS_ERR(tmp_page)) { - ecryptfs_printk(KERN_ERR, "Error getting page at index " - "[0x%.16x]\n", index); - rc = PTR_ERR(tmp_page); - goto out; - } - kmap(tmp_page); - rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error preparing to write zero's " - "to remainder of page at index [0x%.16x]\n", - index); - kunmap(tmp_page); - page_cache_release(tmp_page); - goto out; - } - memset(((char *)page_address(tmp_page) + start), 0, num_zeros); - rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros); - if (rc < 0) { - ecryptfs_printk(KERN_ERR, "Error attempting to write zero's " - "to remainder of page at index [0x%.16x]\n", - index); - kunmap(tmp_page); - page_cache_release(tmp_page); - goto out; - } - rc = 0; - kunmap(tmp_page); - page_cache_release(tmp_page); -out: - return rc; -} - -static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block) -{ - int rc = 0; - struct inode *inode; - struct inode *lower_inode; - - inode = (struct inode *)mapping->host; - lower_inode = ecryptfs_inode_to_lower(inode); - if (lower_inode->i_mapping->a_ops->bmap) - rc = lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping, - block); - return rc; -} - -static void ecryptfs_sync_page(struct page *page) -{ - struct inode *inode; - struct inode *lower_inode; - struct page *lower_page; - - inode = page->mapping->host; - lower_inode = ecryptfs_inode_to_lower(inode); - /* NOTE: Recently swapped with grab_cache_page(), since - * sync_page() just makes sure that pending I/O gets done. */ - lower_page = find_lock_page(lower_inode->i_mapping, page->index); - if (!lower_page) { - ecryptfs_printk(KERN_DEBUG, "find_lock_page failed\n"); - return; - } - lower_page->mapping->a_ops->sync_page(lower_page); - ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n", - lower_page->index); - unlock_page(lower_page); - page_cache_release(lower_page); -} - -struct address_space_operations ecryptfs_aops = { - .writepage = ecryptfs_writepage, - .readpage = ecryptfs_readpage, - .prepare_write = ecryptfs_prepare_write, - .commit_write = ecryptfs_commit_write, - .bmap = ecryptfs_bmap, - .sync_page = ecryptfs_sync_page, -}; diff --git a/trunk/fs/ecryptfs/super.c b/trunk/fs/ecryptfs/super.c deleted file mode 100644 index c337c0410fb1..000000000000 --- a/trunk/fs/ecryptfs/super.c +++ /dev/null @@ -1,198 +0,0 @@ -/** - * eCryptfs: Linux filesystem encryption layer - * - * Copyright (C) 1997-2003 Erez Zadok - * Copyright (C) 2001-2003 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. - * Author(s): Michael A. Halcrow - * Michael C. Thompson - * - * 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 "ecryptfs_kernel.h" - -struct kmem_cache *ecryptfs_inode_info_cache; - -/** - * ecryptfs_alloc_inode - allocate an ecryptfs inode - * @sb: Pointer to the ecryptfs super block - * - * Called to bring an inode into existence. - * - * Only handle allocation, setting up structures should be done in - * ecryptfs_read_inode. This is because the kernel, between now and - * then, will 0 out the private data pointer. - * - * Returns a pointer to a newly allocated inode, NULL otherwise - */ -static struct inode *ecryptfs_alloc_inode(struct super_block *sb) -{ - struct ecryptfs_inode_info *ecryptfs_inode; - struct inode *inode = NULL; - - ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache, - SLAB_KERNEL); - if (unlikely(!ecryptfs_inode)) - goto out; - ecryptfs_init_crypt_stat(&ecryptfs_inode->crypt_stat); - inode = &ecryptfs_inode->vfs_inode; -out: - return inode; -} - -/** - * ecryptfs_destroy_inode - * @inode: The ecryptfs inode - * - * This is used during the final destruction of the inode. - * All allocation of memory related to the inode, including allocated - * memory in the crypt_stat struct, will be released here. - * There should be no chance that this deallocation will be missed. - */ -static void ecryptfs_destroy_inode(struct inode *inode) -{ - struct ecryptfs_inode_info *inode_info; - - inode_info = ecryptfs_inode_to_private(inode); - ecryptfs_destruct_crypt_stat(&inode_info->crypt_stat); - kmem_cache_free(ecryptfs_inode_info_cache, inode_info); -} - -/** - * ecryptfs_init_inode - * @inode: The ecryptfs inode - * - * Set up the ecryptfs inode. - */ -void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode) -{ - ecryptfs_set_inode_lower(inode, lower_inode); - inode->i_ino = lower_inode->i_ino; - inode->i_version++; - inode->i_op = &ecryptfs_main_iops; - inode->i_fop = &ecryptfs_main_fops; - inode->i_mapping->a_ops = &ecryptfs_aops; -} - -/** - * ecryptfs_put_super - * @sb: Pointer to the ecryptfs super block - * - * Final actions when unmounting a file system. - * This will handle deallocation and release of our private data. - */ -static void ecryptfs_put_super(struct super_block *sb) -{ - struct ecryptfs_sb_info *sb_info = ecryptfs_superblock_to_private(sb); - - ecryptfs_destruct_mount_crypt_stat(&sb_info->mount_crypt_stat); - kmem_cache_free(ecryptfs_sb_info_cache, sb_info); - ecryptfs_set_superblock_private(sb, NULL); -} - -/** - * ecryptfs_statfs - * @sb: The ecryptfs super block - * @buf: The struct kstatfs to fill in with stats - * - * Get the filesystem statistics. Currently, we let this pass right through - * to the lower filesystem and take no action ourselves. - */ -static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - return vfs_statfs(ecryptfs_dentry_to_lower(dentry), buf); -} - -/** - * ecryptfs_clear_inode - * @inode - The ecryptfs inode - * - * Called by iput() when the inode reference count reached zero - * and the inode is not hashed anywhere. Used to clear anything - * that needs to be, before the inode is completely destroyed and put - * on the inode free list. We use this to drop out reference to the - * lower inode. - */ -static void ecryptfs_clear_inode(struct inode *inode) -{ - iput(ecryptfs_inode_to_lower(inode)); -} - -/** - * ecryptfs_umount_begin - * - * Called in do_umount(). - */ -static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags) -{ - struct vfsmount *lower_mnt = - ecryptfs_dentry_to_lower_mnt(vfsmnt->mnt_sb->s_root); - struct super_block *lower_sb; - - mntput(lower_mnt); - lower_sb = lower_mnt->mnt_sb; - if (lower_sb->s_op->umount_begin) - lower_sb->s_op->umount_begin(lower_mnt, flags); -} - -/** - * ecryptfs_show_options - * - * Prints the directory we are currently mounted over. - * Returns zero on success; non-zero otherwise - */ -static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt) -{ - struct super_block *sb = mnt->mnt_sb; - struct dentry *lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root); - struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(sb->s_root); - char *tmp_page; - char *path; - int rc = 0; - - tmp_page = (char *)__get_free_page(GFP_KERNEL); - if (!tmp_page) { - rc = -ENOMEM; - goto out; - } - path = d_path(lower_root_dentry, lower_mnt, tmp_page, PAGE_SIZE); - if (IS_ERR(path)) { - rc = PTR_ERR(path); - goto out; - } - seq_printf(m, ",dir=%s", path); - free_page((unsigned long)tmp_page); -out: - return rc; -} - -struct super_operations ecryptfs_sops = { - .alloc_inode = ecryptfs_alloc_inode, - .destroy_inode = ecryptfs_destroy_inode, - .drop_inode = generic_delete_inode, - .put_super = ecryptfs_put_super, - .statfs = ecryptfs_statfs, - .remount_fs = NULL, - .clear_inode = ecryptfs_clear_inode, - .umount_begin = ecryptfs_umount_begin, - .show_options = ecryptfs_show_options -}; diff --git a/trunk/fs/gfs2/Kconfig b/trunk/fs/gfs2/Kconfig deleted file mode 100644 index 8c27de8b9568..000000000000 --- a/trunk/fs/gfs2/Kconfig +++ /dev/null @@ -1,44 +0,0 @@ -config GFS2_FS - tristate "GFS2 file system support" - depends on EXPERIMENTAL - select FS_POSIX_ACL - help - A cluster filesystem. - - Allows a cluster of computers to simultaneously use a block device - that is shared between them (with FC, iSCSI, NBD, etc...). GFS reads - and writes to the block device like a local filesystem, but also uses - a lock module to allow the computers coordinate their I/O so - filesystem consistency is maintained. One of the nifty features of - GFS is perfect consistency -- changes made to the filesystem on one - machine show up immediately on all other machines in the cluster. - - To use the GFS2 filesystem, you will need to enable one or more of - the below locking modules. Documentation and utilities for GFS2 can - be found here: http://sources.redhat.com/cluster - -config GFS2_FS_LOCKING_NOLOCK - tristate "GFS2 \"nolock\" locking module" - depends on GFS2_FS - help - Single node locking module for GFS2. - - Use this module if you want to use GFS2 on a single node without - its clustering features. You can still take advantage of the - large file support, and upgrade to running a full cluster later on - if required. - - If you will only be using GFS2 in cluster mode, you do not need this - module. - -config GFS2_FS_LOCKING_DLM - tristate "GFS2 DLM locking module" - depends on GFS2_FS - select DLM - help - Multiple node locking module for GFS2 - - Most users of GFS2 will require this module. It provides the locking - interface between GFS2 and the DLM, which is required to use GFS2 - in a cluster environment. - diff --git a/trunk/fs/gfs2/Makefile b/trunk/fs/gfs2/Makefile deleted file mode 100644 index e3f1ada643ac..000000000000 --- a/trunk/fs/gfs2/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -obj-$(CONFIG_GFS2_FS) += gfs2.o -gfs2-y := acl.o bmap.o daemon.o dir.o eaops.o eattr.o glock.o \ - glops.o inode.o lm.o log.o lops.o locking.o main.o meta_io.o \ - mount.o ondisk.o ops_address.o ops_dentry.o ops_export.o ops_file.o \ - ops_fstype.o ops_inode.o ops_super.o ops_vm.o quota.o \ - recovery.o rgrp.o super.o sys.o trans.o util.o - -obj-$(CONFIG_GFS2_FS_LOCKING_NOLOCK) += locking/nolock/ -obj-$(CONFIG_GFS2_FS_LOCKING_DLM) += locking/dlm/ - diff --git a/trunk/fs/gfs2/acl.c b/trunk/fs/gfs2/acl.c deleted file mode 100644 index 5f959b8ce406..000000000000 --- a/trunk/fs/gfs2/acl.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "acl.h" -#include "eaops.h" -#include "eattr.h" -#include "glock.h" -#include "inode.h" -#include "meta_io.h" -#include "trans.h" -#include "util.h" - -#define ACL_ACCESS 1 -#define ACL_DEFAULT 0 - -int gfs2_acl_validate_set(struct gfs2_inode *ip, int access, - struct gfs2_ea_request *er, - int *remove, mode_t *mode) -{ - struct posix_acl *acl; - int error; - - error = gfs2_acl_validate_remove(ip, access); - if (error) - return error; - - if (!er->er_data) - return -EINVAL; - - acl = posix_acl_from_xattr(er->er_data, er->er_data_len); - if (IS_ERR(acl)) - return PTR_ERR(acl); - if (!acl) { - *remove = 1; - return 0; - } - - error = posix_acl_valid(acl); - if (error) - goto out; - - if (access) { - error = posix_acl_equiv_mode(acl, mode); - if (!error) - *remove = 1; - else if (error > 0) - error = 0; - } - -out: - posix_acl_release(acl); - return error; -} - -int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access) -{ - if (!GFS2_SB(&ip->i_inode)->sd_args.ar_posix_acl) - return -EOPNOTSUPP; - if (current->fsuid != ip->i_di.di_uid && !capable(CAP_FOWNER)) - return -EPERM; - if (S_ISLNK(ip->i_di.di_mode)) - return -EOPNOTSUPP; - if (!access && !S_ISDIR(ip->i_di.di_mode)) - return -EACCES; - - return 0; -} - -static int acl_get(struct gfs2_inode *ip, int access, struct posix_acl **acl, - struct gfs2_ea_location *el, char **data, unsigned int *len) -{ - struct gfs2_ea_request er; - struct gfs2_ea_location el_this; - int error; - - if (!ip->i_di.di_eattr) - return 0; - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - if (access) { - er.er_name = GFS2_POSIX_ACL_ACCESS; - er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN; - } else { - er.er_name = GFS2_POSIX_ACL_DEFAULT; - er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN; - } - er.er_type = GFS2_EATYPE_SYS; - - if (!el) - el = &el_this; - - error = gfs2_ea_find(ip, &er, el); - if (error) - return error; - if (!el->el_ea) - return 0; - if (!GFS2_EA_DATA_LEN(el->el_ea)) - goto out; - - er.er_data_len = GFS2_EA_DATA_LEN(el->el_ea); - er.er_data = kmalloc(er.er_data_len, GFP_KERNEL); - error = -ENOMEM; - if (!er.er_data) - goto out; - - error = gfs2_ea_get_copy(ip, el, er.er_data); - if (error) - goto out_kfree; - - if (acl) { - *acl = posix_acl_from_xattr(er.er_data, er.er_data_len); - if (IS_ERR(*acl)) - error = PTR_ERR(*acl); - } - -out_kfree: - if (error || !data) - kfree(er.er_data); - else { - *data = er.er_data; - *len = er.er_data_len; - } -out: - if (error || el == &el_this) - brelse(el->el_bh); - return error; -} - -/** - * gfs2_check_acl_locked - Check an ACL to see if we're allowed to do something - * @inode: the file we want to do something to - * @mask: what we want to do - * - * Returns: errno - */ - -int gfs2_check_acl_locked(struct inode *inode, int mask) -{ - struct posix_acl *acl = NULL; - int error; - - error = acl_get(GFS2_I(inode), ACL_ACCESS, &acl, NULL, NULL, NULL); - if (error) - return error; - - if (acl) { - error = posix_acl_permission(inode, acl, mask); - posix_acl_release(acl); - return error; - } - - return -EAGAIN; -} - -int gfs2_check_acl(struct inode *inode, int mask) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder i_gh; - int error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (!error) { - error = gfs2_check_acl_locked(inode, mask); - gfs2_glock_dq_uninit(&i_gh); - } - - return error; -} - -static int munge_mode(struct gfs2_inode *ip, mode_t mode) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head *dibh; - int error; - - error = gfs2_trans_begin(sdp, RES_DINODE, 0); - if (error) - return error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - gfs2_assert_withdraw(sdp, - (ip->i_di.di_mode & S_IFMT) == (mode & S_IFMT)); - ip->i_di.di_mode = mode; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(sdp); - - return 0; -} - -int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct posix_acl *acl = NULL, *clone; - struct gfs2_ea_request er; - mode_t mode = ip->i_di.di_mode; - int error; - - if (!sdp->sd_args.ar_posix_acl) - return 0; - if (S_ISLNK(ip->i_di.di_mode)) - return 0; - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - er.er_type = GFS2_EATYPE_SYS; - - error = acl_get(dip, ACL_DEFAULT, &acl, NULL, - &er.er_data, &er.er_data_len); - if (error) - return error; - if (!acl) { - mode &= ~current->fs->umask; - if (mode != ip->i_di.di_mode) - error = munge_mode(ip, mode); - return error; - } - - clone = posix_acl_clone(acl, GFP_KERNEL); - error = -ENOMEM; - if (!clone) - goto out; - posix_acl_release(acl); - acl = clone; - - if (S_ISDIR(ip->i_di.di_mode)) { - er.er_name = GFS2_POSIX_ACL_DEFAULT; - er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN; - error = gfs2_system_eaops.eo_set(ip, &er); - if (error) - goto out; - } - - error = posix_acl_create_masq(acl, &mode); - if (error < 0) - goto out; - if (error > 0) { - er.er_name = GFS2_POSIX_ACL_ACCESS; - er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN; - posix_acl_to_xattr(acl, er.er_data, er.er_data_len); - er.er_mode = mode; - er.er_flags = GFS2_ERF_MODE; - error = gfs2_system_eaops.eo_set(ip, &er); - if (error) - goto out; - } else - munge_mode(ip, mode); - -out: - posix_acl_release(acl); - kfree(er.er_data); - return error; -} - -int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr) -{ - struct posix_acl *acl = NULL, *clone; - struct gfs2_ea_location el; - char *data; - unsigned int len; - int error; - - error = acl_get(ip, ACL_ACCESS, &acl, &el, &data, &len); - if (error) - return error; - if (!acl) - return gfs2_setattr_simple(ip, attr); - - clone = posix_acl_clone(acl, GFP_KERNEL); - error = -ENOMEM; - if (!clone) - goto out; - posix_acl_release(acl); - acl = clone; - - error = posix_acl_chmod_masq(acl, attr->ia_mode); - if (!error) { - posix_acl_to_xattr(acl, data, len); - error = gfs2_ea_acl_chmod(ip, &el, attr, data); - } - -out: - posix_acl_release(acl); - brelse(el.el_bh); - kfree(data); - return error; -} - diff --git a/trunk/fs/gfs2/acl.h b/trunk/fs/gfs2/acl.h deleted file mode 100644 index 05c294fe0d78..000000000000 --- a/trunk/fs/gfs2/acl.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __ACL_DOT_H__ -#define __ACL_DOT_H__ - -#include "incore.h" - -#define GFS2_POSIX_ACL_ACCESS "posix_acl_access" -#define GFS2_POSIX_ACL_ACCESS_LEN 16 -#define GFS2_POSIX_ACL_DEFAULT "posix_acl_default" -#define GFS2_POSIX_ACL_DEFAULT_LEN 17 - -#define GFS2_ACL_IS_ACCESS(name, len) \ - ((len) == GFS2_POSIX_ACL_ACCESS_LEN && \ - !memcmp(GFS2_POSIX_ACL_ACCESS, (name), (len))) - -#define GFS2_ACL_IS_DEFAULT(name, len) \ - ((len) == GFS2_POSIX_ACL_DEFAULT_LEN && \ - !memcmp(GFS2_POSIX_ACL_DEFAULT, (name), (len))) - -struct gfs2_ea_request; - -int gfs2_acl_validate_set(struct gfs2_inode *ip, int access, - struct gfs2_ea_request *er, - int *remove, mode_t *mode); -int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access); -int gfs2_check_acl_locked(struct inode *inode, int mask); -int gfs2_check_acl(struct inode *inode, int mask); -int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip); -int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr); - -#endif /* __ACL_DOT_H__ */ diff --git a/trunk/fs/gfs2/bmap.c b/trunk/fs/gfs2/bmap.c deleted file mode 100644 index cc57f2ecd219..000000000000 --- a/trunk/fs/gfs2/bmap.c +++ /dev/null @@ -1,1221 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "inode.h" -#include "meta_io.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "dir.h" -#include "util.h" -#include "ops_address.h" - -/* This doesn't need to be that large as max 64 bit pointers in a 4k - * block is 512, so __u16 is fine for that. It saves stack space to - * keep it small. - */ -struct metapath { - __u16 mp_list[GFS2_MAX_META_HEIGHT]; -}; - -typedef int (*block_call_t) (struct gfs2_inode *ip, struct buffer_head *dibh, - struct buffer_head *bh, u64 *top, - u64 *bottom, unsigned int height, - void *data); - -struct strip_mine { - int sm_first; - unsigned int sm_height; -}; - -/** - * gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page - * @ip: the inode - * @dibh: the dinode buffer - * @block: the block number that was allocated - * @private: any locked page held by the caller process - * - * Returns: errno - */ - -static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, - u64 block, struct page *page) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct inode *inode = &ip->i_inode; - struct buffer_head *bh; - int release = 0; - - if (!page || page->index) { - page = grab_cache_page(inode->i_mapping, 0); - if (!page) - return -ENOMEM; - release = 1; - } - - if (!PageUptodate(page)) { - void *kaddr = kmap(page); - - memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), - ip->i_di.di_size); - memset(kaddr + ip->i_di.di_size, 0, - PAGE_CACHE_SIZE - ip->i_di.di_size); - kunmap(page); - - SetPageUptodate(page); - } - - if (!page_has_buffers(page)) - create_empty_buffers(page, 1 << inode->i_blkbits, - (1 << BH_Uptodate)); - - bh = page_buffers(page); - - if (!buffer_mapped(bh)) - map_bh(bh, inode->i_sb, block); - - set_buffer_uptodate(bh); - if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) - gfs2_trans_add_bh(ip->i_gl, bh, 0); - mark_buffer_dirty(bh); - - if (release) { - unlock_page(page); - page_cache_release(page); - } - - return 0; -} - -/** - * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big - * @ip: The GFS2 inode to unstuff - * @unstuffer: the routine that handles unstuffing a non-zero length file - * @private: private data for the unstuffer - * - * This routine unstuffs a dinode and returns it to a "normal" state such - * that the height can be grown in the traditional way. - * - * Returns: errno - */ - -int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page) -{ - struct buffer_head *bh, *dibh; - struct gfs2_dinode *di; - u64 block = 0; - int isdir = gfs2_is_dir(ip); - int error; - - down_write(&ip->i_rw_mutex); - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out; - - if (ip->i_di.di_size) { - /* Get a free block, fill it with the stuffed data, - and write it out to disk */ - - if (isdir) { - block = gfs2_alloc_meta(ip); - - error = gfs2_dir_get_new_buffer(ip, block, &bh); - if (error) - goto out_brelse; - gfs2_buffer_copy_tail(bh, sizeof(struct gfs2_meta_header), - dibh, sizeof(struct gfs2_dinode)); - brelse(bh); - } else { - block = gfs2_alloc_data(ip); - - error = gfs2_unstuffer_page(ip, dibh, block, page); - if (error) - goto out_brelse; - } - } - - /* Set up the pointer to the new block */ - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - di = (struct gfs2_dinode *)dibh->b_data; - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - - if (ip->i_di.di_size) { - *(__be64 *)(di + 1) = cpu_to_be64(block); - ip->i_di.di_blocks++; - di->di_blocks = cpu_to_be64(ip->i_di.di_blocks); - } - - ip->i_di.di_height = 1; - di->di_height = cpu_to_be16(1); - -out_brelse: - brelse(dibh); -out: - up_write(&ip->i_rw_mutex); - return error; -} - -/** - * calc_tree_height - Calculate the height of a metadata tree - * @ip: The GFS2 inode - * @size: The proposed size of the file - * - * Work out how tall a metadata tree needs to be in order to accommodate a - * file of a particular size. If size is less than the current size of - * the inode, then the current size of the inode is used instead of the - * supplied one. - * - * Returns: the height the tree should be - */ - -static unsigned int calc_tree_height(struct gfs2_inode *ip, u64 size) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - u64 *arr; - unsigned int max, height; - - if (ip->i_di.di_size > size) - size = ip->i_di.di_size; - - if (gfs2_is_dir(ip)) { - arr = sdp->sd_jheightsize; - max = sdp->sd_max_jheight; - } else { - arr = sdp->sd_heightsize; - max = sdp->sd_max_height; - } - - for (height = 0; height < max; height++) - if (arr[height] >= size) - break; - - return height; -} - -/** - * build_height - Build a metadata tree of the requested height - * @ip: The GFS2 inode - * @height: The height to build to - * - * - * Returns: errno - */ - -static int build_height(struct inode *inode, unsigned height) -{ - struct gfs2_inode *ip = GFS2_I(inode); - unsigned new_height = height - ip->i_di.di_height; - struct buffer_head *dibh; - struct buffer_head *blocks[GFS2_MAX_META_HEIGHT]; - struct gfs2_dinode *di; - int error; - u64 *bp; - u64 bn; - unsigned n; - - if (height <= ip->i_di.di_height) - return 0; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - for(n = 0; n < new_height; n++) { - bn = gfs2_alloc_meta(ip); - blocks[n] = gfs2_meta_new(ip->i_gl, bn); - gfs2_trans_add_bh(ip->i_gl, blocks[n], 1); - } - - n = 0; - bn = blocks[0]->b_blocknr; - if (new_height > 1) { - for(; n < new_height-1; n++) { - gfs2_metatype_set(blocks[n], GFS2_METATYPE_IN, - GFS2_FORMAT_IN); - gfs2_buffer_clear_tail(blocks[n], - sizeof(struct gfs2_meta_header)); - bp = (u64 *)(blocks[n]->b_data + - sizeof(struct gfs2_meta_header)); - *bp = cpu_to_be64(blocks[n+1]->b_blocknr); - brelse(blocks[n]); - blocks[n] = NULL; - } - } - gfs2_metatype_set(blocks[n], GFS2_METATYPE_IN, GFS2_FORMAT_IN); - gfs2_buffer_copy_tail(blocks[n], sizeof(struct gfs2_meta_header), - dibh, sizeof(struct gfs2_dinode)); - brelse(blocks[n]); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - di = (struct gfs2_dinode *)dibh->b_data; - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - *(__be64 *)(di + 1) = cpu_to_be64(bn); - ip->i_di.di_height += new_height; - ip->i_di.di_blocks += new_height; - di->di_height = cpu_to_be16(ip->i_di.di_height); - di->di_blocks = cpu_to_be64(ip->i_di.di_blocks); - brelse(dibh); - return error; -} - -/** - * find_metapath - Find path through the metadata tree - * @ip: The inode pointer - * @mp: The metapath to return the result in - * @block: The disk block to look up - * - * This routine returns a struct metapath structure that defines a path - * through the metadata of inode "ip" to get to block "block". - * - * Example: - * Given: "ip" is a height 3 file, "offset" is 101342453, and this is a - * filesystem with a blocksize of 4096. - * - * find_metapath() would return a struct metapath structure set to: - * mp_offset = 101342453, mp_height = 3, mp_list[0] = 0, mp_list[1] = 48, - * and mp_list[2] = 165. - * - * That means that in order to get to the block containing the byte at - * offset 101342453, we would load the indirect block pointed to by pointer - * 0 in the dinode. We would then load the indirect block pointed to by - * pointer 48 in that indirect block. We would then load the data block - * pointed to by pointer 165 in that indirect block. - * - * ---------------------------------------- - * | Dinode | | - * | | 4| - * | |0 1 2 3 4 5 9| - * | | 6| - * ---------------------------------------- - * | - * | - * V - * ---------------------------------------- - * | Indirect Block | - * | 5| - * | 4 4 4 4 4 5 5 1| - * |0 5 6 7 8 9 0 1 2| - * ---------------------------------------- - * | - * | - * V - * ---------------------------------------- - * | Indirect Block | - * | 1 1 1 1 1 5| - * | 6 6 6 6 6 1| - * |0 3 4 5 6 7 2| - * ---------------------------------------- - * | - * | - * V - * ---------------------------------------- - * | Data block containing offset | - * | 101342453 | - * | | - * | | - * ---------------------------------------- - * - */ - -static void find_metapath(struct gfs2_inode *ip, u64 block, - struct metapath *mp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - u64 b = block; - unsigned int i; - - for (i = ip->i_di.di_height; i--;) - mp->mp_list[i] = do_div(b, sdp->sd_inptrs); - -} - -/** - * metapointer - Return pointer to start of metadata in a buffer - * @bh: The buffer - * @height: The metadata height (0 = dinode) - * @mp: The metapath - * - * Return a pointer to the block number of the next height of the metadata - * tree given a buffer containing the pointer to the current height of the - * metadata tree. - */ - -static inline u64 *metapointer(struct buffer_head *bh, int *boundary, - unsigned int height, const struct metapath *mp) -{ - unsigned int head_size = (height > 0) ? - sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode); - u64 *ptr; - *boundary = 0; - ptr = ((u64 *)(bh->b_data + head_size)) + mp->mp_list[height]; - if (ptr + 1 == (u64 *)(bh->b_data + bh->b_size)) - *boundary = 1; - return ptr; -} - -/** - * lookup_block - Get the next metadata block in metadata tree - * @ip: The GFS2 inode - * @bh: Buffer containing the pointers to metadata blocks - * @height: The height of the tree (0 = dinode) - * @mp: The metapath - * @create: Non-zero if we may create a new meatdata block - * @new: Used to indicate if we did create a new metadata block - * @block: the returned disk block number - * - * Given a metatree, complete to a particular height, checks to see if the next - * height of the tree exists. If not the next height of the tree is created. - * The block number of the next height of the metadata tree is returned. - * - */ - -static int lookup_block(struct gfs2_inode *ip, struct buffer_head *bh, - unsigned int height, struct metapath *mp, int create, - int *new, u64 *block) -{ - int boundary; - u64 *ptr = metapointer(bh, &boundary, height, mp); - - if (*ptr) { - *block = be64_to_cpu(*ptr); - return boundary; - } - - *block = 0; - - if (!create) - return 0; - - if (height == ip->i_di.di_height - 1 && !gfs2_is_dir(ip)) - *block = gfs2_alloc_data(ip); - else - *block = gfs2_alloc_meta(ip); - - gfs2_trans_add_bh(ip->i_gl, bh, 1); - - *ptr = cpu_to_be64(*block); - ip->i_di.di_blocks++; - - *new = 1; - return 0; -} - -/** - * gfs2_block_pointers - Map a block from an inode to a disk block - * @inode: The inode - * @lblock: The logical block number - * @map_bh: The bh to be mapped - * @mp: metapath to use - * - * Find the block number on the current device which corresponds to an - * inode's block. If the block had to be created, "new" will be set. - * - * Returns: errno - */ - -static int gfs2_block_pointers(struct inode *inode, u64 lblock, int create, - struct buffer_head *bh_map, struct metapath *mp, - unsigned int maxlen) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct buffer_head *bh; - unsigned int bsize; - unsigned int height; - unsigned int end_of_metadata; - unsigned int x; - int error = 0; - int new = 0; - u64 dblock = 0; - int boundary; - - BUG_ON(maxlen == 0); - - if (gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip))) - return 0; - - bsize = gfs2_is_dir(ip) ? sdp->sd_jbsize : sdp->sd_sb.sb_bsize; - - height = calc_tree_height(ip, (lblock + 1) * bsize); - if (ip->i_di.di_height < height) { - if (!create) - return 0; - - error = build_height(inode, height); - if (error) - return error; - } - - find_metapath(ip, lblock, mp); - end_of_metadata = ip->i_di.di_height - 1; - - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - return error; - - for (x = 0; x < end_of_metadata; x++) { - lookup_block(ip, bh, x, mp, create, &new, &dblock); - brelse(bh); - if (!dblock) - return 0; - - error = gfs2_meta_indirect_buffer(ip, x+1, dblock, new, &bh); - if (error) - return error; - } - - boundary = lookup_block(ip, bh, end_of_metadata, mp, create, &new, &dblock); - clear_buffer_mapped(bh_map); - clear_buffer_new(bh_map); - clear_buffer_boundary(bh_map); - - if (dblock) { - map_bh(bh_map, inode->i_sb, dblock); - if (boundary) - set_buffer_boundary(bh); - if (new) { - struct buffer_head *dibh; - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - set_buffer_new(bh_map); - goto out_brelse; - } - while(--maxlen && !buffer_boundary(bh_map)) { - u64 eblock; - - mp->mp_list[end_of_metadata]++; - boundary = lookup_block(ip, bh, end_of_metadata, mp, 0, &new, &eblock); - if (eblock != ++dblock) - break; - bh_map->b_size += (1 << inode->i_blkbits); - if (boundary) - set_buffer_boundary(bh_map); - } - } -out_brelse: - brelse(bh); - return 0; -} - - -static inline void bmap_lock(struct inode *inode, int create) -{ - struct gfs2_inode *ip = GFS2_I(inode); - if (create) - down_write(&ip->i_rw_mutex); - else - down_read(&ip->i_rw_mutex); -} - -static inline void bmap_unlock(struct inode *inode, int create) -{ - struct gfs2_inode *ip = GFS2_I(inode); - if (create) - up_write(&ip->i_rw_mutex); - else - up_read(&ip->i_rw_mutex); -} - -int gfs2_block_map(struct inode *inode, u64 lblock, int create, - struct buffer_head *bh, unsigned int maxlen) -{ - struct metapath mp; - int ret; - - bmap_lock(inode, create); - ret = gfs2_block_pointers(inode, lblock, create, bh, &mp, maxlen); - bmap_unlock(inode, create); - return ret; -} - -int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen) -{ - struct metapath mp; - struct buffer_head bh = { .b_state = 0, .b_blocknr = 0, .b_size = 0 }; - int ret; - int create = *new; - - BUG_ON(!extlen); - BUG_ON(!dblock); - BUG_ON(!new); - - bmap_lock(inode, create); - ret = gfs2_block_pointers(inode, lblock, create, &bh, &mp, 32); - bmap_unlock(inode, create); - *extlen = bh.b_size >> inode->i_blkbits; - *dblock = bh.b_blocknr; - if (buffer_new(&bh)) - *new = 1; - else - *new = 0; - return ret; -} - -/** - * recursive_scan - recursively scan through the end of a file - * @ip: the inode - * @dibh: the dinode buffer - * @mp: the path through the metadata to the point to start - * @height: the height the recursion is at - * @block: the indirect block to look at - * @first: 1 if this is the first block - * @bc: the call to make for each piece of metadata - * @data: data opaque to this function to pass to @bc - * - * When this is first called @height and @block should be zero and - * @first should be 1. - * - * Returns: errno - */ - -static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, - struct metapath *mp, unsigned int height, - u64 block, int first, block_call_t bc, - void *data) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head *bh = NULL; - u64 *top, *bottom; - u64 bn; - int error; - int mh_size = sizeof(struct gfs2_meta_header); - - if (!height) { - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - return error; - dibh = bh; - - top = (u64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + mp->mp_list[0]; - bottom = (u64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + sdp->sd_diptrs; - } else { - error = gfs2_meta_indirect_buffer(ip, height, block, 0, &bh); - if (error) - return error; - - top = (u64 *)(bh->b_data + mh_size) + - (first ? mp->mp_list[height] : 0); - - bottom = (u64 *)(bh->b_data + mh_size) + sdp->sd_inptrs; - } - - error = bc(ip, dibh, bh, top, bottom, height, data); - if (error) - goto out; - - if (height < ip->i_di.di_height - 1) - for (; top < bottom; top++, first = 0) { - if (!*top) - continue; - - bn = be64_to_cpu(*top); - - error = recursive_scan(ip, dibh, mp, height + 1, bn, - first, bc, data); - if (error) - break; - } - -out: - brelse(bh); - return error; -} - -/** - * do_strip - Look for a layer a particular layer of the file and strip it off - * @ip: the inode - * @dibh: the dinode buffer - * @bh: A buffer of pointers - * @top: The first pointer in the buffer - * @bottom: One more than the last pointer - * @height: the height this buffer is at - * @data: a pointer to a struct strip_mine - * - * Returns: errno - */ - -static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, - struct buffer_head *bh, u64 *top, u64 *bottom, - unsigned int height, void *data) -{ - struct strip_mine *sm = data; - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_rgrp_list rlist; - u64 bn, bstart; - u32 blen; - u64 *p; - unsigned int rg_blocks = 0; - int metadata; - unsigned int revokes = 0; - int x; - int error; - - if (!*top) - sm->sm_first = 0; - - if (height != sm->sm_height) - return 0; - - if (sm->sm_first) { - top++; - sm->sm_first = 0; - } - - metadata = (height != ip->i_di.di_height - 1); - if (metadata) - revokes = (height) ? sdp->sd_inptrs : sdp->sd_diptrs; - - error = gfs2_rindex_hold(sdp, &ip->i_alloc.al_ri_gh); - if (error) - return error; - - memset(&rlist, 0, sizeof(struct gfs2_rgrp_list)); - bstart = 0; - blen = 0; - - for (p = top; p < bottom; p++) { - if (!*p) - continue; - - bn = be64_to_cpu(*p); - - if (bstart + blen == bn) - blen++; - else { - if (bstart) - gfs2_rlist_add(sdp, &rlist, bstart); - - bstart = bn; - blen = 1; - } - } - - if (bstart) - gfs2_rlist_add(sdp, &rlist, bstart); - else - goto out; /* Nothing to do */ - - gfs2_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0); - - for (x = 0; x < rlist.rl_rgrps; x++) { - struct gfs2_rgrpd *rgd; - rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; - } - - error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); - if (error) - goto out_rlist; - - error = gfs2_trans_begin(sdp, rg_blocks + RES_DINODE + - RES_INDIRECT + RES_STATFS + RES_QUOTA, - revokes); - if (error) - goto out_rg_gunlock; - - down_write(&ip->i_rw_mutex); - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - - bstart = 0; - blen = 0; - - for (p = top; p < bottom; p++) { - if (!*p) - continue; - - bn = be64_to_cpu(*p); - - if (bstart + blen == bn) - blen++; - else { - if (bstart) { - if (metadata) - gfs2_free_meta(ip, bstart, blen); - else - gfs2_free_data(ip, bstart, blen); - } - - bstart = bn; - blen = 1; - } - - *p = 0; - if (!ip->i_di.di_blocks) - gfs2_consist_inode(ip); - ip->i_di.di_blocks--; - } - if (bstart) { - if (metadata) - gfs2_free_meta(ip, bstart, blen); - else - gfs2_free_data(ip, bstart, blen); - } - - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - - gfs2_dinode_out(&ip->i_di, dibh->b_data); - - up_write(&ip->i_rw_mutex); - - gfs2_trans_end(sdp); - -out_rg_gunlock: - gfs2_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs); -out_rlist: - gfs2_rlist_free(&rlist); -out: - gfs2_glock_dq_uninit(&ip->i_alloc.al_ri_gh); - return error; -} - -/** - * do_grow - Make a file look bigger than it is - * @ip: the inode - * @size: the size to set the file to - * - * Called with an exclusive lock on @ip. - * - * Returns: errno - */ - -static int do_grow(struct gfs2_inode *ip, u64 size) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al; - struct buffer_head *dibh; - unsigned int h; - int error; - - al = gfs2_alloc_get(ip); - - error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out; - - error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); - if (error) - goto out_gunlock_q; - - al->al_requested = sdp->sd_max_height + RES_DATA; - - error = gfs2_inplace_reserve(ip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, - sdp->sd_max_height + al->al_rgd->rd_ri.ri_length + - RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA, 0); - if (error) - goto out_ipres; - - if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { - if (gfs2_is_stuffed(ip)) { - error = gfs2_unstuff_dinode(ip, NULL); - if (error) - goto out_end_trans; - } - - h = calc_tree_height(ip, size); - if (ip->i_di.di_height < h) { - down_write(&ip->i_rw_mutex); - error = build_height(&ip->i_inode, h); - up_write(&ip->i_rw_mutex); - if (error) - goto out_end_trans; - } - } - - ip->i_di.di_size = size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - -out_end_trans: - gfs2_trans_end(sdp); -out_ipres: - gfs2_inplace_release(ip); -out_gunlock_q: - gfs2_quota_unlock(ip); -out: - gfs2_alloc_put(ip); - return error; -} - - -/** - * gfs2_block_truncate_page - Deal with zeroing out data for truncate - * - * This is partly borrowed from ext3. - */ -static int gfs2_block_truncate_page(struct address_space *mapping) -{ - struct inode *inode = mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - loff_t from = inode->i_size; - unsigned long index = from >> PAGE_CACHE_SHIFT; - unsigned offset = from & (PAGE_CACHE_SIZE-1); - unsigned blocksize, iblock, length, pos; - struct buffer_head *bh; - struct page *page; - void *kaddr; - int err; - - page = grab_cache_page(mapping, index); - if (!page) - return 0; - - blocksize = inode->i_sb->s_blocksize; - length = blocksize - (offset & (blocksize - 1)); - iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - - if (!page_has_buffers(page)) - create_empty_buffers(page, blocksize, 0); - - /* Find the buffer that contains "offset" */ - bh = page_buffers(page); - pos = blocksize; - while (offset >= pos) { - bh = bh->b_this_page; - iblock++; - pos += blocksize; - } - - err = 0; - - if (!buffer_mapped(bh)) { - gfs2_get_block(inode, iblock, bh, 0); - /* unmapped? It's a hole - nothing to do */ - if (!buffer_mapped(bh)) - goto unlock; - } - - /* Ok, it's mapped. Make sure it's up-to-date */ - if (PageUptodate(page)) - set_buffer_uptodate(bh); - - if (!buffer_uptodate(bh)) { - err = -EIO; - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - /* Uhhuh. Read error. Complain and punt. */ - if (!buffer_uptodate(bh)) - goto unlock; - } - - if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) - gfs2_trans_add_bh(ip->i_gl, bh, 0); - - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - -unlock: - unlock_page(page); - page_cache_release(page); - return err; -} - -static int trunc_start(struct gfs2_inode *ip, u64 size) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head *dibh; - int journaled = gfs2_is_jdata(ip); - int error; - - error = gfs2_trans_begin(sdp, - RES_DINODE + (journaled ? RES_JDATA : 0), 0); - if (error) - return error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out; - - if (gfs2_is_stuffed(ip)) { - ip->i_di.di_size = size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + size); - error = 1; - - } else { - if (size & (u64)(sdp->sd_sb.sb_bsize - 1)) - error = gfs2_block_truncate_page(ip->i_inode.i_mapping); - - if (!error) { - ip->i_di.di_size = size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - ip->i_di.di_flags |= GFS2_DIF_TRUNC_IN_PROG; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - } - } - - brelse(dibh); - -out: - gfs2_trans_end(sdp); - return error; -} - -static int trunc_dealloc(struct gfs2_inode *ip, u64 size) -{ - unsigned int height = ip->i_di.di_height; - u64 lblock; - struct metapath mp; - int error; - - if (!size) - lblock = 0; - else - lblock = (size - 1) >> GFS2_SB(&ip->i_inode)->sd_sb.sb_bsize_shift; - - find_metapath(ip, lblock, &mp); - gfs2_alloc_get(ip); - - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out; - - while (height--) { - struct strip_mine sm; - sm.sm_first = !!size; - sm.sm_height = height; - - error = recursive_scan(ip, NULL, &mp, 0, 0, 1, do_strip, &sm); - if (error) - break; - } - - gfs2_quota_unhold(ip); - -out: - gfs2_alloc_put(ip); - return error; -} - -static int trunc_end(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head *dibh; - int error; - - error = gfs2_trans_begin(sdp, RES_DINODE, 0); - if (error) - return error; - - down_write(&ip->i_rw_mutex); - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out; - - if (!ip->i_di.di_size) { - ip->i_di.di_height = 0; - ip->i_di.di_goal_meta = - ip->i_di.di_goal_data = - ip->i_num.no_addr; - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - } - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - ip->i_di.di_flags &= ~GFS2_DIF_TRUNC_IN_PROG; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - -out: - up_write(&ip->i_rw_mutex); - gfs2_trans_end(sdp); - return error; -} - -/** - * do_shrink - make a file smaller - * @ip: the inode - * @size: the size to make the file - * @truncator: function to truncate the last partial block - * - * Called with an exclusive lock on @ip. - * - * Returns: errno - */ - -static int do_shrink(struct gfs2_inode *ip, u64 size) -{ - int error; - - error = trunc_start(ip, size); - if (error < 0) - return error; - if (error > 0) - return 0; - - error = trunc_dealloc(ip, size); - if (!error) - error = trunc_end(ip); - - return error; -} - -/** - * gfs2_truncatei - make a file a given size - * @ip: the inode - * @size: the size to make the file - * @truncator: function to truncate the last partial block - * - * The file size can grow, shrink, or stay the same size. - * - * Returns: errno - */ - -int gfs2_truncatei(struct gfs2_inode *ip, u64 size) -{ - int error; - - if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), S_ISREG(ip->i_di.di_mode))) - return -EINVAL; - - if (size > ip->i_di.di_size) - error = do_grow(ip, size); - else - error = do_shrink(ip, size); - - return error; -} - -int gfs2_truncatei_resume(struct gfs2_inode *ip) -{ - int error; - error = trunc_dealloc(ip, ip->i_di.di_size); - if (!error) - error = trunc_end(ip); - return error; -} - -int gfs2_file_dealloc(struct gfs2_inode *ip) -{ - return trunc_dealloc(ip, 0); -} - -/** - * gfs2_write_calc_reserv - calculate number of blocks needed to write to a file - * @ip: the file - * @len: the number of bytes to be written to the file - * @data_blocks: returns the number of data blocks required - * @ind_blocks: returns the number of indirect blocks required - * - */ - -void gfs2_write_calc_reserv(struct gfs2_inode *ip, unsigned int len, - unsigned int *data_blocks, unsigned int *ind_blocks) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - unsigned int tmp; - - if (gfs2_is_dir(ip)) { - *data_blocks = DIV_ROUND_UP(len, sdp->sd_jbsize) + 2; - *ind_blocks = 3 * (sdp->sd_max_jheight - 1); - } else { - *data_blocks = (len >> sdp->sd_sb.sb_bsize_shift) + 3; - *ind_blocks = 3 * (sdp->sd_max_height - 1); - } - - for (tmp = *data_blocks; tmp > sdp->sd_diptrs;) { - tmp = DIV_ROUND_UP(tmp, sdp->sd_inptrs); - *ind_blocks += tmp; - } -} - -/** - * gfs2_write_alloc_required - figure out if a write will require an allocation - * @ip: the file being written to - * @offset: the offset to write to - * @len: the number of bytes being written - * @alloc_required: set to 1 if an alloc is required, 0 otherwise - * - * Returns: errno - */ - -int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, - unsigned int len, int *alloc_required) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - u64 lblock, lblock_stop, dblock; - u32 extlen; - int new = 0; - int error = 0; - - *alloc_required = 0; - - if (!len) - return 0; - - if (gfs2_is_stuffed(ip)) { - if (offset + len > - sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) - *alloc_required = 1; - return 0; - } - - if (gfs2_is_dir(ip)) { - unsigned int bsize = sdp->sd_jbsize; - lblock = offset; - do_div(lblock, bsize); - lblock_stop = offset + len + bsize - 1; - do_div(lblock_stop, bsize); - } else { - unsigned int shift = sdp->sd_sb.sb_bsize_shift; - lblock = offset >> shift; - lblock_stop = (offset + len + sdp->sd_sb.sb_bsize - 1) >> shift; - } - - for (; lblock < lblock_stop; lblock += extlen) { - error = gfs2_extent_map(&ip->i_inode, lblock, &new, &dblock, &extlen); - if (error) - return error; - - if (!dblock) { - *alloc_required = 1; - return 0; - } - } - - return 0; -} - diff --git a/trunk/fs/gfs2/bmap.h b/trunk/fs/gfs2/bmap.h deleted file mode 100644 index 0fd379b4cd9e..000000000000 --- a/trunk/fs/gfs2/bmap.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __BMAP_DOT_H__ -#define __BMAP_DOT_H__ - -struct inode; -struct gfs2_inode; -struct page; - -int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page); -int gfs2_block_map(struct inode *inode, u64 lblock, int create, struct buffer_head *bh, unsigned int maxlen); -int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen); - -int gfs2_truncatei(struct gfs2_inode *ip, u64 size); -int gfs2_truncatei_resume(struct gfs2_inode *ip); -int gfs2_file_dealloc(struct gfs2_inode *ip); - -void gfs2_write_calc_reserv(struct gfs2_inode *ip, unsigned int len, - unsigned int *data_blocks, - unsigned int *ind_blocks); -int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, - unsigned int len, int *alloc_required); - -#endif /* __BMAP_DOT_H__ */ diff --git a/trunk/fs/gfs2/daemon.c b/trunk/fs/gfs2/daemon.c deleted file mode 100644 index cab1f68d4685..000000000000 --- a/trunk/fs/gfs2/daemon.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "daemon.h" -#include "glock.h" -#include "log.h" -#include "quota.h" -#include "recovery.h" -#include "super.h" -#include "util.h" - -/* This uses schedule_timeout() instead of msleep() because it's good for - the daemons to wake up more often than the timeout when unmounting so - the user's unmount doesn't sit there forever. - - The kthread functions used to start these daemons block and flush signals. */ - -/** - * gfs2_scand - Look for cached glocks and inodes to toss from memory - * @sdp: Pointer to GFS2 superblock - * - * One of these daemons runs, finding candidates to add to sd_reclaim_list. - * See gfs2_glockd() - */ - -int gfs2_scand(void *data) -{ - struct gfs2_sbd *sdp = data; - unsigned long t; - - while (!kthread_should_stop()) { - gfs2_scand_internal(sdp); - t = gfs2_tune_get(sdp, gt_scand_secs) * HZ; - schedule_timeout_interruptible(t); - } - - return 0; -} - -/** - * gfs2_glockd - Reclaim unused glock structures - * @sdp: Pointer to GFS2 superblock - * - * One or more of these daemons run, reclaiming glocks on sd_reclaim_list. - * Number of daemons can be set by user, with num_glockd mount option. - */ - -int gfs2_glockd(void *data) -{ - struct gfs2_sbd *sdp = data; - - while (!kthread_should_stop()) { - while (atomic_read(&sdp->sd_reclaim_count)) - gfs2_reclaim_glock(sdp); - - wait_event_interruptible(sdp->sd_reclaim_wq, - (atomic_read(&sdp->sd_reclaim_count) || - kthread_should_stop())); - } - - return 0; -} - -/** - * gfs2_recoverd - Recover dead machine's journals - * @sdp: Pointer to GFS2 superblock - * - */ - -int gfs2_recoverd(void *data) -{ - struct gfs2_sbd *sdp = data; - unsigned long t; - - while (!kthread_should_stop()) { - gfs2_check_journals(sdp); - t = gfs2_tune_get(sdp, gt_recoverd_secs) * HZ; - schedule_timeout_interruptible(t); - } - - return 0; -} - -/** - * gfs2_logd - Update log tail as Active Items get flushed to in-place blocks - * @sdp: Pointer to GFS2 superblock - * - * Also, periodically check to make sure that we're using the most recent - * journal index. - */ - -int gfs2_logd(void *data) -{ - struct gfs2_sbd *sdp = data; - struct gfs2_holder ji_gh; - unsigned long t; - - while (!kthread_should_stop()) { - /* Advance the log tail */ - - t = sdp->sd_log_flush_time + - gfs2_tune_get(sdp, gt_log_flush_secs) * HZ; - - gfs2_ail1_empty(sdp, DIO_ALL); - - if (time_after_eq(jiffies, t)) { - gfs2_log_flush(sdp, NULL); - sdp->sd_log_flush_time = jiffies; - } - - /* Check for latest journal index */ - - t = sdp->sd_jindex_refresh_time + - gfs2_tune_get(sdp, gt_jindex_refresh_secs) * HZ; - - if (time_after_eq(jiffies, t)) { - if (!gfs2_jindex_hold(sdp, &ji_gh)) - gfs2_glock_dq_uninit(&ji_gh); - sdp->sd_jindex_refresh_time = jiffies; - } - - t = gfs2_tune_get(sdp, gt_logd_secs) * HZ; - schedule_timeout_interruptible(t); - } - - return 0; -} - -/** - * gfs2_quotad - Write cached quota changes into the quota file - * @sdp: Pointer to GFS2 superblock - * - */ - -int gfs2_quotad(void *data) -{ - struct gfs2_sbd *sdp = data; - unsigned long t; - int error; - - while (!kthread_should_stop()) { - /* Update the master statfs file */ - - t = sdp->sd_statfs_sync_time + - gfs2_tune_get(sdp, gt_statfs_quantum) * HZ; - - if (time_after_eq(jiffies, t)) { - error = gfs2_statfs_sync(sdp); - if (error && - error != -EROFS && - !test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) - fs_err(sdp, "quotad: (1) error=%d\n", error); - sdp->sd_statfs_sync_time = jiffies; - } - - /* Update quota file */ - - t = sdp->sd_quota_sync_time + - gfs2_tune_get(sdp, gt_quota_quantum) * HZ; - - if (time_after_eq(jiffies, t)) { - error = gfs2_quota_sync(sdp); - if (error && - error != -EROFS && - !test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) - fs_err(sdp, "quotad: (2) error=%d\n", error); - sdp->sd_quota_sync_time = jiffies; - } - - gfs2_quota_scan(sdp); - - t = gfs2_tune_get(sdp, gt_quotad_secs) * HZ; - schedule_timeout_interruptible(t); - } - - return 0; -} - diff --git a/trunk/fs/gfs2/daemon.h b/trunk/fs/gfs2/daemon.h deleted file mode 100644 index 801007120fb2..000000000000 --- a/trunk/fs/gfs2/daemon.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __DAEMON_DOT_H__ -#define __DAEMON_DOT_H__ - -int gfs2_scand(void *data); -int gfs2_glockd(void *data); -int gfs2_recoverd(void *data); -int gfs2_logd(void *data); -int gfs2_quotad(void *data); - -#endif /* __DAEMON_DOT_H__ */ diff --git a/trunk/fs/gfs2/dir.c b/trunk/fs/gfs2/dir.c deleted file mode 100644 index 459498cac93b..000000000000 --- a/trunk/fs/gfs2/dir.c +++ /dev/null @@ -1,1961 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -/* - * Implements Extendible Hashing as described in: - * "Extendible Hashing" by Fagin, et al in - * __ACM Trans. on Database Systems__, Sept 1979. - * - * - * Here's the layout of dirents which is essentially the same as that of ext2 - * within a single block. The field de_name_len is the number of bytes - * actually required for the name (no null terminator). The field de_rec_len - * is the number of bytes allocated to the dirent. The offset of the next - * dirent in the block is (dirent + dirent->de_rec_len). When a dirent is - * deleted, the preceding dirent inherits its allocated space, ie - * prev->de_rec_len += deleted->de_rec_len. Since the next dirent is obtained - * by adding de_rec_len to the current dirent, this essentially causes the - * deleted dirent to get jumped over when iterating through all the dirents. - * - * When deleting the first dirent in a block, there is no previous dirent so - * the field de_ino is set to zero to designate it as deleted. When allocating - * a dirent, gfs2_dirent_alloc iterates through the dirents in a block. If the - * first dirent has (de_ino == 0) and de_rec_len is large enough, this first - * dirent is allocated. Otherwise it must go through all the 'used' dirents - * searching for one in which the amount of total space minus the amount of - * used space will provide enough space for the new dirent. - * - * There are two types of blocks in which dirents reside. In a stuffed dinode, - * the dirents begin at offset sizeof(struct gfs2_dinode) from the beginning of - * the block. In leaves, they begin at offset sizeof(struct gfs2_leaf) from the - * beginning of the leaf block. The dirents reside in leaves when - * - * dip->i_di.di_flags & GFS2_DIF_EXHASH is true - * - * Otherwise, the dirents are "linear", within a single stuffed dinode block. - * - * When the dirents are in leaves, the actual contents of the directory file are - * used as an array of 64-bit block pointers pointing to the leaf blocks. The - * dirents are NOT in the directory file itself. There can be more than one - * block pointer in the array that points to the same leaf. In fact, when a - * directory is first converted from linear to exhash, all of the pointers - * point to the same leaf. - * - * When a leaf is completely full, the size of the hash table can be - * doubled unless it is already at the maximum size which is hard coded into - * GFS2_DIR_MAX_DEPTH. After that, leaves are chained together in a linked list, - * but never before the maximum hash table size has been reached. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "dir.h" -#include "glock.h" -#include "inode.h" -#include "meta_io.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "bmap.h" -#include "util.h" - -#define IS_LEAF 1 /* Hashed (leaf) directory */ -#define IS_DINODE 2 /* Linear (stuffed dinode block) directory */ - -#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) -#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) - -typedef int (*leaf_call_t) (struct gfs2_inode *dip, u32 index, u32 len, - u64 leaf_no, void *data); -typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent, - const struct qstr *name, void *opaque); - - -int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, - struct buffer_head **bhp) -{ - struct buffer_head *bh; - - bh = gfs2_meta_new(ip->i_gl, block); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD); - gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header)); - *bhp = bh; - return 0; -} - -static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, u64 block, - struct buffer_head **bhp) -{ - struct buffer_head *bh; - int error; - - error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, &bh); - if (error) - return error; - if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_JD)) { - brelse(bh); - return -EIO; - } - *bhp = bh; - return 0; -} - -static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf, - unsigned int offset, unsigned int size) -{ - struct buffer_head *dibh; - int error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - memcpy(dibh->b_data + offset + sizeof(struct gfs2_dinode), buf, size); - if (ip->i_di.di_size < offset + size) - ip->i_di.di_size = offset + size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - - brelse(dibh); - - return size; -} - - - -/** - * gfs2_dir_write_data - Write directory information to the inode - * @ip: The GFS2 inode - * @buf: The buffer containing information to be written - * @offset: The file offset to start writing at - * @size: The amount of data to write - * - * Returns: The number of bytes correctly written or error code - */ -static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf, - u64 offset, unsigned int size) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head *dibh; - u64 lblock, dblock; - u32 extlen = 0; - unsigned int o; - int copied = 0; - int error = 0; - - if (!size) - return 0; - - if (gfs2_is_stuffed(ip) && - offset + size <= sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) - return gfs2_dir_write_stuffed(ip, buf, (unsigned int)offset, - size); - - if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip))) - return -EINVAL; - - if (gfs2_is_stuffed(ip)) { - error = gfs2_unstuff_dinode(ip, NULL); - if (error) - return error; - } - - lblock = offset; - o = do_div(lblock, sdp->sd_jbsize) + sizeof(struct gfs2_meta_header); - - while (copied < size) { - unsigned int amount; - struct buffer_head *bh; - int new; - - amount = size - copied; - if (amount > sdp->sd_sb.sb_bsize - o) - amount = sdp->sd_sb.sb_bsize - o; - - if (!extlen) { - new = 1; - error = gfs2_extent_map(&ip->i_inode, lblock, &new, - &dblock, &extlen); - if (error) - goto fail; - error = -EIO; - if (gfs2_assert_withdraw(sdp, dblock)) - goto fail; - } - - if (amount == sdp->sd_jbsize || new) - error = gfs2_dir_get_new_buffer(ip, dblock, &bh); - else - error = gfs2_dir_get_existing_buffer(ip, dblock, &bh); - - if (error) - goto fail; - - gfs2_trans_add_bh(ip->i_gl, bh, 1); - memcpy(bh->b_data + o, buf, amount); - brelse(bh); - if (error) - goto fail; - - buf += amount; - copied += amount; - lblock++; - dblock++; - extlen--; - - o = sizeof(struct gfs2_meta_header); - } - -out: - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - if (ip->i_di.di_size < offset + copied) - ip->i_di.di_size = offset + copied; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - - return copied; -fail: - if (copied) - goto out; - return error; -} - -static int gfs2_dir_read_stuffed(struct gfs2_inode *ip, char *buf, - u64 offset, unsigned int size) -{ - struct buffer_head *dibh; - int error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - offset += sizeof(struct gfs2_dinode); - memcpy(buf, dibh->b_data + offset, size); - brelse(dibh); - } - - return (error) ? error : size; -} - - -/** - * gfs2_dir_read_data - Read a data from a directory inode - * @ip: The GFS2 Inode - * @buf: The buffer to place result into - * @offset: File offset to begin jdata_readng from - * @size: Amount of data to transfer - * - * Returns: The amount of data actually copied or the error - */ -static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset, - unsigned int size, unsigned ra) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - u64 lblock, dblock; - u32 extlen = 0; - unsigned int o; - int copied = 0; - int error = 0; - - if (offset >= ip->i_di.di_size) - return 0; - - if (offset + size > ip->i_di.di_size) - size = ip->i_di.di_size - offset; - - if (!size) - return 0; - - if (gfs2_is_stuffed(ip)) - return gfs2_dir_read_stuffed(ip, buf, offset, size); - - if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip))) - return -EINVAL; - - lblock = offset; - o = do_div(lblock, sdp->sd_jbsize) + sizeof(struct gfs2_meta_header); - - while (copied < size) { - unsigned int amount; - struct buffer_head *bh; - int new; - - amount = size - copied; - if (amount > sdp->sd_sb.sb_bsize - o) - amount = sdp->sd_sb.sb_bsize - o; - - if (!extlen) { - new = 0; - error = gfs2_extent_map(&ip->i_inode, lblock, &new, - &dblock, &extlen); - if (error || !dblock) - goto fail; - BUG_ON(extlen < 1); - if (!ra) - extlen = 1; - bh = gfs2_meta_ra(ip->i_gl, dblock, extlen); - } - if (!bh) { - error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, &bh); - if (error) - goto fail; - } - error = gfs2_metatype_check(sdp, bh, GFS2_METATYPE_JD); - if (error) { - brelse(bh); - goto fail; - } - dblock++; - extlen--; - memcpy(buf, bh->b_data + o, amount); - brelse(bh); - bh = NULL; - buf += amount; - copied += amount; - lblock++; - o = sizeof(struct gfs2_meta_header); - } - - return copied; -fail: - return (copied) ? copied : error; -} - -static inline int __gfs2_dirent_find(const struct gfs2_dirent *dent, - const struct qstr *name, int ret) -{ - if (dent->de_inum.no_addr != 0 && - be32_to_cpu(dent->de_hash) == name->hash && - be16_to_cpu(dent->de_name_len) == name->len && - memcmp(dent+1, name->name, name->len) == 0) - return ret; - return 0; -} - -static int gfs2_dirent_find(const struct gfs2_dirent *dent, - const struct qstr *name, - void *opaque) -{ - return __gfs2_dirent_find(dent, name, 1); -} - -static int gfs2_dirent_prev(const struct gfs2_dirent *dent, - const struct qstr *name, - void *opaque) -{ - return __gfs2_dirent_find(dent, name, 2); -} - -/* - * name->name holds ptr to start of block. - * name->len holds size of block. - */ -static int gfs2_dirent_last(const struct gfs2_dirent *dent, - const struct qstr *name, - void *opaque) -{ - const char *start = name->name; - const char *end = (const char *)dent + be16_to_cpu(dent->de_rec_len); - if (name->len == (end - start)) - return 1; - return 0; -} - -static int gfs2_dirent_find_space(const struct gfs2_dirent *dent, - const struct qstr *name, - void *opaque) -{ - unsigned required = GFS2_DIRENT_SIZE(name->len); - unsigned actual = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len)); - unsigned totlen = be16_to_cpu(dent->de_rec_len); - - if (!dent->de_inum.no_addr) - actual = GFS2_DIRENT_SIZE(0); - if (totlen - actual >= required) - return 1; - return 0; -} - -struct dirent_gather { - const struct gfs2_dirent **pdent; - unsigned offset; -}; - -static int gfs2_dirent_gather(const struct gfs2_dirent *dent, - const struct qstr *name, - void *opaque) -{ - struct dirent_gather *g = opaque; - if (dent->de_inum.no_addr) { - g->pdent[g->offset++] = dent; - } - return 0; -} - -/* - * Other possible things to check: - * - Inode located within filesystem size (and on valid block) - * - Valid directory entry type - * Not sure how heavy-weight we want to make this... could also check - * hash is correct for example, but that would take a lot of extra time. - * For now the most important thing is to check that the various sizes - * are correct. - */ -static int gfs2_check_dirent(struct gfs2_dirent *dent, unsigned int offset, - unsigned int size, unsigned int len, int first) -{ - const char *msg = "gfs2_dirent too small"; - if (unlikely(size < sizeof(struct gfs2_dirent))) - goto error; - msg = "gfs2_dirent misaligned"; - if (unlikely(offset & 0x7)) - goto error; - msg = "gfs2_dirent points beyond end of block"; - if (unlikely(offset + size > len)) - goto error; - msg = "zero inode number"; - if (unlikely(!first && !dent->de_inum.no_addr)) - goto error; - msg = "name length is greater than space in dirent"; - if (dent->de_inum.no_addr && - unlikely(sizeof(struct gfs2_dirent)+be16_to_cpu(dent->de_name_len) > - size)) - goto error; - return 0; -error: - printk(KERN_WARNING "gfs2_check_dirent: %s (%s)\n", msg, - first ? "first in block" : "not first in block"); - return -EIO; -} - -static int gfs2_dirent_offset(const void *buf) -{ - const struct gfs2_meta_header *h = buf; - int offset; - - BUG_ON(buf == NULL); - - switch(be32_to_cpu(h->mh_type)) { - case GFS2_METATYPE_LF: - offset = sizeof(struct gfs2_leaf); - break; - case GFS2_METATYPE_DI: - offset = sizeof(struct gfs2_dinode); - break; - default: - goto wrong_type; - } - return offset; -wrong_type: - printk(KERN_WARNING "gfs2_scan_dirent: wrong block type %u\n", - be32_to_cpu(h->mh_type)); - return -1; -} - -static struct gfs2_dirent *gfs2_dirent_scan(struct inode *inode, void *buf, - unsigned int len, gfs2_dscan_t scan, - const struct qstr *name, - void *opaque) -{ - struct gfs2_dirent *dent, *prev; - unsigned offset; - unsigned size; - int ret = 0; - - ret = gfs2_dirent_offset(buf); - if (ret < 0) - goto consist_inode; - - offset = ret; - prev = NULL; - dent = buf + offset; - size = be16_to_cpu(dent->de_rec_len); - if (gfs2_check_dirent(dent, offset, size, len, 1)) - goto consist_inode; - do { - ret = scan(dent, name, opaque); - if (ret) - break; - offset += size; - if (offset == len) - break; - prev = dent; - dent = buf + offset; - size = be16_to_cpu(dent->de_rec_len); - if (gfs2_check_dirent(dent, offset, size, len, 0)) - goto consist_inode; - } while(1); - - switch(ret) { - case 0: - return NULL; - case 1: - return dent; - case 2: - return prev ? prev : dent; - default: - BUG_ON(ret > 0); - return ERR_PTR(ret); - } - -consist_inode: - gfs2_consist_inode(GFS2_I(inode)); - return ERR_PTR(-EIO); -} - - -/** - * dirent_first - Return the first dirent - * @dip: the directory - * @bh: The buffer - * @dent: Pointer to list of dirents - * - * return first dirent whether bh points to leaf or stuffed dinode - * - * Returns: IS_LEAF, IS_DINODE, or -errno - */ - -static int dirent_first(struct gfs2_inode *dip, struct buffer_head *bh, - struct gfs2_dirent **dent) -{ - struct gfs2_meta_header *h = (struct gfs2_meta_header *)bh->b_data; - - if (be32_to_cpu(h->mh_type) == GFS2_METATYPE_LF) { - if (gfs2_meta_check(GFS2_SB(&dip->i_inode), bh)) - return -EIO; - *dent = (struct gfs2_dirent *)(bh->b_data + - sizeof(struct gfs2_leaf)); - return IS_LEAF; - } else { - if (gfs2_metatype_check(GFS2_SB(&dip->i_inode), bh, GFS2_METATYPE_DI)) - return -EIO; - *dent = (struct gfs2_dirent *)(bh->b_data + - sizeof(struct gfs2_dinode)); - return IS_DINODE; - } -} - -static int dirent_check_reclen(struct gfs2_inode *dip, - const struct gfs2_dirent *d, const void *end_p) -{ - const void *ptr = d; - u16 rec_len = be16_to_cpu(d->de_rec_len); - - if (unlikely(rec_len < sizeof(struct gfs2_dirent))) - goto broken; - ptr += rec_len; - if (ptr < end_p) - return rec_len; - if (ptr == end_p) - return -ENOENT; -broken: - gfs2_consist_inode(dip); - return -EIO; -} - -/** - * dirent_next - Next dirent - * @dip: the directory - * @bh: The buffer - * @dent: Pointer to list of dirents - * - * Returns: 0 on success, error code otherwise - */ - -static int dirent_next(struct gfs2_inode *dip, struct buffer_head *bh, - struct gfs2_dirent **dent) -{ - struct gfs2_dirent *cur = *dent, *tmp; - char *bh_end = bh->b_data + bh->b_size; - int ret; - - ret = dirent_check_reclen(dip, cur, bh_end); - if (ret < 0) - return ret; - - tmp = (void *)cur + ret; - ret = dirent_check_reclen(dip, tmp, bh_end); - if (ret == -EIO) - return ret; - - /* Only the first dent could ever have de_inum.no_addr == 0 */ - if (!tmp->de_inum.no_addr) { - gfs2_consist_inode(dip); - return -EIO; - } - - *dent = tmp; - return 0; -} - -/** - * dirent_del - Delete a dirent - * @dip: The GFS2 inode - * @bh: The buffer - * @prev: The previous dirent - * @cur: The current dirent - * - */ - -static void dirent_del(struct gfs2_inode *dip, struct buffer_head *bh, - struct gfs2_dirent *prev, struct gfs2_dirent *cur) -{ - u16 cur_rec_len, prev_rec_len; - - if (!cur->de_inum.no_addr) { - gfs2_consist_inode(dip); - return; - } - - gfs2_trans_add_bh(dip->i_gl, bh, 1); - - /* If there is no prev entry, this is the first entry in the block. - The de_rec_len is already as big as it needs to be. Just zero - out the inode number and return. */ - - if (!prev) { - cur->de_inum.no_addr = 0; /* No endianess worries */ - return; - } - - /* Combine this dentry with the previous one. */ - - prev_rec_len = be16_to_cpu(prev->de_rec_len); - cur_rec_len = be16_to_cpu(cur->de_rec_len); - - if ((char *)prev + prev_rec_len != (char *)cur) - gfs2_consist_inode(dip); - if ((char *)cur + cur_rec_len > bh->b_data + bh->b_size) - gfs2_consist_inode(dip); - - prev_rec_len += cur_rec_len; - prev->de_rec_len = cpu_to_be16(prev_rec_len); -} - -/* - * Takes a dent from which to grab space as an argument. Returns the - * newly created dent. - */ -static struct gfs2_dirent *gfs2_init_dirent(struct inode *inode, - struct gfs2_dirent *dent, - const struct qstr *name, - struct buffer_head *bh) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_dirent *ndent; - unsigned offset = 0, totlen; - - if (dent->de_inum.no_addr) - offset = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len)); - totlen = be16_to_cpu(dent->de_rec_len); - BUG_ON(offset + name->len > totlen); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - ndent = (struct gfs2_dirent *)((char *)dent + offset); - dent->de_rec_len = cpu_to_be16(offset); - gfs2_qstr2dirent(name, totlen - offset, ndent); - return ndent; -} - -static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode, - struct buffer_head *bh, - const struct qstr *name) -{ - struct gfs2_dirent *dent; - dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, - gfs2_dirent_find_space, name, NULL); - if (!dent || IS_ERR(dent)) - return dent; - return gfs2_init_dirent(inode, dent, name, bh); -} - -static int get_leaf(struct gfs2_inode *dip, u64 leaf_no, - struct buffer_head **bhp) -{ - int error; - - error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, bhp); - if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) { - /* printk(KERN_INFO "block num=%llu\n", leaf_no); */ - error = -EIO; - } - - return error; -} - -/** - * get_leaf_nr - Get a leaf number associated with the index - * @dip: The GFS2 inode - * @index: - * @leaf_out: - * - * Returns: 0 on success, error code otherwise - */ - -static int get_leaf_nr(struct gfs2_inode *dip, u32 index, - u64 *leaf_out) -{ - u64 leaf_no; - int error; - - error = gfs2_dir_read_data(dip, (char *)&leaf_no, - index * sizeof(u64), - sizeof(u64), 0); - if (error != sizeof(u64)) - return (error < 0) ? error : -EIO; - - *leaf_out = be64_to_cpu(leaf_no); - - return 0; -} - -static int get_first_leaf(struct gfs2_inode *dip, u32 index, - struct buffer_head **bh_out) -{ - u64 leaf_no; - int error; - - error = get_leaf_nr(dip, index, &leaf_no); - if (!error) - error = get_leaf(dip, leaf_no, bh_out); - - return error; -} - -static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode, - const struct qstr *name, - gfs2_dscan_t scan, - struct buffer_head **pbh) -{ - struct buffer_head *bh; - struct gfs2_dirent *dent; - struct gfs2_inode *ip = GFS2_I(inode); - int error; - - if (ip->i_di.di_flags & GFS2_DIF_EXHASH) { - struct gfs2_leaf *leaf; - unsigned hsize = 1 << ip->i_di.di_depth; - unsigned index; - u64 ln; - if (hsize * sizeof(u64) != ip->i_di.di_size) { - gfs2_consist_inode(ip); - return ERR_PTR(-EIO); - } - - index = name->hash >> (32 - ip->i_di.di_depth); - error = get_first_leaf(ip, index, &bh); - if (error) - return ERR_PTR(error); - do { - dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, - scan, name, NULL); - if (dent) - goto got_dent; - leaf = (struct gfs2_leaf *)bh->b_data; - ln = be64_to_cpu(leaf->lf_next); - brelse(bh); - if (!ln) - break; - - error = get_leaf(ip, ln, &bh); - } while(!error); - - return error ? ERR_PTR(error) : NULL; - } - - - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - return ERR_PTR(error); - dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, scan, name, NULL); -got_dent: - if (unlikely(dent == NULL || IS_ERR(dent))) { - brelse(bh); - bh = NULL; - } - *pbh = bh; - return dent; -} - -static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh, u16 depth) -{ - struct gfs2_inode *ip = GFS2_I(inode); - u64 bn = gfs2_alloc_meta(ip); - struct buffer_head *bh = gfs2_meta_new(ip->i_gl, bn); - struct gfs2_leaf *leaf; - struct gfs2_dirent *dent; - struct qstr name = { .name = "", .len = 0, .hash = 0 }; - if (!bh) - return NULL; - - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_metatype_set(bh, GFS2_METATYPE_LF, GFS2_FORMAT_LF); - leaf = (struct gfs2_leaf *)bh->b_data; - leaf->lf_depth = cpu_to_be16(depth); - leaf->lf_entries = 0; - leaf->lf_dirent_format = cpu_to_be16(GFS2_FORMAT_DE); - leaf->lf_next = 0; - memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved)); - dent = (struct gfs2_dirent *)(leaf+1); - gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent); - *pbh = bh; - return leaf; -} - -/** - * dir_make_exhash - Convert a stuffed directory into an ExHash directory - * @dip: The GFS2 inode - * - * Returns: 0 on success, error code otherwise - */ - -static int dir_make_exhash(struct inode *inode) -{ - struct gfs2_inode *dip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct gfs2_dirent *dent; - struct qstr args; - struct buffer_head *bh, *dibh; - struct gfs2_leaf *leaf; - int y; - u32 x; - u64 *lp, bn; - int error; - - error = gfs2_meta_inode_buffer(dip, &dibh); - if (error) - return error; - - /* Turn over a new leaf */ - - leaf = new_leaf(inode, &bh, 0); - if (!leaf) - return -ENOSPC; - bn = bh->b_blocknr; - - gfs2_assert(sdp, dip->i_di.di_entries < (1 << 16)); - leaf->lf_entries = cpu_to_be16(dip->i_di.di_entries); - - /* Copy dirents */ - - gfs2_buffer_copy_tail(bh, sizeof(struct gfs2_leaf), dibh, - sizeof(struct gfs2_dinode)); - - /* Find last entry */ - - x = 0; - args.len = bh->b_size - sizeof(struct gfs2_dinode) + - sizeof(struct gfs2_leaf); - args.name = bh->b_data; - dent = gfs2_dirent_scan(&dip->i_inode, bh->b_data, bh->b_size, - gfs2_dirent_last, &args, NULL); - if (!dent) { - brelse(bh); - brelse(dibh); - return -EIO; - } - if (IS_ERR(dent)) { - brelse(bh); - brelse(dibh); - return PTR_ERR(dent); - } - - /* Adjust the last dirent's record length - (Remember that dent still points to the last entry.) */ - - dent->de_rec_len = cpu_to_be16(be16_to_cpu(dent->de_rec_len) + - sizeof(struct gfs2_dinode) - - sizeof(struct gfs2_leaf)); - - brelse(bh); - - /* We're done with the new leaf block, now setup the new - hash table. */ - - gfs2_trans_add_bh(dip->i_gl, dibh, 1); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - - lp = (u64 *)(dibh->b_data + sizeof(struct gfs2_dinode)); - - for (x = sdp->sd_hash_ptrs; x--; lp++) - *lp = cpu_to_be64(bn); - - dip->i_di.di_size = sdp->sd_sb.sb_bsize / 2; - dip->i_di.di_blocks++; - dip->i_di.di_flags |= GFS2_DIF_EXHASH; - dip->i_di.di_payload_format = 0; - - for (x = sdp->sd_hash_ptrs, y = -1; x; x >>= 1, y++) ; - dip->i_di.di_depth = y; - - gfs2_dinode_out(&dip->i_di, dibh->b_data); - - brelse(dibh); - - return 0; -} - -/** - * dir_split_leaf - Split a leaf block into two - * @dip: The GFS2 inode - * @index: - * @leaf_no: - * - * Returns: 0 on success, error code on failure - */ - -static int dir_split_leaf(struct inode *inode, const struct qstr *name) -{ - struct gfs2_inode *dip = GFS2_I(inode); - struct buffer_head *nbh, *obh, *dibh; - struct gfs2_leaf *nleaf, *oleaf; - struct gfs2_dirent *dent = NULL, *prev = NULL, *next = NULL, *new; - u32 start, len, half_len, divider; - u64 bn, *lp, leaf_no; - u32 index; - int x, moved = 0; - int error; - - index = name->hash >> (32 - dip->i_di.di_depth); - error = get_leaf_nr(dip, index, &leaf_no); - if (error) - return error; - - /* Get the old leaf block */ - error = get_leaf(dip, leaf_no, &obh); - if (error) - return error; - - oleaf = (struct gfs2_leaf *)obh->b_data; - if (dip->i_di.di_depth == be16_to_cpu(oleaf->lf_depth)) { - brelse(obh); - return 1; /* can't split */ - } - - gfs2_trans_add_bh(dip->i_gl, obh, 1); - - nleaf = new_leaf(inode, &nbh, be16_to_cpu(oleaf->lf_depth) + 1); - if (!nleaf) { - brelse(obh); - return -ENOSPC; - } - bn = nbh->b_blocknr; - - /* Compute the start and len of leaf pointers in the hash table. */ - len = 1 << (dip->i_di.di_depth - be16_to_cpu(oleaf->lf_depth)); - half_len = len >> 1; - if (!half_len) { - printk(KERN_WARNING "di_depth %u lf_depth %u index %u\n", dip->i_di.di_depth, be16_to_cpu(oleaf->lf_depth), index); - gfs2_consist_inode(dip); - error = -EIO; - goto fail_brelse; - } - - start = (index & ~(len - 1)); - - /* Change the pointers. - Don't bother distinguishing stuffed from non-stuffed. - This code is complicated enough already. */ - lp = kmalloc(half_len * sizeof(u64), GFP_NOFS | __GFP_NOFAIL); - /* Change the pointers */ - for (x = 0; x < half_len; x++) - lp[x] = cpu_to_be64(bn); - - error = gfs2_dir_write_data(dip, (char *)lp, start * sizeof(u64), - half_len * sizeof(u64)); - if (error != half_len * sizeof(u64)) { - if (error >= 0) - error = -EIO; - goto fail_lpfree; - } - - kfree(lp); - - /* Compute the divider */ - divider = (start + half_len) << (32 - dip->i_di.di_depth); - - /* Copy the entries */ - dirent_first(dip, obh, &dent); - - do { - next = dent; - if (dirent_next(dip, obh, &next)) - next = NULL; - - if (dent->de_inum.no_addr && - be32_to_cpu(dent->de_hash) < divider) { - struct qstr str; - str.name = (char*)(dent+1); - str.len = be16_to_cpu(dent->de_name_len); - str.hash = be32_to_cpu(dent->de_hash); - new = gfs2_dirent_alloc(inode, nbh, &str); - if (IS_ERR(new)) { - error = PTR_ERR(new); - break; - } - - new->de_inum = dent->de_inum; /* No endian worries */ - new->de_type = dent->de_type; /* No endian worries */ - nleaf->lf_entries = cpu_to_be16(be16_to_cpu(nleaf->lf_entries)+1); - - dirent_del(dip, obh, prev, dent); - - if (!oleaf->lf_entries) - gfs2_consist_inode(dip); - oleaf->lf_entries = cpu_to_be16(be16_to_cpu(oleaf->lf_entries)-1); - - if (!prev) - prev = dent; - - moved = 1; - } else { - prev = dent; - } - dent = next; - } while (dent); - - oleaf->lf_depth = nleaf->lf_depth; - - error = gfs2_meta_inode_buffer(dip, &dibh); - if (!gfs2_assert_withdraw(GFS2_SB(&dip->i_inode), !error)) { - dip->i_di.di_blocks++; - gfs2_dinode_out(&dip->i_di, dibh->b_data); - brelse(dibh); - } - - brelse(obh); - brelse(nbh); - - return error; - -fail_lpfree: - kfree(lp); - -fail_brelse: - brelse(obh); - brelse(nbh); - return error; -} - -/** - * dir_double_exhash - Double size of ExHash table - * @dip: The GFS2 dinode - * - * Returns: 0 on success, error code on failure - */ - -static int dir_double_exhash(struct gfs2_inode *dip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct buffer_head *dibh; - u32 hsize; - u64 *buf; - u64 *from, *to; - u64 block; - int x; - int error = 0; - - hsize = 1 << dip->i_di.di_depth; - if (hsize * sizeof(u64) != dip->i_di.di_size) { - gfs2_consist_inode(dip); - return -EIO; - } - - /* Allocate both the "from" and "to" buffers in one big chunk */ - - buf = kcalloc(3, sdp->sd_hash_bsize, GFP_KERNEL | __GFP_NOFAIL); - - for (block = dip->i_di.di_size >> sdp->sd_hash_bsize_shift; block--;) { - error = gfs2_dir_read_data(dip, (char *)buf, - block * sdp->sd_hash_bsize, - sdp->sd_hash_bsize, 1); - if (error != sdp->sd_hash_bsize) { - if (error >= 0) - error = -EIO; - goto fail; - } - - from = buf; - to = (u64 *)((char *)buf + sdp->sd_hash_bsize); - - for (x = sdp->sd_hash_ptrs; x--; from++) { - *to++ = *from; /* No endianess worries */ - *to++ = *from; - } - - error = gfs2_dir_write_data(dip, - (char *)buf + sdp->sd_hash_bsize, - block * sdp->sd_sb.sb_bsize, - sdp->sd_sb.sb_bsize); - if (error != sdp->sd_sb.sb_bsize) { - if (error >= 0) - error = -EIO; - goto fail; - } - } - - kfree(buf); - - error = gfs2_meta_inode_buffer(dip, &dibh); - if (!gfs2_assert_withdraw(sdp, !error)) { - dip->i_di.di_depth++; - gfs2_dinode_out(&dip->i_di, dibh->b_data); - brelse(dibh); - } - - return error; - -fail: - kfree(buf); - return error; -} - -/** - * compare_dents - compare directory entries by hash value - * @a: first dent - * @b: second dent - * - * When comparing the hash entries of @a to @b: - * gt: returns 1 - * lt: returns -1 - * eq: returns 0 - */ - -static int compare_dents(const void *a, const void *b) -{ - const struct gfs2_dirent *dent_a, *dent_b; - u32 hash_a, hash_b; - int ret = 0; - - dent_a = *(const struct gfs2_dirent **)a; - hash_a = be32_to_cpu(dent_a->de_hash); - - dent_b = *(const struct gfs2_dirent **)b; - hash_b = be32_to_cpu(dent_b->de_hash); - - if (hash_a > hash_b) - ret = 1; - else if (hash_a < hash_b) - ret = -1; - else { - unsigned int len_a = be16_to_cpu(dent_a->de_name_len); - unsigned int len_b = be16_to_cpu(dent_b->de_name_len); - - if (len_a > len_b) - ret = 1; - else if (len_a < len_b) - ret = -1; - else - ret = memcmp(dent_a + 1, dent_b + 1, len_a); - } - - return ret; -} - -/** - * do_filldir_main - read out directory entries - * @dip: The GFS2 inode - * @offset: The offset in the file to read from - * @opaque: opaque data to pass to filldir - * @filldir: The function to pass entries to - * @darr: an array of struct gfs2_dirent pointers to read - * @entries: the number of entries in darr - * @copied: pointer to int that's non-zero if a entry has been copied out - * - * Jump through some hoops to make sure that if there are hash collsions, - * they are read out at the beginning of a buffer. We want to minimize - * the possibility that they will fall into different readdir buffers or - * that someone will want to seek to that location. - * - * Returns: errno, >0 on exception from filldir - */ - -static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, - void *opaque, gfs2_filldir_t filldir, - const struct gfs2_dirent **darr, u32 entries, - int *copied) -{ - const struct gfs2_dirent *dent, *dent_next; - struct gfs2_inum inum; - u64 off, off_next; - unsigned int x, y; - int run = 0; - int error = 0; - - sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL); - - dent_next = darr[0]; - off_next = be32_to_cpu(dent_next->de_hash); - off_next = gfs2_disk_hash2offset(off_next); - - for (x = 0, y = 1; x < entries; x++, y++) { - dent = dent_next; - off = off_next; - - if (y < entries) { - dent_next = darr[y]; - off_next = be32_to_cpu(dent_next->de_hash); - off_next = gfs2_disk_hash2offset(off_next); - - if (off < *offset) - continue; - *offset = off; - - if (off_next == off) { - if (*copied && !run) - return 1; - run = 1; - } else - run = 0; - } else { - if (off < *offset) - continue; - *offset = off; - } - - gfs2_inum_in(&inum, (char *)&dent->de_inum); - - error = filldir(opaque, (const char *)(dent + 1), - be16_to_cpu(dent->de_name_len), - off, &inum, - be16_to_cpu(dent->de_type)); - if (error) - return 1; - - *copied = 1; - } - - /* Increment the *offset by one, so the next time we come into the - do_filldir fxn, we get the next entry instead of the last one in the - current leaf */ - - (*offset)++; - - return 0; -} - -static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, - gfs2_filldir_t filldir, int *copied, - unsigned *depth, u64 leaf_no) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct buffer_head *bh; - struct gfs2_leaf *lf; - unsigned entries = 0; - unsigned leaves = 0; - const struct gfs2_dirent **darr, *dent; - struct dirent_gather g; - struct buffer_head **larr; - int leaf = 0; - int error, i; - u64 lfn = leaf_no; - - do { - error = get_leaf(ip, lfn, &bh); - if (error) - goto out; - lf = (struct gfs2_leaf *)bh->b_data; - if (leaves == 0) - *depth = be16_to_cpu(lf->lf_depth); - entries += be16_to_cpu(lf->lf_entries); - leaves++; - lfn = be64_to_cpu(lf->lf_next); - brelse(bh); - } while(lfn); - - if (!entries) - return 0; - - error = -ENOMEM; - larr = vmalloc((leaves + entries) * sizeof(void *)); - if (!larr) - goto out; - darr = (const struct gfs2_dirent **)(larr + leaves); - g.pdent = darr; - g.offset = 0; - lfn = leaf_no; - - do { - error = get_leaf(ip, lfn, &bh); - if (error) - goto out_kfree; - lf = (struct gfs2_leaf *)bh->b_data; - lfn = be64_to_cpu(lf->lf_next); - if (lf->lf_entries) { - dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size, - gfs2_dirent_gather, NULL, &g); - error = PTR_ERR(dent); - if (IS_ERR(dent)) { - goto out_kfree; - } - error = 0; - larr[leaf++] = bh; - } else { - brelse(bh); - } - } while(lfn); - - error = do_filldir_main(ip, offset, opaque, filldir, darr, - entries, copied); -out_kfree: - for(i = 0; i < leaf; i++) - brelse(larr[i]); - vfree(larr); -out: - return error; -} - -/** - * dir_e_read - Reads the entries from a directory into a filldir buffer - * @dip: dinode pointer - * @offset: the hash of the last entry read shifted to the right once - * @opaque: buffer for the filldir function to fill - * @filldir: points to the filldir function to use - * - * Returns: errno - */ - -static int dir_e_read(struct inode *inode, u64 *offset, void *opaque, - gfs2_filldir_t filldir) -{ - struct gfs2_inode *dip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - u32 hsize, len = 0; - u32 ht_offset, lp_offset, ht_offset_cur = -1; - u32 hash, index; - u64 *lp; - int copied = 0; - int error = 0; - unsigned depth = 0; - - hsize = 1 << dip->i_di.di_depth; - if (hsize * sizeof(u64) != dip->i_di.di_size) { - gfs2_consist_inode(dip); - return -EIO; - } - - hash = gfs2_dir_offset2hash(*offset); - index = hash >> (32 - dip->i_di.di_depth); - - lp = kmalloc(sdp->sd_hash_bsize, GFP_KERNEL); - if (!lp) - return -ENOMEM; - - while (index < hsize) { - lp_offset = index & (sdp->sd_hash_ptrs - 1); - ht_offset = index - lp_offset; - - if (ht_offset_cur != ht_offset) { - error = gfs2_dir_read_data(dip, (char *)lp, - ht_offset * sizeof(u64), - sdp->sd_hash_bsize, 1); - if (error != sdp->sd_hash_bsize) { - if (error >= 0) - error = -EIO; - goto out; - } - ht_offset_cur = ht_offset; - } - - error = gfs2_dir_read_leaf(inode, offset, opaque, filldir, - &copied, &depth, - be64_to_cpu(lp[lp_offset])); - if (error) - break; - - len = 1 << (dip->i_di.di_depth - depth); - index = (index & ~(len - 1)) + len; - } - -out: - kfree(lp); - if (error > 0) - error = 0; - return error; -} - -int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, - gfs2_filldir_t filldir) -{ - struct gfs2_inode *dip = GFS2_I(inode); - struct dirent_gather g; - const struct gfs2_dirent **darr, *dent; - struct buffer_head *dibh; - int copied = 0; - int error; - - if (!dip->i_di.di_entries) - return 0; - - if (dip->i_di.di_flags & GFS2_DIF_EXHASH) - return dir_e_read(inode, offset, opaque, filldir); - - if (!gfs2_is_stuffed(dip)) { - gfs2_consist_inode(dip); - return -EIO; - } - - error = gfs2_meta_inode_buffer(dip, &dibh); - if (error) - return error; - - error = -ENOMEM; - darr = kmalloc(dip->i_di.di_entries * sizeof(struct gfs2_dirent *), - GFP_KERNEL); - if (darr) { - g.pdent = darr; - g.offset = 0; - dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size, - gfs2_dirent_gather, NULL, &g); - if (IS_ERR(dent)) { - error = PTR_ERR(dent); - goto out; - } - error = do_filldir_main(dip, offset, opaque, filldir, darr, - dip->i_di.di_entries, &copied); -out: - kfree(darr); - } - - if (error > 0) - error = 0; - - brelse(dibh); - - return error; -} - -/** - * gfs2_dir_search - Search a directory - * @dip: The GFS2 inode - * @filename: - * @inode: - * - * This routine searches a directory for a file or another directory. - * Assumes a glock is held on dip. - * - * Returns: errno - */ - -int gfs2_dir_search(struct inode *dir, const struct qstr *name, - struct gfs2_inum *inum, unsigned int *type) -{ - struct buffer_head *bh; - struct gfs2_dirent *dent; - - dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh); - if (dent) { - if (IS_ERR(dent)) - return PTR_ERR(dent); - if (inum) - gfs2_inum_in(inum, (char *)&dent->de_inum); - if (type) - *type = be16_to_cpu(dent->de_type); - brelse(bh); - return 0; - } - return -ENOENT; -} - -static int dir_new_leaf(struct inode *inode, const struct qstr *name) -{ - struct buffer_head *bh, *obh; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_leaf *leaf, *oleaf; - int error; - u32 index; - u64 bn; - - index = name->hash >> (32 - ip->i_di.di_depth); - error = get_first_leaf(ip, index, &obh); - if (error) - return error; - do { - oleaf = (struct gfs2_leaf *)obh->b_data; - bn = be64_to_cpu(oleaf->lf_next); - if (!bn) - break; - brelse(obh); - error = get_leaf(ip, bn, &obh); - if (error) - return error; - } while(1); - - gfs2_trans_add_bh(ip->i_gl, obh, 1); - - leaf = new_leaf(inode, &bh, be16_to_cpu(oleaf->lf_depth)); - if (!leaf) { - brelse(obh); - return -ENOSPC; - } - oleaf->lf_next = cpu_to_be64(bh->b_blocknr); - brelse(bh); - brelse(obh); - - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - return error; - gfs2_trans_add_bh(ip->i_gl, bh, 1); - ip->i_di.di_blocks++; - gfs2_dinode_out(&ip->i_di, bh->b_data); - brelse(bh); - return 0; -} - -/** - * gfs2_dir_add - Add new filename into directory - * @dip: The GFS2 inode - * @filename: The new name - * @inode: The inode number of the entry - * @type: The type of the entry - * - * Returns: 0 on success, error code on failure - */ - -int gfs2_dir_add(struct inode *inode, const struct qstr *name, - const struct gfs2_inum *inum, unsigned type) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct buffer_head *bh; - struct gfs2_dirent *dent; - struct gfs2_leaf *leaf; - int error; - - while(1) { - dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, - &bh); - if (dent) { - if (IS_ERR(dent)) - return PTR_ERR(dent); - dent = gfs2_init_dirent(inode, dent, name, bh); - gfs2_inum_out(inum, (char *)&dent->de_inum); - dent->de_type = cpu_to_be16(type); - if (ip->i_di.di_flags & GFS2_DIF_EXHASH) { - leaf = (struct gfs2_leaf *)bh->b_data; - leaf->lf_entries = cpu_to_be16(be16_to_cpu(leaf->lf_entries) + 1); - } - brelse(bh); - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - break; - gfs2_trans_add_bh(ip->i_gl, bh, 1); - ip->i_di.di_entries++; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - gfs2_dinode_out(&ip->i_di, bh->b_data); - brelse(bh); - error = 0; - break; - } - if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH)) { - error = dir_make_exhash(inode); - if (error) - break; - continue; - } - error = dir_split_leaf(inode, name); - if (error == 0) - continue; - if (error < 0) - break; - if (ip->i_di.di_depth < GFS2_DIR_MAX_DEPTH) { - error = dir_double_exhash(ip); - if (error) - break; - error = dir_split_leaf(inode, name); - if (error < 0) - break; - if (error == 0) - continue; - } - error = dir_new_leaf(inode, name); - if (!error) - continue; - error = -ENOSPC; - break; - } - return error; -} - - -/** - * gfs2_dir_del - Delete a directory entry - * @dip: The GFS2 inode - * @filename: The filename - * - * Returns: 0 on success, error code on failure - */ - -int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) -{ - struct gfs2_dirent *dent, *prev = NULL; - struct buffer_head *bh; - int error; - - /* Returns _either_ the entry (if its first in block) or the - previous entry otherwise */ - dent = gfs2_dirent_search(&dip->i_inode, name, gfs2_dirent_prev, &bh); - if (!dent) { - gfs2_consist_inode(dip); - return -EIO; - } - if (IS_ERR(dent)) { - gfs2_consist_inode(dip); - return PTR_ERR(dent); - } - /* If not first in block, adjust pointers accordingly */ - if (gfs2_dirent_find(dent, name, NULL) == 0) { - prev = dent; - dent = (struct gfs2_dirent *)((char *)dent + be16_to_cpu(prev->de_rec_len)); - } - - dirent_del(dip, bh, prev, dent); - if (dip->i_di.di_flags & GFS2_DIF_EXHASH) { - struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data; - u16 entries = be16_to_cpu(leaf->lf_entries); - if (!entries) - gfs2_consist_inode(dip); - leaf->lf_entries = cpu_to_be16(--entries); - } - brelse(bh); - - error = gfs2_meta_inode_buffer(dip, &bh); - if (error) - return error; - - if (!dip->i_di.di_entries) - gfs2_consist_inode(dip); - gfs2_trans_add_bh(dip->i_gl, bh, 1); - dip->i_di.di_entries--; - dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds(); - gfs2_dinode_out(&dip->i_di, bh->b_data); - brelse(bh); - mark_inode_dirty(&dip->i_inode); - - return error; -} - -/** - * gfs2_dir_mvino - Change inode number of directory entry - * @dip: The GFS2 inode - * @filename: - * @new_inode: - * - * This routine changes the inode number of a directory entry. It's used - * by rename to change ".." when a directory is moved. - * Assumes a glock is held on dvp. - * - * Returns: errno - */ - -int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, - struct gfs2_inum *inum, unsigned int new_type) -{ - struct buffer_head *bh; - struct gfs2_dirent *dent; - int error; - - dent = gfs2_dirent_search(&dip->i_inode, filename, gfs2_dirent_find, &bh); - if (!dent) { - gfs2_consist_inode(dip); - return -EIO; - } - if (IS_ERR(dent)) - return PTR_ERR(dent); - - gfs2_trans_add_bh(dip->i_gl, bh, 1); - gfs2_inum_out(inum, (char *)&dent->de_inum); - dent->de_type = cpu_to_be16(new_type); - - if (dip->i_di.di_flags & GFS2_DIF_EXHASH) { - brelse(bh); - error = gfs2_meta_inode_buffer(dip, &bh); - if (error) - return error; - gfs2_trans_add_bh(dip->i_gl, bh, 1); - } - - dip->i_di.di_mtime = dip->i_di.di_ctime = get_seconds(); - gfs2_dinode_out(&dip->i_di, bh->b_data); - brelse(bh); - return 0; -} - -/** - * foreach_leaf - call a function for each leaf in a directory - * @dip: the directory - * @lc: the function to call for each each - * @data: private data to pass to it - * - * Returns: errno - */ - -static int foreach_leaf(struct gfs2_inode *dip, leaf_call_t lc, void *data) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct buffer_head *bh; - struct gfs2_leaf *leaf; - u32 hsize, len; - u32 ht_offset, lp_offset, ht_offset_cur = -1; - u32 index = 0; - u64 *lp; - u64 leaf_no; - int error = 0; - - hsize = 1 << dip->i_di.di_depth; - if (hsize * sizeof(u64) != dip->i_di.di_size) { - gfs2_consist_inode(dip); - return -EIO; - } - - lp = kmalloc(sdp->sd_hash_bsize, GFP_KERNEL); - if (!lp) - return -ENOMEM; - - while (index < hsize) { - lp_offset = index & (sdp->sd_hash_ptrs - 1); - ht_offset = index - lp_offset; - - if (ht_offset_cur != ht_offset) { - error = gfs2_dir_read_data(dip, (char *)lp, - ht_offset * sizeof(u64), - sdp->sd_hash_bsize, 1); - if (error != sdp->sd_hash_bsize) { - if (error >= 0) - error = -EIO; - goto out; - } - ht_offset_cur = ht_offset; - } - - leaf_no = be64_to_cpu(lp[lp_offset]); - if (leaf_no) { - error = get_leaf(dip, leaf_no, &bh); - if (error) - goto out; - leaf = (struct gfs2_leaf *)bh->b_data; - len = 1 << (dip->i_di.di_depth - be16_to_cpu(leaf->lf_depth)); - brelse(bh); - - error = lc(dip, index, len, leaf_no, data); - if (error) - goto out; - - index = (index & ~(len - 1)) + len; - } else - index++; - } - - if (index != hsize) { - gfs2_consist_inode(dip); - error = -EIO; - } - -out: - kfree(lp); - - return error; -} - -/** - * leaf_dealloc - Deallocate a directory leaf - * @dip: the directory - * @index: the hash table offset in the directory - * @len: the number of pointers to this leaf - * @leaf_no: the leaf number - * @data: not used - * - * Returns: errno - */ - -static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, - u64 leaf_no, void *data) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_leaf *tmp_leaf; - struct gfs2_rgrp_list rlist; - struct buffer_head *bh, *dibh; - u64 blk, nblk; - unsigned int rg_blocks = 0, l_blocks = 0; - char *ht; - unsigned int x, size = len * sizeof(u64); - int error; - - memset(&rlist, 0, sizeof(struct gfs2_rgrp_list)); - - ht = kzalloc(size, GFP_KERNEL); - if (!ht) - return -ENOMEM; - - gfs2_alloc_get(dip); - - error = gfs2_quota_hold(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out; - - error = gfs2_rindex_hold(sdp, &dip->i_alloc.al_ri_gh); - if (error) - goto out_qs; - - /* Count the number of leaves */ - - for (blk = leaf_no; blk; blk = nblk) { - error = get_leaf(dip, blk, &bh); - if (error) - goto out_rlist; - tmp_leaf = (struct gfs2_leaf *)bh->b_data; - nblk = be64_to_cpu(tmp_leaf->lf_next); - brelse(bh); - - gfs2_rlist_add(sdp, &rlist, blk); - l_blocks++; - } - - gfs2_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0); - - for (x = 0; x < rlist.rl_rgrps; x++) { - struct gfs2_rgrpd *rgd; - rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; - } - - error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); - if (error) - goto out_rlist; - - error = gfs2_trans_begin(sdp, - rg_blocks + (DIV_ROUND_UP(size, sdp->sd_jbsize) + 1) + - RES_DINODE + RES_STATFS + RES_QUOTA, l_blocks); - if (error) - goto out_rg_gunlock; - - for (blk = leaf_no; blk; blk = nblk) { - error = get_leaf(dip, blk, &bh); - if (error) - goto out_end_trans; - tmp_leaf = (struct gfs2_leaf *)bh->b_data; - nblk = be64_to_cpu(tmp_leaf->lf_next); - brelse(bh); - - gfs2_free_meta(dip, blk, 1); - - if (!dip->i_di.di_blocks) - gfs2_consist_inode(dip); - dip->i_di.di_blocks--; - } - - error = gfs2_dir_write_data(dip, ht, index * sizeof(u64), size); - if (error != size) { - if (error >= 0) - error = -EIO; - goto out_end_trans; - } - - error = gfs2_meta_inode_buffer(dip, &dibh); - if (error) - goto out_end_trans; - - gfs2_trans_add_bh(dip->i_gl, dibh, 1); - gfs2_dinode_out(&dip->i_di, dibh->b_data); - brelse(dibh); - -out_end_trans: - gfs2_trans_end(sdp); -out_rg_gunlock: - gfs2_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs); -out_rlist: - gfs2_rlist_free(&rlist); - gfs2_glock_dq_uninit(&dip->i_alloc.al_ri_gh); -out_qs: - gfs2_quota_unhold(dip); -out: - gfs2_alloc_put(dip); - kfree(ht); - return error; -} - -/** - * gfs2_dir_exhash_dealloc - free all the leaf blocks in a directory - * @dip: the directory - * - * Dealloc all on-disk directory leaves to FREEMETA state - * Change on-disk inode type to "regular file" - * - * Returns: errno - */ - -int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct buffer_head *bh; - int error; - - /* Dealloc on-disk leaves to FREEMETA state */ - error = foreach_leaf(dip, leaf_dealloc, NULL); - if (error) - return error; - - /* Make this a regular file in case we crash. - (We don't want to free these blocks a second time.) */ - - error = gfs2_trans_begin(sdp, RES_DINODE, 0); - if (error) - return error; - - error = gfs2_meta_inode_buffer(dip, &bh); - if (!error) { - gfs2_trans_add_bh(dip->i_gl, bh, 1); - ((struct gfs2_dinode *)bh->b_data)->di_mode = - cpu_to_be32(S_IFREG); - brelse(bh); - } - - gfs2_trans_end(sdp); - - return error; -} - -/** - * gfs2_diradd_alloc_required - find if adding entry will require an allocation - * @ip: the file being written to - * @filname: the filename that's going to be added - * - * Returns: 1 if alloc required, 0 if not, -ve on error - */ - -int gfs2_diradd_alloc_required(struct inode *inode, const struct qstr *name) -{ - struct gfs2_dirent *dent; - struct buffer_head *bh; - - dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, &bh); - if (!dent) { - return 1; - } - if (IS_ERR(dent)) - return PTR_ERR(dent); - brelse(bh); - return 0; -} - diff --git a/trunk/fs/gfs2/dir.h b/trunk/fs/gfs2/dir.h deleted file mode 100644 index 371233419b07..000000000000 --- a/trunk/fs/gfs2/dir.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __DIR_DOT_H__ -#define __DIR_DOT_H__ - -#include - -struct inode; -struct gfs2_inode; -struct gfs2_inum; - -/** - * gfs2_filldir_t - Report a directory entry to the caller of gfs2_dir_read() - * @opaque: opaque data used by the function - * @name: the name of the directory entry - * @length: the length of the name - * @offset: the entry's offset in the directory - * @inum: the inode number the entry points to - * @type: the type of inode the entry points to - * - * Returns: 0 on success, 1 if buffer full - */ - -typedef int (*gfs2_filldir_t) (void *opaque, - const char *name, unsigned int length, - u64 offset, - struct gfs2_inum *inum, unsigned int type); - -int gfs2_dir_search(struct inode *dir, const struct qstr *filename, - struct gfs2_inum *inum, unsigned int *type); -int gfs2_dir_add(struct inode *inode, const struct qstr *filename, - const struct gfs2_inum *inum, unsigned int type); -int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); -int gfs2_dir_read(struct inode *inode, u64 * offset, void *opaque, - gfs2_filldir_t filldir); -int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, - struct gfs2_inum *new_inum, unsigned int new_type); - -int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip); - -int gfs2_diradd_alloc_required(struct inode *dir, - const struct qstr *filename); -int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, - struct buffer_head **bhp); - -static inline u32 gfs2_disk_hash(const char *data, int len) -{ - return crc32_le((u32)~0, data, len) ^ (u32)~0; -} - - -static inline void gfs2_str2qstr(struct qstr *name, const char *fname) -{ - name->name = fname; - name->len = strlen(fname); - name->hash = gfs2_disk_hash(name->name, name->len); -} - -/* N.B. This probably ought to take inum & type as args as well */ -static inline void gfs2_qstr2dirent(const struct qstr *name, u16 reclen, struct gfs2_dirent *dent) -{ - dent->de_inum.no_addr = cpu_to_be64(0); - dent->de_inum.no_formal_ino = cpu_to_be64(0); - dent->de_hash = cpu_to_be32(name->hash); - dent->de_rec_len = cpu_to_be16(reclen); - dent->de_name_len = cpu_to_be16(name->len); - dent->de_type = cpu_to_be16(0); - memset(dent->__pad, 0, sizeof(dent->__pad)); - memcpy(dent + 1, name->name, name->len); -} - -#endif /* __DIR_DOT_H__ */ diff --git a/trunk/fs/gfs2/eaops.c b/trunk/fs/gfs2/eaops.c deleted file mode 100644 index 92c54e9b0dc3..000000000000 --- a/trunk/fs/gfs2/eaops.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "acl.h" -#include "eaops.h" -#include "eattr.h" -#include "util.h" - -/** - * gfs2_ea_name2type - get the type of the ea, and truncate type from the name - * @namep: ea name, possibly with type appended - * - * Returns: GFS2_EATYPE_XXX - */ - -unsigned int gfs2_ea_name2type(const char *name, const char **truncated_name) -{ - unsigned int type; - - if (strncmp(name, "system.", 7) == 0) { - type = GFS2_EATYPE_SYS; - if (truncated_name) - *truncated_name = name + sizeof("system.") - 1; - } else if (strncmp(name, "user.", 5) == 0) { - type = GFS2_EATYPE_USR; - if (truncated_name) - *truncated_name = name + sizeof("user.") - 1; - } else if (strncmp(name, "security.", 9) == 0) { - type = GFS2_EATYPE_SECURITY; - if (truncated_name) - *truncated_name = name + sizeof("security.") - 1; - } else { - type = GFS2_EATYPE_UNUSED; - if (truncated_name) - *truncated_name = NULL; - } - - return type; -} - -static int user_eo_get(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct inode *inode = &ip->i_inode; - int error = permission(inode, MAY_READ, NULL); - if (error) - return error; - - return gfs2_ea_get_i(ip, er); -} - -static int user_eo_set(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct inode *inode = &ip->i_inode; - - if (S_ISREG(inode->i_mode) || - (S_ISDIR(inode->i_mode) && !(inode->i_mode & S_ISVTX))) { - int error = permission(inode, MAY_WRITE, NULL); - if (error) - return error; - } else - return -EPERM; - - return gfs2_ea_set_i(ip, er); -} - -static int user_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct inode *inode = &ip->i_inode; - - if (S_ISREG(inode->i_mode) || - (S_ISDIR(inode->i_mode) && !(inode->i_mode & S_ISVTX))) { - int error = permission(inode, MAY_WRITE, NULL); - if (error) - return error; - } else - return -EPERM; - - return gfs2_ea_remove_i(ip, er); -} - -static int system_eo_get(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - if (!GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len) && - !GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len) && - !capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (GFS2_SB(&ip->i_inode)->sd_args.ar_posix_acl == 0 && - (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len) || - GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len))) - return -EOPNOTSUPP; - - - - return gfs2_ea_get_i(ip, er); -} - -static int system_eo_set(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - int remove = 0; - int error; - - if (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len)) { - if (!(er->er_flags & GFS2_ERF_MODE)) { - er->er_mode = ip->i_di.di_mode; - er->er_flags |= GFS2_ERF_MODE; - } - error = gfs2_acl_validate_set(ip, 1, er, - &remove, &er->er_mode); - if (error) - return error; - error = gfs2_ea_set_i(ip, er); - if (error) - return error; - if (remove) - gfs2_ea_remove_i(ip, er); - return 0; - - } else if (GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) { - error = gfs2_acl_validate_set(ip, 0, er, - &remove, NULL); - if (error) - return error; - if (!remove) - error = gfs2_ea_set_i(ip, er); - else { - error = gfs2_ea_remove_i(ip, er); - if (error == -ENODATA) - error = 0; - } - return error; - } - - return -EPERM; -} - -static int system_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - if (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len)) { - int error = gfs2_acl_validate_remove(ip, 1); - if (error) - return error; - - } else if (GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) { - int error = gfs2_acl_validate_remove(ip, 0); - if (error) - return error; - - } else - return -EPERM; - - return gfs2_ea_remove_i(ip, er); -} - -static int security_eo_get(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct inode *inode = &ip->i_inode; - int error = permission(inode, MAY_READ, NULL); - if (error) - return error; - - return gfs2_ea_get_i(ip, er); -} - -static int security_eo_set(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct inode *inode = &ip->i_inode; - int error = permission(inode, MAY_WRITE, NULL); - if (error) - return error; - - return gfs2_ea_set_i(ip, er); -} - -static int security_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct inode *inode = &ip->i_inode; - int error = permission(inode, MAY_WRITE, NULL); - if (error) - return error; - - return gfs2_ea_remove_i(ip, er); -} - -static struct gfs2_eattr_operations gfs2_user_eaops = { - .eo_get = user_eo_get, - .eo_set = user_eo_set, - .eo_remove = user_eo_remove, - .eo_name = "user", -}; - -struct gfs2_eattr_operations gfs2_system_eaops = { - .eo_get = system_eo_get, - .eo_set = system_eo_set, - .eo_remove = system_eo_remove, - .eo_name = "system", -}; - -static struct gfs2_eattr_operations gfs2_security_eaops = { - .eo_get = security_eo_get, - .eo_set = security_eo_set, - .eo_remove = security_eo_remove, - .eo_name = "security", -}; - -struct gfs2_eattr_operations *gfs2_ea_ops[] = { - NULL, - &gfs2_user_eaops, - &gfs2_system_eaops, - &gfs2_security_eaops, -}; - diff --git a/trunk/fs/gfs2/eaops.h b/trunk/fs/gfs2/eaops.h deleted file mode 100644 index 508b4f7a2449..000000000000 --- a/trunk/fs/gfs2/eaops.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __EAOPS_DOT_H__ -#define __EAOPS_DOT_H__ - -struct gfs2_ea_request; -struct gfs2_inode; - -struct gfs2_eattr_operations { - int (*eo_get) (struct gfs2_inode *ip, struct gfs2_ea_request *er); - int (*eo_set) (struct gfs2_inode *ip, struct gfs2_ea_request *er); - int (*eo_remove) (struct gfs2_inode *ip, struct gfs2_ea_request *er); - char *eo_name; -}; - -unsigned int gfs2_ea_name2type(const char *name, const char **truncated_name); - -extern struct gfs2_eattr_operations gfs2_system_eaops; - -extern struct gfs2_eattr_operations *gfs2_ea_ops[]; - -#endif /* __EAOPS_DOT_H__ */ - diff --git a/trunk/fs/gfs2/eattr.c b/trunk/fs/gfs2/eattr.c deleted file mode 100644 index a65a4ccfd4dd..000000000000 --- a/trunk/fs/gfs2/eattr.c +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "acl.h" -#include "eaops.h" -#include "eattr.h" -#include "glock.h" -#include "inode.h" -#include "meta_io.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" - -/** - * ea_calc_size - returns the acutal number of bytes the request will take up - * (not counting any unstuffed data blocks) - * @sdp: - * @er: - * @size: - * - * Returns: 1 if the EA should be stuffed - */ - -static int ea_calc_size(struct gfs2_sbd *sdp, struct gfs2_ea_request *er, - unsigned int *size) -{ - *size = GFS2_EAREQ_SIZE_STUFFED(er); - if (*size <= sdp->sd_jbsize) - return 1; - - *size = GFS2_EAREQ_SIZE_UNSTUFFED(sdp, er); - - return 0; -} - -static int ea_check_size(struct gfs2_sbd *sdp, struct gfs2_ea_request *er) -{ - unsigned int size; - - if (er->er_data_len > GFS2_EA_MAX_DATA_LEN) - return -ERANGE; - - ea_calc_size(sdp, er, &size); - - /* This can only happen with 512 byte blocks */ - if (size > sdp->sd_jbsize) - return -ERANGE; - - return 0; -} - -typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, - struct gfs2_ea_header *prev, void *private); - -static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh, - ea_call_t ea_call, void *data) -{ - struct gfs2_ea_header *ea, *prev = NULL; - int error = 0; - - if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_EA)) - return -EIO; - - for (ea = GFS2_EA_BH2FIRST(bh);; prev = ea, ea = GFS2_EA2NEXT(ea)) { - if (!GFS2_EA_REC_LEN(ea)) - goto fail; - if (!(bh->b_data <= (char *)ea && (char *)GFS2_EA2NEXT(ea) <= - bh->b_data + bh->b_size)) - goto fail; - if (!GFS2_EATYPE_VALID(ea->ea_type)) - goto fail; - - error = ea_call(ip, bh, ea, prev, data); - if (error) - return error; - - if (GFS2_EA_IS_LAST(ea)) { - if ((char *)GFS2_EA2NEXT(ea) != - bh->b_data + bh->b_size) - goto fail; - break; - } - } - - return error; - -fail: - gfs2_consist_inode(ip); - return -EIO; -} - -static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data) -{ - struct buffer_head *bh, *eabh; - u64 *eablk, *end; - int error; - - error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_WAIT, &bh); - if (error) - return error; - - if (!(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT)) { - error = ea_foreach_i(ip, bh, ea_call, data); - goto out; - } - - if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_IN)) { - error = -EIO; - goto out; - } - - eablk = (u64 *)(bh->b_data + sizeof(struct gfs2_meta_header)); - end = eablk + GFS2_SB(&ip->i_inode)->sd_inptrs; - - for (; eablk < end; eablk++) { - u64 bn; - - if (!*eablk) - break; - bn = be64_to_cpu(*eablk); - - error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, &eabh); - if (error) - break; - error = ea_foreach_i(ip, eabh, ea_call, data); - brelse(eabh); - if (error) - break; - } -out: - brelse(bh); - return error; -} - -struct ea_find { - struct gfs2_ea_request *ef_er; - struct gfs2_ea_location *ef_el; -}; - -static int ea_find_i(struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, - void *private) -{ - struct ea_find *ef = private; - struct gfs2_ea_request *er = ef->ef_er; - - if (ea->ea_type == GFS2_EATYPE_UNUSED) - return 0; - - if (ea->ea_type == er->er_type) { - if (ea->ea_name_len == er->er_name_len && - !memcmp(GFS2_EA2NAME(ea), er->er_name, ea->ea_name_len)) { - struct gfs2_ea_location *el = ef->ef_el; - get_bh(bh); - el->el_bh = bh; - el->el_ea = ea; - el->el_prev = prev; - return 1; - } - } - - return 0; -} - -int gfs2_ea_find(struct gfs2_inode *ip, struct gfs2_ea_request *er, - struct gfs2_ea_location *el) -{ - struct ea_find ef; - int error; - - ef.ef_er = er; - ef.ef_el = el; - - memset(el, 0, sizeof(struct gfs2_ea_location)); - - error = ea_foreach(ip, ea_find_i, &ef); - if (error > 0) - return 0; - - return error; -} - -/** - * ea_dealloc_unstuffed - - * @ip: - * @bh: - * @ea: - * @prev: - * @private: - * - * Take advantage of the fact that all unstuffed blocks are - * allocated from the same RG. But watch, this may not always - * be true. - * - * Returns: errno - */ - -static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, - struct gfs2_ea_header *prev, void *private) -{ - int *leave = private; - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_rgrpd *rgd; - struct gfs2_holder rg_gh; - struct buffer_head *dibh; - u64 *dataptrs, bn = 0; - u64 bstart = 0; - unsigned int blen = 0; - unsigned int blks = 0; - unsigned int x; - int error; - - if (GFS2_EA_IS_STUFFED(ea)) - return 0; - - dataptrs = GFS2_EA2DATAPTRS(ea); - for (x = 0; x < ea->ea_num_ptrs; x++, dataptrs++) { - if (*dataptrs) { - blks++; - bn = be64_to_cpu(*dataptrs); - } - } - if (!blks) - return 0; - - rgd = gfs2_blk2rgrpd(sdp, bn); - if (!rgd) { - gfs2_consist_inode(ip); - return -EIO; - } - - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &rg_gh); - if (error) - return error; - - error = gfs2_trans_begin(sdp, rgd->rd_ri.ri_length + RES_DINODE + - RES_EATTR + RES_STATFS + RES_QUOTA, blks); - if (error) - goto out_gunlock; - - gfs2_trans_add_bh(ip->i_gl, bh, 1); - - dataptrs = GFS2_EA2DATAPTRS(ea); - for (x = 0; x < ea->ea_num_ptrs; x++, dataptrs++) { - if (!*dataptrs) - break; - bn = be64_to_cpu(*dataptrs); - - if (bstart + blen == bn) - blen++; - else { - if (bstart) - gfs2_free_meta(ip, bstart, blen); - bstart = bn; - blen = 1; - } - - *dataptrs = 0; - if (!ip->i_di.di_blocks) - gfs2_consist_inode(ip); - ip->i_di.di_blocks--; - } - if (bstart) - gfs2_free_meta(ip, bstart, blen); - - if (prev && !leave) { - u32 len; - - len = GFS2_EA_REC_LEN(prev) + GFS2_EA_REC_LEN(ea); - prev->ea_rec_len = cpu_to_be32(len); - - if (GFS2_EA_IS_LAST(ea)) - prev->ea_flags |= GFS2_EAFLAG_LAST; - } else { - ea->ea_type = GFS2_EATYPE_UNUSED; - ea->ea_num_ptrs = 0; - } - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - ip->i_di.di_ctime = get_seconds(); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(sdp); - -out_gunlock: - gfs2_glock_dq_uninit(&rg_gh); - return error; -} - -static int ea_remove_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, - struct gfs2_ea_header *prev, int leave) -{ - struct gfs2_alloc *al; - int error; - - al = gfs2_alloc_get(ip); - - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out_alloc; - - error = gfs2_rindex_hold(GFS2_SB(&ip->i_inode), &al->al_ri_gh); - if (error) - goto out_quota; - - error = ea_dealloc_unstuffed(ip, bh, ea, prev, (leave) ? &error : NULL); - - gfs2_glock_dq_uninit(&al->al_ri_gh); - -out_quota: - gfs2_quota_unhold(ip); -out_alloc: - gfs2_alloc_put(ip); - return error; -} - -struct ea_list { - struct gfs2_ea_request *ei_er; - unsigned int ei_size; -}; - -static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, - void *private) -{ - struct ea_list *ei = private; - struct gfs2_ea_request *er = ei->ei_er; - unsigned int ea_size = gfs2_ea_strlen(ea); - - if (ea->ea_type == GFS2_EATYPE_UNUSED) - return 0; - - if (er->er_data_len) { - char *prefix = NULL; - unsigned int l = 0; - char c = 0; - - if (ei->ei_size + ea_size > er->er_data_len) - return -ERANGE; - - switch (ea->ea_type) { - case GFS2_EATYPE_USR: - prefix = "user."; - l = 5; - break; - case GFS2_EATYPE_SYS: - prefix = "system."; - l = 7; - break; - case GFS2_EATYPE_SECURITY: - prefix = "security."; - l = 9; - break; - } - - BUG_ON(l == 0); - - memcpy(er->er_data + ei->ei_size, prefix, l); - memcpy(er->er_data + ei->ei_size + l, GFS2_EA2NAME(ea), - ea->ea_name_len); - memcpy(er->er_data + ei->ei_size + ea_size - 1, &c, 1); - } - - ei->ei_size += ea_size; - - return 0; -} - -/** - * gfs2_ea_list - - * @ip: - * @er: - * - * Returns: actual size of data on success, -errno on error - */ - -int gfs2_ea_list(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_holder i_gh; - int error; - - if (!er->er_data || !er->er_data_len) { - er->er_data = NULL; - er->er_data_len = 0; - } - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (error) - return error; - - if (ip->i_di.di_eattr) { - struct ea_list ei = { .ei_er = er, .ei_size = 0 }; - - error = ea_foreach(ip, ea_list_i, &ei); - if (!error) - error = ei.ei_size; - } - - gfs2_glock_dq_uninit(&i_gh); - - return error; -} - -/** - * ea_get_unstuffed - actually copies the unstuffed data into the - * request buffer - * @ip: The GFS2 inode - * @ea: The extended attribute header structure - * @data: The data to be copied - * - * Returns: errno - */ - -static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea, - char *data) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head **bh; - unsigned int amount = GFS2_EA_DATA_LEN(ea); - unsigned int nptrs = DIV_ROUND_UP(amount, sdp->sd_jbsize); - u64 *dataptrs = GFS2_EA2DATAPTRS(ea); - unsigned int x; - int error = 0; - - bh = kcalloc(nptrs, sizeof(struct buffer_head *), GFP_KERNEL); - if (!bh) - return -ENOMEM; - - for (x = 0; x < nptrs; x++) { - error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0, - bh + x); - if (error) { - while (x--) - brelse(bh[x]); - goto out; - } - dataptrs++; - } - - for (x = 0; x < nptrs; x++) { - error = gfs2_meta_wait(sdp, bh[x]); - if (error) { - for (; x < nptrs; x++) - brelse(bh[x]); - goto out; - } - if (gfs2_metatype_check(sdp, bh[x], GFS2_METATYPE_ED)) { - for (; x < nptrs; x++) - brelse(bh[x]); - error = -EIO; - goto out; - } - - memcpy(data, bh[x]->b_data + sizeof(struct gfs2_meta_header), - (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize); - - amount -= sdp->sd_jbsize; - data += sdp->sd_jbsize; - - brelse(bh[x]); - } - -out: - kfree(bh); - return error; -} - -int gfs2_ea_get_copy(struct gfs2_inode *ip, struct gfs2_ea_location *el, - char *data) -{ - if (GFS2_EA_IS_STUFFED(el->el_ea)) { - memcpy(data, GFS2_EA2DATA(el->el_ea), GFS2_EA_DATA_LEN(el->el_ea)); - return 0; - } else - return ea_get_unstuffed(ip, el->el_ea, data); -} - -/** - * gfs2_ea_get_i - - * @ip: The GFS2 inode - * @er: The request structure - * - * Returns: actual size of data on success, -errno on error - */ - -int gfs2_ea_get_i(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_ea_location el; - int error; - - if (!ip->i_di.di_eattr) - return -ENODATA; - - error = gfs2_ea_find(ip, er, &el); - if (error) - return error; - if (!el.el_ea) - return -ENODATA; - - if (er->er_data_len) { - if (GFS2_EA_DATA_LEN(el.el_ea) > er->er_data_len) - error = -ERANGE; - else - error = gfs2_ea_get_copy(ip, &el, er->er_data); - } - if (!error) - error = GFS2_EA_DATA_LEN(el.el_ea); - - brelse(el.el_bh); - - return error; -} - -/** - * gfs2_ea_get - - * @ip: The GFS2 inode - * @er: The request structure - * - * Returns: actual size of data on success, -errno on error - */ - -int gfs2_ea_get(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_holder i_gh; - int error; - - if (!er->er_name_len || - er->er_name_len > GFS2_EA_MAX_NAME_LEN) - return -EINVAL; - if (!er->er_data || !er->er_data_len) { - er->er_data = NULL; - er->er_data_len = 0; - } - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (error) - return error; - - error = gfs2_ea_ops[er->er_type]->eo_get(ip, er); - - gfs2_glock_dq_uninit(&i_gh); - - return error; -} - -/** - * ea_alloc_blk - allocates a new block for extended attributes. - * @ip: A pointer to the inode that's getting extended attributes - * @bhp: Pointer to pointer to a struct buffer_head - * - * Returns: errno - */ - -static int ea_alloc_blk(struct gfs2_inode *ip, struct buffer_head **bhp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_ea_header *ea; - u64 block; - - block = gfs2_alloc_meta(ip); - - *bhp = gfs2_meta_new(ip->i_gl, block); - gfs2_trans_add_bh(ip->i_gl, *bhp, 1); - gfs2_metatype_set(*bhp, GFS2_METATYPE_EA, GFS2_FORMAT_EA); - gfs2_buffer_clear_tail(*bhp, sizeof(struct gfs2_meta_header)); - - ea = GFS2_EA_BH2FIRST(*bhp); - ea->ea_rec_len = cpu_to_be32(sdp->sd_jbsize); - ea->ea_type = GFS2_EATYPE_UNUSED; - ea->ea_flags = GFS2_EAFLAG_LAST; - ea->ea_num_ptrs = 0; - - ip->i_di.di_blocks++; - - return 0; -} - -/** - * ea_write - writes the request info to an ea, creating new blocks if - * necessary - * @ip: inode that is being modified - * @ea: the location of the new ea in a block - * @er: the write request - * - * Note: does not update ea_rec_len or the GFS2_EAFLAG_LAST bin of ea_flags - * - * returns : errno - */ - -static int ea_write(struct gfs2_inode *ip, struct gfs2_ea_header *ea, - struct gfs2_ea_request *er) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - - ea->ea_data_len = cpu_to_be32(er->er_data_len); - ea->ea_name_len = er->er_name_len; - ea->ea_type = er->er_type; - ea->__pad = 0; - - memcpy(GFS2_EA2NAME(ea), er->er_name, er->er_name_len); - - if (GFS2_EAREQ_SIZE_STUFFED(er) <= sdp->sd_jbsize) { - ea->ea_num_ptrs = 0; - memcpy(GFS2_EA2DATA(ea), er->er_data, er->er_data_len); - } else { - u64 *dataptr = GFS2_EA2DATAPTRS(ea); - const char *data = er->er_data; - unsigned int data_len = er->er_data_len; - unsigned int copy; - unsigned int x; - - ea->ea_num_ptrs = DIV_ROUND_UP(er->er_data_len, sdp->sd_jbsize); - for (x = 0; x < ea->ea_num_ptrs; x++) { - struct buffer_head *bh; - u64 block; - int mh_size = sizeof(struct gfs2_meta_header); - - block = gfs2_alloc_meta(ip); - - bh = gfs2_meta_new(ip->i_gl, block); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_metatype_set(bh, GFS2_METATYPE_ED, GFS2_FORMAT_ED); - - ip->i_di.di_blocks++; - - copy = data_len > sdp->sd_jbsize ? sdp->sd_jbsize : - data_len; - memcpy(bh->b_data + mh_size, data, copy); - if (copy < sdp->sd_jbsize) - memset(bh->b_data + mh_size + copy, 0, - sdp->sd_jbsize - copy); - - *dataptr++ = cpu_to_be64(bh->b_blocknr); - data += copy; - data_len -= copy; - - brelse(bh); - } - - gfs2_assert_withdraw(sdp, !data_len); - } - - return 0; -} - -typedef int (*ea_skeleton_call_t) (struct gfs2_inode *ip, - struct gfs2_ea_request *er, void *private); - -static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, - unsigned int blks, - ea_skeleton_call_t skeleton_call, void *private) -{ - struct gfs2_alloc *al; - struct buffer_head *dibh; - int error; - - al = gfs2_alloc_get(ip); - - error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out; - - error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); - if (error) - goto out_gunlock_q; - - al->al_requested = blks; - - error = gfs2_inplace_reserve(ip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), - blks + al->al_rgd->rd_ri.ri_length + - RES_DINODE + RES_STATFS + RES_QUOTA, 0); - if (error) - goto out_ipres; - - error = skeleton_call(ip, er, private); - if (error) - goto out_end_trans; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - if (er->er_flags & GFS2_ERF_MODE) { - gfs2_assert_withdraw(GFS2_SB(&ip->i_inode), - (ip->i_di.di_mode & S_IFMT) == - (er->er_mode & S_IFMT)); - ip->i_di.di_mode = er->er_mode; - } - ip->i_di.di_ctime = get_seconds(); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - -out_end_trans: - gfs2_trans_end(GFS2_SB(&ip->i_inode)); -out_ipres: - gfs2_inplace_release(ip); -out_gunlock_q: - gfs2_quota_unlock(ip); -out: - gfs2_alloc_put(ip); - return error; -} - -static int ea_init_i(struct gfs2_inode *ip, struct gfs2_ea_request *er, - void *private) -{ - struct buffer_head *bh; - int error; - - error = ea_alloc_blk(ip, &bh); - if (error) - return error; - - ip->i_di.di_eattr = bh->b_blocknr; - error = ea_write(ip, GFS2_EA_BH2FIRST(bh), er); - - brelse(bh); - - return error; -} - -/** - * ea_init - initializes a new eattr block - * @ip: - * @er: - * - * Returns: errno - */ - -static int ea_init(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - unsigned int jbsize = GFS2_SB(&ip->i_inode)->sd_jbsize; - unsigned int blks = 1; - - if (GFS2_EAREQ_SIZE_STUFFED(er) > jbsize) - blks += DIV_ROUND_UP(er->er_data_len, jbsize); - - return ea_alloc_skeleton(ip, er, blks, ea_init_i, NULL); -} - -static struct gfs2_ea_header *ea_split_ea(struct gfs2_ea_header *ea) -{ - u32 ea_size = GFS2_EA_SIZE(ea); - struct gfs2_ea_header *new = (struct gfs2_ea_header *)((char *)ea + - ea_size); - u32 new_size = GFS2_EA_REC_LEN(ea) - ea_size; - int last = ea->ea_flags & GFS2_EAFLAG_LAST; - - ea->ea_rec_len = cpu_to_be32(ea_size); - ea->ea_flags ^= last; - - new->ea_rec_len = cpu_to_be32(new_size); - new->ea_flags = last; - - return new; -} - -static void ea_set_remove_stuffed(struct gfs2_inode *ip, - struct gfs2_ea_location *el) -{ - struct gfs2_ea_header *ea = el->el_ea; - struct gfs2_ea_header *prev = el->el_prev; - u32 len; - - gfs2_trans_add_bh(ip->i_gl, el->el_bh, 1); - - if (!prev || !GFS2_EA_IS_STUFFED(ea)) { - ea->ea_type = GFS2_EATYPE_UNUSED; - return; - } else if (GFS2_EA2NEXT(prev) != ea) { - prev = GFS2_EA2NEXT(prev); - gfs2_assert_withdraw(GFS2_SB(&ip->i_inode), GFS2_EA2NEXT(prev) == ea); - } - - len = GFS2_EA_REC_LEN(prev) + GFS2_EA_REC_LEN(ea); - prev->ea_rec_len = cpu_to_be32(len); - - if (GFS2_EA_IS_LAST(ea)) - prev->ea_flags |= GFS2_EAFLAG_LAST; -} - -struct ea_set { - int ea_split; - - struct gfs2_ea_request *es_er; - struct gfs2_ea_location *es_el; - - struct buffer_head *es_bh; - struct gfs2_ea_header *es_ea; -}; - -static int ea_set_simple_noalloc(struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, struct ea_set *es) -{ - struct gfs2_ea_request *er = es->es_er; - struct buffer_head *dibh; - int error; - - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE + 2 * RES_EATTR, 0); - if (error) - return error; - - gfs2_trans_add_bh(ip->i_gl, bh, 1); - - if (es->ea_split) - ea = ea_split_ea(ea); - - ea_write(ip, ea, er); - - if (es->es_el) - ea_set_remove_stuffed(ip, es->es_el); - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out; - - if (er->er_flags & GFS2_ERF_MODE) { - gfs2_assert_withdraw(GFS2_SB(&ip->i_inode), - (ip->i_di.di_mode & S_IFMT) == (er->er_mode & S_IFMT)); - ip->i_di.di_mode = er->er_mode; - } - ip->i_di.di_ctime = get_seconds(); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); -out: - gfs2_trans_end(GFS2_SB(&ip->i_inode)); - return error; -} - -static int ea_set_simple_alloc(struct gfs2_inode *ip, - struct gfs2_ea_request *er, void *private) -{ - struct ea_set *es = private; - struct gfs2_ea_header *ea = es->es_ea; - int error; - - gfs2_trans_add_bh(ip->i_gl, es->es_bh, 1); - - if (es->ea_split) - ea = ea_split_ea(ea); - - error = ea_write(ip, ea, er); - if (error) - return error; - - if (es->es_el) - ea_set_remove_stuffed(ip, es->es_el); - - return 0; -} - -static int ea_set_simple(struct gfs2_inode *ip, struct buffer_head *bh, - struct gfs2_ea_header *ea, struct gfs2_ea_header *prev, - void *private) -{ - struct ea_set *es = private; - unsigned int size; - int stuffed; - int error; - - stuffed = ea_calc_size(GFS2_SB(&ip->i_inode), es->es_er, &size); - - if (ea->ea_type == GFS2_EATYPE_UNUSED) { - if (GFS2_EA_REC_LEN(ea) < size) - return 0; - if (!GFS2_EA_IS_STUFFED(ea)) { - error = ea_remove_unstuffed(ip, bh, ea, prev, 1); - if (error) - return error; - } - es->ea_split = 0; - } else if (GFS2_EA_REC_LEN(ea) - GFS2_EA_SIZE(ea) >= size) - es->ea_split = 1; - else - return 0; - - if (stuffed) { - error = ea_set_simple_noalloc(ip, bh, ea, es); - if (error) - return error; - } else { - unsigned int blks; - - es->es_bh = bh; - es->es_ea = ea; - blks = 2 + DIV_ROUND_UP(es->es_er->er_data_len, - GFS2_SB(&ip->i_inode)->sd_jbsize); - - error = ea_alloc_skeleton(ip, es->es_er, blks, - ea_set_simple_alloc, es); - if (error) - return error; - } - - return 1; -} - -static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er, - void *private) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head *indbh, *newbh; - u64 *eablk; - int error; - int mh_size = sizeof(struct gfs2_meta_header); - - if (ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT) { - u64 *end; - - error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_WAIT, - &indbh); - if (error) - return error; - - if (gfs2_metatype_check(sdp, indbh, GFS2_METATYPE_IN)) { - error = -EIO; - goto out; - } - - eablk = (u64 *)(indbh->b_data + mh_size); - end = eablk + sdp->sd_inptrs; - - for (; eablk < end; eablk++) - if (!*eablk) - break; - - if (eablk == end) { - error = -ENOSPC; - goto out; - } - - gfs2_trans_add_bh(ip->i_gl, indbh, 1); - } else { - u64 blk; - - blk = gfs2_alloc_meta(ip); - - indbh = gfs2_meta_new(ip->i_gl, blk); - gfs2_trans_add_bh(ip->i_gl, indbh, 1); - gfs2_metatype_set(indbh, GFS2_METATYPE_IN, GFS2_FORMAT_IN); - gfs2_buffer_clear_tail(indbh, mh_size); - - eablk = (u64 *)(indbh->b_data + mh_size); - *eablk = cpu_to_be64(ip->i_di.di_eattr); - ip->i_di.di_eattr = blk; - ip->i_di.di_flags |= GFS2_DIF_EA_INDIRECT; - ip->i_di.di_blocks++; - - eablk++; - } - - error = ea_alloc_blk(ip, &newbh); - if (error) - goto out; - - *eablk = cpu_to_be64((u64)newbh->b_blocknr); - error = ea_write(ip, GFS2_EA_BH2FIRST(newbh), er); - brelse(newbh); - if (error) - goto out; - - if (private) - ea_set_remove_stuffed(ip, private); - -out: - brelse(indbh); - return error; -} - -static int ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er, - struct gfs2_ea_location *el) -{ - struct ea_set es; - unsigned int blks = 2; - int error; - - memset(&es, 0, sizeof(struct ea_set)); - es.es_er = er; - es.es_el = el; - - error = ea_foreach(ip, ea_set_simple, &es); - if (error > 0) - return 0; - if (error) - return error; - - if (!(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT)) - blks++; - if (GFS2_EAREQ_SIZE_STUFFED(er) > GFS2_SB(&ip->i_inode)->sd_jbsize) - blks += DIV_ROUND_UP(er->er_data_len, GFS2_SB(&ip->i_inode)->sd_jbsize); - - return ea_alloc_skeleton(ip, er, blks, ea_set_block, el); -} - -static int ea_set_remove_unstuffed(struct gfs2_inode *ip, - struct gfs2_ea_location *el) -{ - if (el->el_prev && GFS2_EA2NEXT(el->el_prev) != el->el_ea) { - el->el_prev = GFS2_EA2NEXT(el->el_prev); - gfs2_assert_withdraw(GFS2_SB(&ip->i_inode), - GFS2_EA2NEXT(el->el_prev) == el->el_ea); - } - - return ea_remove_unstuffed(ip, el->el_bh, el->el_ea, el->el_prev,0); -} - -int gfs2_ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_ea_location el; - int error; - - if (!ip->i_di.di_eattr) { - if (er->er_flags & XATTR_REPLACE) - return -ENODATA; - return ea_init(ip, er); - } - - error = gfs2_ea_find(ip, er, &el); - if (error) - return error; - - if (el.el_ea) { - if (ip->i_di.di_flags & GFS2_DIF_APPENDONLY) { - brelse(el.el_bh); - return -EPERM; - } - - error = -EEXIST; - if (!(er->er_flags & XATTR_CREATE)) { - int unstuffed = !GFS2_EA_IS_STUFFED(el.el_ea); - error = ea_set_i(ip, er, &el); - if (!error && unstuffed) - ea_set_remove_unstuffed(ip, &el); - } - - brelse(el.el_bh); - } else { - error = -ENODATA; - if (!(er->er_flags & XATTR_REPLACE)) - error = ea_set_i(ip, er, NULL); - } - - return error; -} - -int gfs2_ea_set(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_holder i_gh; - int error; - - if (!er->er_name_len || er->er_name_len > GFS2_EA_MAX_NAME_LEN) - return -EINVAL; - if (!er->er_data || !er->er_data_len) { - er->er_data = NULL; - er->er_data_len = 0; - } - error = ea_check_size(GFS2_SB(&ip->i_inode), er); - if (error) - return error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); - if (error) - return error; - - if (IS_IMMUTABLE(&ip->i_inode)) - error = -EPERM; - else - error = gfs2_ea_ops[er->er_type]->eo_set(ip, er); - - gfs2_glock_dq_uninit(&i_gh); - - return error; -} - -static int ea_remove_stuffed(struct gfs2_inode *ip, struct gfs2_ea_location *el) -{ - struct gfs2_ea_header *ea = el->el_ea; - struct gfs2_ea_header *prev = el->el_prev; - struct buffer_head *dibh; - int error; - - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE + RES_EATTR, 0); - if (error) - return error; - - gfs2_trans_add_bh(ip->i_gl, el->el_bh, 1); - - if (prev) { - u32 len; - - len = GFS2_EA_REC_LEN(prev) + GFS2_EA_REC_LEN(ea); - prev->ea_rec_len = cpu_to_be32(len); - - if (GFS2_EA_IS_LAST(ea)) - prev->ea_flags |= GFS2_EAFLAG_LAST; - } else - ea->ea_type = GFS2_EATYPE_UNUSED; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - ip->i_di.di_ctime = get_seconds(); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(GFS2_SB(&ip->i_inode)); - - return error; -} - -int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_ea_location el; - int error; - - if (!ip->i_di.di_eattr) - return -ENODATA; - - error = gfs2_ea_find(ip, er, &el); - if (error) - return error; - if (!el.el_ea) - return -ENODATA; - - if (GFS2_EA_IS_STUFFED(el.el_ea)) - error = ea_remove_stuffed(ip, &el); - else - error = ea_remove_unstuffed(ip, el.el_bh, el.el_ea, el.el_prev, - 0); - - brelse(el.el_bh); - - return error; -} - -/** - * gfs2_ea_remove - sets (or creates or replaces) an extended attribute - * @ip: pointer to the inode of the target file - * @er: request information - * - * Returns: errno - */ - -int gfs2_ea_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er) -{ - struct gfs2_holder i_gh; - int error; - - if (!er->er_name_len || er->er_name_len > GFS2_EA_MAX_NAME_LEN) - return -EINVAL; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); - if (error) - return error; - - if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode)) - error = -EPERM; - else - error = gfs2_ea_ops[er->er_type]->eo_remove(ip, er); - - gfs2_glock_dq_uninit(&i_gh); - - return error; -} - -static int ea_acl_chmod_unstuffed(struct gfs2_inode *ip, - struct gfs2_ea_header *ea, char *data) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct buffer_head **bh; - unsigned int amount = GFS2_EA_DATA_LEN(ea); - unsigned int nptrs = DIV_ROUND_UP(amount, sdp->sd_jbsize); - u64 *dataptrs = GFS2_EA2DATAPTRS(ea); - unsigned int x; - int error; - - bh = kcalloc(nptrs, sizeof(struct buffer_head *), GFP_KERNEL); - if (!bh) - return -ENOMEM; - - error = gfs2_trans_begin(sdp, nptrs + RES_DINODE, 0); - if (error) - goto out; - - for (x = 0; x < nptrs; x++) { - error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0, - bh + x); - if (error) { - while (x--) - brelse(bh[x]); - goto fail; - } - dataptrs++; - } - - for (x = 0; x < nptrs; x++) { - error = gfs2_meta_wait(sdp, bh[x]); - if (error) { - for (; x < nptrs; x++) - brelse(bh[x]); - goto fail; - } - if (gfs2_metatype_check(sdp, bh[x], GFS2_METATYPE_ED)) { - for (; x < nptrs; x++) - brelse(bh[x]); - error = -EIO; - goto fail; - } - - gfs2_trans_add_bh(ip->i_gl, bh[x], 1); - - memcpy(bh[x]->b_data + sizeof(struct gfs2_meta_header), data, - (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize); - - amount -= sdp->sd_jbsize; - data += sdp->sd_jbsize; - - brelse(bh[x]); - } - -out: - kfree(bh); - return error; - -fail: - gfs2_trans_end(sdp); - kfree(bh); - return error; -} - -int gfs2_ea_acl_chmod(struct gfs2_inode *ip, struct gfs2_ea_location *el, - struct iattr *attr, char *data) -{ - struct buffer_head *dibh; - int error; - - if (GFS2_EA_IS_STUFFED(el->el_ea)) { - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE + RES_EATTR, 0); - if (error) - return error; - - gfs2_trans_add_bh(ip->i_gl, el->el_bh, 1); - memcpy(GFS2_EA2DATA(el->el_ea), data, - GFS2_EA_DATA_LEN(el->el_ea)); - } else - error = ea_acl_chmod_unstuffed(ip, el->el_ea, data); - - if (error) - return error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - error = inode_setattr(&ip->i_inode, attr); - gfs2_assert_warn(GFS2_SB(&ip->i_inode), !error); - gfs2_inode_attr_out(ip); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(GFS2_SB(&ip->i_inode)); - - return error; -} - -static int ea_dealloc_indirect(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_rgrp_list rlist; - struct buffer_head *indbh, *dibh; - u64 *eablk, *end; - unsigned int rg_blocks = 0; - u64 bstart = 0; - unsigned int blen = 0; - unsigned int blks = 0; - unsigned int x; - int error; - - memset(&rlist, 0, sizeof(struct gfs2_rgrp_list)); - - error = gfs2_meta_read(ip->i_gl, ip->i_di.di_eattr, DIO_WAIT, &indbh); - if (error) - return error; - - if (gfs2_metatype_check(sdp, indbh, GFS2_METATYPE_IN)) { - error = -EIO; - goto out; - } - - eablk = (u64 *)(indbh->b_data + sizeof(struct gfs2_meta_header)); - end = eablk + sdp->sd_inptrs; - - for (; eablk < end; eablk++) { - u64 bn; - - if (!*eablk) - break; - bn = be64_to_cpu(*eablk); - - if (bstart + blen == bn) - blen++; - else { - if (bstart) - gfs2_rlist_add(sdp, &rlist, bstart); - bstart = bn; - blen = 1; - } - blks++; - } - if (bstart) - gfs2_rlist_add(sdp, &rlist, bstart); - else - goto out; - - gfs2_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0); - - for (x = 0; x < rlist.rl_rgrps; x++) { - struct gfs2_rgrpd *rgd; - rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; - } - - error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); - if (error) - goto out_rlist_free; - - error = gfs2_trans_begin(sdp, rg_blocks + RES_DINODE + RES_INDIRECT + - RES_STATFS + RES_QUOTA, blks); - if (error) - goto out_gunlock; - - gfs2_trans_add_bh(ip->i_gl, indbh, 1); - - eablk = (u64 *)(indbh->b_data + sizeof(struct gfs2_meta_header)); - bstart = 0; - blen = 0; - - for (; eablk < end; eablk++) { - u64 bn; - - if (!*eablk) - break; - bn = be64_to_cpu(*eablk); - - if (bstart + blen == bn) - blen++; - else { - if (bstart) - gfs2_free_meta(ip, bstart, blen); - bstart = bn; - blen = 1; - } - - *eablk = 0; - if (!ip->i_di.di_blocks) - gfs2_consist_inode(ip); - ip->i_di.di_blocks--; - } - if (bstart) - gfs2_free_meta(ip, bstart, blen); - - ip->i_di.di_flags &= ~GFS2_DIF_EA_INDIRECT; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(sdp); - -out_gunlock: - gfs2_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs); -out_rlist_free: - gfs2_rlist_free(&rlist); -out: - brelse(indbh); - return error; -} - -static int ea_dealloc_block(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_rgrpd *rgd; - struct buffer_head *dibh; - int error; - - rgd = gfs2_blk2rgrpd(sdp, ip->i_di.di_eattr); - if (!rgd) { - gfs2_consist_inode(ip); - return -EIO; - } - - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, - &al->al_rgd_gh); - if (error) - return error; - - error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_DINODE + RES_STATFS + - RES_QUOTA, 1); - if (error) - goto out_gunlock; - - gfs2_free_meta(ip, ip->i_di.di_eattr, 1); - - ip->i_di.di_eattr = 0; - if (!ip->i_di.di_blocks) - gfs2_consist_inode(ip); - ip->i_di.di_blocks--; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(sdp); - -out_gunlock: - gfs2_glock_dq_uninit(&al->al_rgd_gh); - return error; -} - -/** - * gfs2_ea_dealloc - deallocate the extended attribute fork - * @ip: the inode - * - * Returns: errno - */ - -int gfs2_ea_dealloc(struct gfs2_inode *ip) -{ - struct gfs2_alloc *al; - int error; - - al = gfs2_alloc_get(ip); - - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out_alloc; - - error = gfs2_rindex_hold(GFS2_SB(&ip->i_inode), &al->al_ri_gh); - if (error) - goto out_quota; - - error = ea_foreach(ip, ea_dealloc_unstuffed, NULL); - if (error) - goto out_rindex; - - if (ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT) { - error = ea_dealloc_indirect(ip); - if (error) - goto out_rindex; - } - - error = ea_dealloc_block(ip); - -out_rindex: - gfs2_glock_dq_uninit(&al->al_ri_gh); -out_quota: - gfs2_quota_unhold(ip); -out_alloc: - gfs2_alloc_put(ip); - return error; -} - diff --git a/trunk/fs/gfs2/eattr.h b/trunk/fs/gfs2/eattr.h deleted file mode 100644 index ffa65947d686..000000000000 --- a/trunk/fs/gfs2/eattr.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __EATTR_DOT_H__ -#define __EATTR_DOT_H__ - -struct gfs2_inode; -struct iattr; - -#define GFS2_EA_REC_LEN(ea) be32_to_cpu((ea)->ea_rec_len) -#define GFS2_EA_DATA_LEN(ea) be32_to_cpu((ea)->ea_data_len) - -#define GFS2_EA_SIZE(ea) \ -ALIGN(sizeof(struct gfs2_ea_header) + (ea)->ea_name_len + \ - ((GFS2_EA_IS_STUFFED(ea)) ? GFS2_EA_DATA_LEN(ea) : \ - (sizeof(u64) * (ea)->ea_num_ptrs)), 8) - -#define GFS2_EA_IS_STUFFED(ea) (!(ea)->ea_num_ptrs) -#define GFS2_EA_IS_LAST(ea) ((ea)->ea_flags & GFS2_EAFLAG_LAST) - -#define GFS2_EAREQ_SIZE_STUFFED(er) \ -ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + (er)->er_data_len, 8) - -#define GFS2_EAREQ_SIZE_UNSTUFFED(sdp, er) \ -ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + \ - sizeof(u64) * DIV_ROUND_UP((er)->er_data_len, (sdp)->sd_jbsize), 8) - -#define GFS2_EA2NAME(ea) ((char *)((struct gfs2_ea_header *)(ea) + 1)) -#define GFS2_EA2DATA(ea) (GFS2_EA2NAME(ea) + (ea)->ea_name_len) - -#define GFS2_EA2DATAPTRS(ea) \ -((u64 *)(GFS2_EA2NAME(ea) + ALIGN((ea)->ea_name_len, 8))) - -#define GFS2_EA2NEXT(ea) \ -((struct gfs2_ea_header *)((char *)(ea) + GFS2_EA_REC_LEN(ea))) - -#define GFS2_EA_BH2FIRST(bh) \ -((struct gfs2_ea_header *)((bh)->b_data + sizeof(struct gfs2_meta_header))) - -#define GFS2_ERF_MODE 0x80000000 - -struct gfs2_ea_request { - const char *er_name; - char *er_data; - unsigned int er_name_len; - unsigned int er_data_len; - unsigned int er_type; /* GFS2_EATYPE_... */ - int er_flags; - mode_t er_mode; -}; - -struct gfs2_ea_location { - struct buffer_head *el_bh; - struct gfs2_ea_header *el_ea; - struct gfs2_ea_header *el_prev; -}; - -int gfs2_ea_get_i(struct gfs2_inode *ip, struct gfs2_ea_request *er); -int gfs2_ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er); -int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er); - -int gfs2_ea_list(struct gfs2_inode *ip, struct gfs2_ea_request *er); -int gfs2_ea_get(struct gfs2_inode *ip, struct gfs2_ea_request *er); -int gfs2_ea_set(struct gfs2_inode *ip, struct gfs2_ea_request *er); -int gfs2_ea_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er); - -int gfs2_ea_dealloc(struct gfs2_inode *ip); - -/* Exported to acl.c */ - -int gfs2_ea_find(struct gfs2_inode *ip, - struct gfs2_ea_request *er, - struct gfs2_ea_location *el); -int gfs2_ea_get_copy(struct gfs2_inode *ip, - struct gfs2_ea_location *el, - char *data); -int gfs2_ea_acl_chmod(struct gfs2_inode *ip, struct gfs2_ea_location *el, - struct iattr *attr, char *data); - -static inline unsigned int gfs2_ea_strlen(struct gfs2_ea_header *ea) -{ - switch (ea->ea_type) { - case GFS2_EATYPE_USR: - return 5 + ea->ea_name_len + 1; - case GFS2_EATYPE_SYS: - return 7 + ea->ea_name_len + 1; - case GFS2_EATYPE_SECURITY: - return 9 + ea->ea_name_len + 1; - default: - return 0; - } -} - -#endif /* __EATTR_DOT_H__ */ diff --git a/trunk/fs/gfs2/gfs2.h b/trunk/fs/gfs2/gfs2.h deleted file mode 100644 index 3bb11c0f8b56..000000000000 --- a/trunk/fs/gfs2/gfs2.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __GFS2_DOT_H__ -#define __GFS2_DOT_H__ - -enum { - NO_CREATE = 0, - CREATE = 1, -}; - -enum { - NO_WAIT = 0, - WAIT = 1, -}; - -enum { - NO_FORCE = 0, - FORCE = 1, -}; - -#define GFS2_FAST_NAME_SIZE 8 - -#endif /* __GFS2_DOT_H__ */ - diff --git a/trunk/fs/gfs2/glock.c b/trunk/fs/gfs2/glock.c deleted file mode 100644 index 78fe0fae23ff..000000000000 --- a/trunk/fs/gfs2/glock.c +++ /dev/null @@ -1,2231 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "lm.h" -#include "lops.h" -#include "meta_io.h" -#include "quota.h" -#include "super.h" -#include "util.h" - -struct greedy { - struct gfs2_holder gr_gh; - struct work_struct gr_work; -}; - -struct gfs2_gl_hash_bucket { - struct hlist_head hb_list; -}; - -typedef void (*glock_examiner) (struct gfs2_glock * gl); - -static int gfs2_dump_lockstate(struct gfs2_sbd *sdp); -static int dump_glock(struct gfs2_glock *gl); -static int dump_inode(struct gfs2_inode *ip); - -#define GFS2_GL_HASH_SHIFT 15 -#define GFS2_GL_HASH_SIZE (1 << GFS2_GL_HASH_SHIFT) -#define GFS2_GL_HASH_MASK (GFS2_GL_HASH_SIZE - 1) - -static struct gfs2_gl_hash_bucket gl_hash_table[GFS2_GL_HASH_SIZE]; - -/* - * Despite what you might think, the numbers below are not arbitrary :-) - * They are taken from the ipv4 routing hash code, which is well tested - * and thus should be nearly optimal. Later on we might tweek the numbers - * but for now this should be fine. - * - * The reason for putting the locks in a separate array from the list heads - * is that we can have fewer locks than list heads and save memory. We use - * the same hash function for both, but with a different hash mask. - */ -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) || \ - defined(CONFIG_PROVE_LOCKING) - -#ifdef CONFIG_LOCKDEP -# define GL_HASH_LOCK_SZ 256 -#else -# if NR_CPUS >= 32 -# define GL_HASH_LOCK_SZ 4096 -# elif NR_CPUS >= 16 -# define GL_HASH_LOCK_SZ 2048 -# elif NR_CPUS >= 8 -# define GL_HASH_LOCK_SZ 1024 -# elif NR_CPUS >= 4 -# define GL_HASH_LOCK_SZ 512 -# else -# define GL_HASH_LOCK_SZ 256 -# endif -#endif - -/* We never want more locks than chains */ -#if GFS2_GL_HASH_SIZE < GL_HASH_LOCK_SZ -# undef GL_HASH_LOCK_SZ -# define GL_HASH_LOCK_SZ GFS2_GL_HASH_SIZE -#endif - -static rwlock_t gl_hash_locks[GL_HASH_LOCK_SZ]; - -static inline rwlock_t *gl_lock_addr(unsigned int x) -{ - return &gl_hash_locks[x & (GL_HASH_LOCK_SZ-1)]; -} -#else /* not SMP, so no spinlocks required */ -static inline rwlock_t *gl_lock_addr(x) -{ - return NULL; -} -#endif - -/** - * relaxed_state_ok - is a requested lock compatible with the current lock mode? - * @actual: the current state of the lock - * @requested: the lock state that was requested by the caller - * @flags: the modifier flags passed in by the caller - * - * Returns: 1 if the locks are compatible, 0 otherwise - */ - -static inline int relaxed_state_ok(unsigned int actual, unsigned requested, - int flags) -{ - if (actual == requested) - return 1; - - if (flags & GL_EXACT) - return 0; - - if (actual == LM_ST_EXCLUSIVE && requested == LM_ST_SHARED) - return 1; - - if (actual != LM_ST_UNLOCKED && (flags & LM_FLAG_ANY)) - return 1; - - return 0; -} - -/** - * gl_hash() - Turn glock number into hash bucket number - * @lock: The glock number - * - * Returns: The number of the corresponding hash bucket - */ - -static unsigned int gl_hash(const struct gfs2_sbd *sdp, - const struct lm_lockname *name) -{ - unsigned int h; - - h = jhash(&name->ln_number, sizeof(u64), 0); - h = jhash(&name->ln_type, sizeof(unsigned int), h); - h = jhash(&sdp, sizeof(struct gfs2_sbd *), h); - h &= GFS2_GL_HASH_MASK; - - return h; -} - -/** - * glock_free() - Perform a few checks and then release struct gfs2_glock - * @gl: The glock to release - * - * Also calls lock module to release its internal structure for this glock. - * - */ - -static void glock_free(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - struct inode *aspace = gl->gl_aspace; - - gfs2_lm_put_lock(sdp, gl->gl_lock); - - if (aspace) - gfs2_aspace_put(aspace); - - kmem_cache_free(gfs2_glock_cachep, gl); -} - -/** - * gfs2_glock_hold() - increment reference count on glock - * @gl: The glock to hold - * - */ - -void gfs2_glock_hold(struct gfs2_glock *gl) -{ - atomic_inc(&gl->gl_ref); -} - -/** - * gfs2_glock_put() - Decrement reference count on glock - * @gl: The glock to put - * - */ - -int gfs2_glock_put(struct gfs2_glock *gl) -{ - int rv = 0; - struct gfs2_sbd *sdp = gl->gl_sbd; - - write_lock(gl_lock_addr(gl->gl_hash)); - if (atomic_dec_and_test(&gl->gl_ref)) { - hlist_del(&gl->gl_list); - write_unlock(gl_lock_addr(gl->gl_hash)); - BUG_ON(spin_is_locked(&gl->gl_spin)); - gfs2_assert(sdp, gl->gl_state == LM_ST_UNLOCKED); - gfs2_assert(sdp, list_empty(&gl->gl_reclaim)); - gfs2_assert(sdp, list_empty(&gl->gl_holders)); - gfs2_assert(sdp, list_empty(&gl->gl_waiters1)); - gfs2_assert(sdp, list_empty(&gl->gl_waiters2)); - gfs2_assert(sdp, list_empty(&gl->gl_waiters3)); - glock_free(gl); - rv = 1; - goto out; - } - write_unlock(gl_lock_addr(gl->gl_hash)); -out: - return rv; -} - -/** - * queue_empty - check to see if a glock's queue is empty - * @gl: the glock - * @head: the head of the queue to check - * - * This function protects the list in the event that a process already - * has a holder on the list and is adding a second holder for itself. - * The glmutex lock is what generally prevents processes from working - * on the same glock at once, but the special case of adding a second - * holder for yourself ("recursive" locking) doesn't involve locking - * glmutex, making the spin lock necessary. - * - * Returns: 1 if the queue is empty - */ - -static inline int queue_empty(struct gfs2_glock *gl, struct list_head *head) -{ - int empty; - spin_lock(&gl->gl_spin); - empty = list_empty(head); - spin_unlock(&gl->gl_spin); - return empty; -} - -/** - * search_bucket() - Find struct gfs2_glock by lock number - * @bucket: the bucket to search - * @name: The lock name - * - * Returns: NULL, or the struct gfs2_glock with the requested number - */ - -static struct gfs2_glock *search_bucket(unsigned int hash, - const struct gfs2_sbd *sdp, - const struct lm_lockname *name) -{ - struct gfs2_glock *gl; - struct hlist_node *h; - - hlist_for_each_entry(gl, h, &gl_hash_table[hash].hb_list, gl_list) { - if (!lm_name_equal(&gl->gl_name, name)) - continue; - if (gl->gl_sbd != sdp) - continue; - - atomic_inc(&gl->gl_ref); - - return gl; - } - - return NULL; -} - -/** - * gfs2_glock_find() - Find glock by lock number - * @sdp: The GFS2 superblock - * @name: The lock name - * - * Returns: NULL, or the struct gfs2_glock with the requested number - */ - -static struct gfs2_glock *gfs2_glock_find(const struct gfs2_sbd *sdp, - const struct lm_lockname *name) -{ - unsigned int hash = gl_hash(sdp, name); - struct gfs2_glock *gl; - - read_lock(gl_lock_addr(hash)); - gl = search_bucket(hash, sdp, name); - read_unlock(gl_lock_addr(hash)); - - return gl; -} - -/** - * gfs2_glock_get() - Get a glock, or create one if one doesn't exist - * @sdp: The GFS2 superblock - * @number: the lock number - * @glops: The glock_operations to use - * @create: If 0, don't create the glock if it doesn't exist - * @glp: the glock is returned here - * - * This does not lock a glock, just finds/creates structures for one. - * - * Returns: errno - */ - -int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, - const struct gfs2_glock_operations *glops, int create, - struct gfs2_glock **glp) -{ - struct lm_lockname name = { .ln_number = number, .ln_type = glops->go_type }; - struct gfs2_glock *gl, *tmp; - unsigned int hash = gl_hash(sdp, &name); - int error; - - read_lock(gl_lock_addr(hash)); - gl = search_bucket(hash, sdp, &name); - read_unlock(gl_lock_addr(hash)); - - if (gl || !create) { - *glp = gl; - return 0; - } - - gl = kmem_cache_alloc(gfs2_glock_cachep, GFP_KERNEL); - if (!gl) - return -ENOMEM; - - gl->gl_flags = 0; - gl->gl_name = name; - atomic_set(&gl->gl_ref, 1); - gl->gl_state = LM_ST_UNLOCKED; - gl->gl_hash = hash; - gl->gl_owner = NULL; - gl->gl_ip = 0; - gl->gl_ops = glops; - gl->gl_req_gh = NULL; - gl->gl_req_bh = NULL; - gl->gl_vn = 0; - gl->gl_stamp = jiffies; - gl->gl_object = NULL; - gl->gl_sbd = sdp; - gl->gl_aspace = NULL; - lops_init_le(&gl->gl_le, &gfs2_glock_lops); - - /* If this glock protects actual on-disk data or metadata blocks, - create a VFS inode to manage the pages/buffers holding them. */ - if (glops == &gfs2_inode_glops || glops == &gfs2_rgrp_glops) { - gl->gl_aspace = gfs2_aspace_get(sdp); - if (!gl->gl_aspace) { - error = -ENOMEM; - goto fail; - } - } - - error = gfs2_lm_get_lock(sdp, &name, &gl->gl_lock); - if (error) - goto fail_aspace; - - write_lock(gl_lock_addr(hash)); - tmp = search_bucket(hash, sdp, &name); - if (tmp) { - write_unlock(gl_lock_addr(hash)); - glock_free(gl); - gl = tmp; - } else { - hlist_add_head(&gl->gl_list, &gl_hash_table[hash].hb_list); - write_unlock(gl_lock_addr(hash)); - } - - *glp = gl; - - return 0; - -fail_aspace: - if (gl->gl_aspace) - gfs2_aspace_put(gl->gl_aspace); -fail: - kmem_cache_free(gfs2_glock_cachep, gl); - return error; -} - -/** - * gfs2_holder_init - initialize a struct gfs2_holder in the default way - * @gl: the glock - * @state: the state we're requesting - * @flags: the modifier flags - * @gh: the holder structure - * - */ - -void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, unsigned flags, - struct gfs2_holder *gh) -{ - INIT_LIST_HEAD(&gh->gh_list); - gh->gh_gl = gl; - gh->gh_ip = (unsigned long)__builtin_return_address(0); - gh->gh_owner = current; - gh->gh_state = state; - gh->gh_flags = flags; - gh->gh_error = 0; - gh->gh_iflags = 0; - init_completion(&gh->gh_wait); - - if (gh->gh_state == LM_ST_EXCLUSIVE) - gh->gh_flags |= GL_LOCAL_EXCL; - - gfs2_glock_hold(gl); -} - -/** - * gfs2_holder_reinit - reinitialize a struct gfs2_holder so we can requeue it - * @state: the state we're requesting - * @flags: the modifier flags - * @gh: the holder structure - * - * Don't mess with the glock. - * - */ - -void gfs2_holder_reinit(unsigned int state, unsigned flags, struct gfs2_holder *gh) -{ - gh->gh_state = state; - gh->gh_flags = flags; - if (gh->gh_state == LM_ST_EXCLUSIVE) - gh->gh_flags |= GL_LOCAL_EXCL; - - gh->gh_iflags &= 1 << HIF_ALLOCED; - gh->gh_ip = (unsigned long)__builtin_return_address(0); -} - -/** - * gfs2_holder_uninit - uninitialize a holder structure (drop glock reference) - * @gh: the holder structure - * - */ - -void gfs2_holder_uninit(struct gfs2_holder *gh) -{ - gfs2_glock_put(gh->gh_gl); - gh->gh_gl = NULL; - gh->gh_ip = 0; -} - -/** - * gfs2_holder_get - get a struct gfs2_holder structure - * @gl: the glock - * @state: the state we're requesting - * @flags: the modifier flags - * @gfp_flags: - * - * Figure out how big an impact this function has. Either: - * 1) Replace it with a cache of structures hanging off the struct gfs2_sbd - * 2) Leave it like it is - * - * Returns: the holder structure, NULL on ENOMEM - */ - -static struct gfs2_holder *gfs2_holder_get(struct gfs2_glock *gl, - unsigned int state, - int flags, gfp_t gfp_flags) -{ - struct gfs2_holder *gh; - - gh = kmalloc(sizeof(struct gfs2_holder), gfp_flags); - if (!gh) - return NULL; - - gfs2_holder_init(gl, state, flags, gh); - set_bit(HIF_ALLOCED, &gh->gh_iflags); - gh->gh_ip = (unsigned long)__builtin_return_address(0); - return gh; -} - -/** - * gfs2_holder_put - get rid of a struct gfs2_holder structure - * @gh: the holder structure - * - */ - -static void gfs2_holder_put(struct gfs2_holder *gh) -{ - gfs2_holder_uninit(gh); - kfree(gh); -} - -/** - * rq_mutex - process a mutex request in the queue - * @gh: the glock holder - * - * Returns: 1 if the queue is blocked - */ - -static int rq_mutex(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - - list_del_init(&gh->gh_list); - /* gh->gh_error never examined. */ - set_bit(GLF_LOCK, &gl->gl_flags); - complete(&gh->gh_wait); - - return 1; -} - -/** - * rq_promote - process a promote request in the queue - * @gh: the glock holder - * - * Acquire a new inter-node lock, or change a lock state to more restrictive. - * - * Returns: 1 if the queue is blocked - */ - -static int rq_promote(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - - if (!relaxed_state_ok(gl->gl_state, gh->gh_state, gh->gh_flags)) { - if (list_empty(&gl->gl_holders)) { - gl->gl_req_gh = gh; - set_bit(GLF_LOCK, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - - if (atomic_read(&sdp->sd_reclaim_count) > - gfs2_tune_get(sdp, gt_reclaim_limit) && - !(gh->gh_flags & LM_FLAG_PRIORITY)) { - gfs2_reclaim_glock(sdp); - gfs2_reclaim_glock(sdp); - } - - glops->go_xmote_th(gl, gh->gh_state, gh->gh_flags); - spin_lock(&gl->gl_spin); - } - return 1; - } - - if (list_empty(&gl->gl_holders)) { - set_bit(HIF_FIRST, &gh->gh_iflags); - set_bit(GLF_LOCK, &gl->gl_flags); - } else { - struct gfs2_holder *next_gh; - if (gh->gh_flags & GL_LOCAL_EXCL) - return 1; - next_gh = list_entry(gl->gl_holders.next, struct gfs2_holder, - gh_list); - if (next_gh->gh_flags & GL_LOCAL_EXCL) - return 1; - } - - list_move_tail(&gh->gh_list, &gl->gl_holders); - gh->gh_error = 0; - set_bit(HIF_HOLDER, &gh->gh_iflags); - - complete(&gh->gh_wait); - - return 0; -} - -/** - * rq_demote - process a demote request in the queue - * @gh: the glock holder - * - * Returns: 1 if the queue is blocked - */ - -static int rq_demote(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - const struct gfs2_glock_operations *glops = gl->gl_ops; - - if (!list_empty(&gl->gl_holders)) - return 1; - - if (gl->gl_state == gh->gh_state || gl->gl_state == LM_ST_UNLOCKED) { - list_del_init(&gh->gh_list); - gh->gh_error = 0; - spin_unlock(&gl->gl_spin); - if (test_bit(HIF_DEALLOC, &gh->gh_iflags)) - gfs2_holder_put(gh); - else - complete(&gh->gh_wait); - spin_lock(&gl->gl_spin); - } else { - gl->gl_req_gh = gh; - set_bit(GLF_LOCK, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - - if (gh->gh_state == LM_ST_UNLOCKED || - gl->gl_state != LM_ST_EXCLUSIVE) - glops->go_drop_th(gl); - else - glops->go_xmote_th(gl, gh->gh_state, gh->gh_flags); - - spin_lock(&gl->gl_spin); - } - - return 0; -} - -/** - * rq_greedy - process a queued request to drop greedy status - * @gh: the glock holder - * - * Returns: 1 if the queue is blocked - */ - -static int rq_greedy(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - - list_del_init(&gh->gh_list); - /* gh->gh_error never examined. */ - clear_bit(GLF_GREEDY, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - - gfs2_holder_uninit(gh); - kfree(container_of(gh, struct greedy, gr_gh)); - - spin_lock(&gl->gl_spin); - - return 0; -} - -/** - * run_queue - process holder structures on a glock - * @gl: the glock - * - */ -static void run_queue(struct gfs2_glock *gl) -{ - struct gfs2_holder *gh; - int blocked = 1; - - for (;;) { - if (test_bit(GLF_LOCK, &gl->gl_flags)) - break; - - if (!list_empty(&gl->gl_waiters1)) { - gh = list_entry(gl->gl_waiters1.next, - struct gfs2_holder, gh_list); - - if (test_bit(HIF_MUTEX, &gh->gh_iflags)) - blocked = rq_mutex(gh); - else - gfs2_assert_warn(gl->gl_sbd, 0); - - } else if (!list_empty(&gl->gl_waiters2) && - !test_bit(GLF_SKIP_WAITERS2, &gl->gl_flags)) { - gh = list_entry(gl->gl_waiters2.next, - struct gfs2_holder, gh_list); - - if (test_bit(HIF_DEMOTE, &gh->gh_iflags)) - blocked = rq_demote(gh); - else if (test_bit(HIF_GREEDY, &gh->gh_iflags)) - blocked = rq_greedy(gh); - else - gfs2_assert_warn(gl->gl_sbd, 0); - - } else if (!list_empty(&gl->gl_waiters3)) { - gh = list_entry(gl->gl_waiters3.next, - struct gfs2_holder, gh_list); - - if (test_bit(HIF_PROMOTE, &gh->gh_iflags)) - blocked = rq_promote(gh); - else - gfs2_assert_warn(gl->gl_sbd, 0); - - } else - break; - - if (blocked) - break; - } -} - -/** - * gfs2_glmutex_lock - acquire a local lock on a glock - * @gl: the glock - * - * Gives caller exclusive access to manipulate a glock structure. - */ - -static void gfs2_glmutex_lock(struct gfs2_glock *gl) -{ - struct gfs2_holder gh; - - gfs2_holder_init(gl, 0, 0, &gh); - set_bit(HIF_MUTEX, &gh.gh_iflags); - - spin_lock(&gl->gl_spin); - if (test_and_set_bit(GLF_LOCK, &gl->gl_flags)) { - list_add_tail(&gh.gh_list, &gl->gl_waiters1); - } else { - gl->gl_owner = current; - gl->gl_ip = (unsigned long)__builtin_return_address(0); - complete(&gh.gh_wait); - } - spin_unlock(&gl->gl_spin); - - wait_for_completion(&gh.gh_wait); - gfs2_holder_uninit(&gh); -} - -/** - * gfs2_glmutex_trylock - try to acquire a local lock on a glock - * @gl: the glock - * - * Returns: 1 if the glock is acquired - */ - -static int gfs2_glmutex_trylock(struct gfs2_glock *gl) -{ - int acquired = 1; - - spin_lock(&gl->gl_spin); - if (test_and_set_bit(GLF_LOCK, &gl->gl_flags)) { - acquired = 0; - } else { - gl->gl_owner = current; - gl->gl_ip = (unsigned long)__builtin_return_address(0); - } - spin_unlock(&gl->gl_spin); - - return acquired; -} - -/** - * gfs2_glmutex_unlock - release a local lock on a glock - * @gl: the glock - * - */ - -static void gfs2_glmutex_unlock(struct gfs2_glock *gl) -{ - spin_lock(&gl->gl_spin); - clear_bit(GLF_LOCK, &gl->gl_flags); - gl->gl_owner = NULL; - gl->gl_ip = 0; - run_queue(gl); - BUG_ON(!spin_is_locked(&gl->gl_spin)); - spin_unlock(&gl->gl_spin); -} - -/** - * handle_callback - add a demote request to a lock's queue - * @gl: the glock - * @state: the state the caller wants us to change to - * - * Note: This may fail sliently if we are out of memory. - */ - -static void handle_callback(struct gfs2_glock *gl, unsigned int state) -{ - struct gfs2_holder *gh, *new_gh = NULL; - -restart: - spin_lock(&gl->gl_spin); - - list_for_each_entry(gh, &gl->gl_waiters2, gh_list) { - if (test_bit(HIF_DEMOTE, &gh->gh_iflags) && - gl->gl_req_gh != gh) { - if (gh->gh_state != state) - gh->gh_state = LM_ST_UNLOCKED; - goto out; - } - } - - if (new_gh) { - list_add_tail(&new_gh->gh_list, &gl->gl_waiters2); - new_gh = NULL; - } else { - spin_unlock(&gl->gl_spin); - - new_gh = gfs2_holder_get(gl, state, LM_FLAG_TRY, GFP_KERNEL); - if (!new_gh) - return; - set_bit(HIF_DEMOTE, &new_gh->gh_iflags); - set_bit(HIF_DEALLOC, &new_gh->gh_iflags); - - goto restart; - } - -out: - spin_unlock(&gl->gl_spin); - - if (new_gh) - gfs2_holder_put(new_gh); -} - -void gfs2_glock_inode_squish(struct inode *inode) -{ - struct gfs2_holder gh; - struct gfs2_glock *gl = GFS2_I(inode)->i_gl; - gfs2_holder_init(gl, LM_ST_UNLOCKED, 0, &gh); - set_bit(HIF_DEMOTE, &gh.gh_iflags); - spin_lock(&gl->gl_spin); - gfs2_assert(inode->i_sb->s_fs_info, list_empty(&gl->gl_holders)); - list_add_tail(&gh.gh_list, &gl->gl_waiters2); - run_queue(gl); - spin_unlock(&gl->gl_spin); - wait_for_completion(&gh.gh_wait); - gfs2_holder_uninit(&gh); -} - -/** - * state_change - record that the glock is now in a different state - * @gl: the glock - * @new_state the new state - * - */ - -static void state_change(struct gfs2_glock *gl, unsigned int new_state) -{ - int held1, held2; - - held1 = (gl->gl_state != LM_ST_UNLOCKED); - held2 = (new_state != LM_ST_UNLOCKED); - - if (held1 != held2) { - if (held2) - gfs2_glock_hold(gl); - else - gfs2_glock_put(gl); - } - - gl->gl_state = new_state; -} - -/** - * xmote_bh - Called after the lock module is done acquiring a lock - * @gl: The glock in question - * @ret: the int returned from the lock module - * - */ - -static void xmote_bh(struct gfs2_glock *gl, unsigned int ret) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - struct gfs2_holder *gh = gl->gl_req_gh; - int prev_state = gl->gl_state; - int op_done = 1; - - gfs2_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags)); - gfs2_assert_warn(sdp, queue_empty(gl, &gl->gl_holders)); - gfs2_assert_warn(sdp, !(ret & LM_OUT_ASYNC)); - - state_change(gl, ret & LM_OUT_ST_MASK); - - if (prev_state != LM_ST_UNLOCKED && !(ret & LM_OUT_CACHEABLE)) { - if (glops->go_inval) - glops->go_inval(gl, DIO_METADATA | DIO_DATA); - } else if (gl->gl_state == LM_ST_DEFERRED) { - /* We might not want to do this here. - Look at moving to the inode glops. */ - if (glops->go_inval) - glops->go_inval(gl, DIO_DATA); - } - - /* Deal with each possible exit condition */ - - if (!gh) - gl->gl_stamp = jiffies; - else if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) { - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - gh->gh_error = -EIO; - spin_unlock(&gl->gl_spin); - } else if (test_bit(HIF_DEMOTE, &gh->gh_iflags)) { - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - if (gl->gl_state == gh->gh_state || - gl->gl_state == LM_ST_UNLOCKED) { - gh->gh_error = 0; - } else { - if (gfs2_assert_warn(sdp, gh->gh_flags & - (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) == -1) - fs_warn(sdp, "ret = 0x%.8X\n", ret); - gh->gh_error = GLR_TRYFAILED; - } - spin_unlock(&gl->gl_spin); - - if (ret & LM_OUT_CANCELED) - handle_callback(gl, LM_ST_UNLOCKED); - - } else if (ret & LM_OUT_CANCELED) { - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - gh->gh_error = GLR_CANCELED; - spin_unlock(&gl->gl_spin); - - } else if (relaxed_state_ok(gl->gl_state, gh->gh_state, gh->gh_flags)) { - spin_lock(&gl->gl_spin); - list_move_tail(&gh->gh_list, &gl->gl_holders); - gh->gh_error = 0; - set_bit(HIF_HOLDER, &gh->gh_iflags); - spin_unlock(&gl->gl_spin); - - set_bit(HIF_FIRST, &gh->gh_iflags); - - op_done = 0; - - } else if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) { - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - gh->gh_error = GLR_TRYFAILED; - spin_unlock(&gl->gl_spin); - - } else { - if (gfs2_assert_withdraw(sdp, 0) == -1) - fs_err(sdp, "ret = 0x%.8X\n", ret); - } - - if (glops->go_xmote_bh) - glops->go_xmote_bh(gl); - - if (op_done) { - spin_lock(&gl->gl_spin); - gl->gl_req_gh = NULL; - gl->gl_req_bh = NULL; - clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); - spin_unlock(&gl->gl_spin); - } - - gfs2_glock_put(gl); - - if (gh) { - if (test_bit(HIF_DEALLOC, &gh->gh_iflags)) - gfs2_holder_put(gh); - else - complete(&gh->gh_wait); - } -} - -/** - * gfs2_glock_xmote_th - Call into the lock module to acquire or change a glock - * @gl: The glock in question - * @state: the requested state - * @flags: modifier flags to the lock call - * - */ - -void gfs2_glock_xmote_th(struct gfs2_glock *gl, unsigned int state, int flags) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - int lck_flags = flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB | - LM_FLAG_NOEXP | LM_FLAG_ANY | - LM_FLAG_PRIORITY); - unsigned int lck_ret; - - gfs2_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags)); - gfs2_assert_warn(sdp, queue_empty(gl, &gl->gl_holders)); - gfs2_assert_warn(sdp, state != LM_ST_UNLOCKED); - gfs2_assert_warn(sdp, state != gl->gl_state); - - if (gl->gl_state == LM_ST_EXCLUSIVE && glops->go_sync) - glops->go_sync(gl, DIO_METADATA | DIO_DATA | DIO_RELEASE); - - gfs2_glock_hold(gl); - gl->gl_req_bh = xmote_bh; - - lck_ret = gfs2_lm_lock(sdp, gl->gl_lock, gl->gl_state, state, lck_flags); - - if (gfs2_assert_withdraw(sdp, !(lck_ret & LM_OUT_ERROR))) - return; - - if (lck_ret & LM_OUT_ASYNC) - gfs2_assert_warn(sdp, lck_ret == LM_OUT_ASYNC); - else - xmote_bh(gl, lck_ret); -} - -/** - * drop_bh - Called after a lock module unlock completes - * @gl: the glock - * @ret: the return status - * - * Doesn't wake up the process waiting on the struct gfs2_holder (if any) - * Doesn't drop the reference on the glock the top half took out - * - */ - -static void drop_bh(struct gfs2_glock *gl, unsigned int ret) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - struct gfs2_holder *gh = gl->gl_req_gh; - - clear_bit(GLF_PREFETCH, &gl->gl_flags); - - gfs2_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags)); - gfs2_assert_warn(sdp, queue_empty(gl, &gl->gl_holders)); - gfs2_assert_warn(sdp, !ret); - - state_change(gl, LM_ST_UNLOCKED); - - if (glops->go_inval) - glops->go_inval(gl, DIO_METADATA | DIO_DATA); - - if (gh) { - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - gh->gh_error = 0; - spin_unlock(&gl->gl_spin); - } - - if (glops->go_drop_bh) - glops->go_drop_bh(gl); - - spin_lock(&gl->gl_spin); - gl->gl_req_gh = NULL; - gl->gl_req_bh = NULL; - clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); - spin_unlock(&gl->gl_spin); - - gfs2_glock_put(gl); - - if (gh) { - if (test_bit(HIF_DEALLOC, &gh->gh_iflags)) - gfs2_holder_put(gh); - else - complete(&gh->gh_wait); - } -} - -/** - * gfs2_glock_drop_th - call into the lock module to unlock a lock - * @gl: the glock - * - */ - -void gfs2_glock_drop_th(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - unsigned int ret; - - gfs2_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags)); - gfs2_assert_warn(sdp, queue_empty(gl, &gl->gl_holders)); - gfs2_assert_warn(sdp, gl->gl_state != LM_ST_UNLOCKED); - - if (gl->gl_state == LM_ST_EXCLUSIVE && glops->go_sync) - glops->go_sync(gl, DIO_METADATA | DIO_DATA | DIO_RELEASE); - - gfs2_glock_hold(gl); - gl->gl_req_bh = drop_bh; - - ret = gfs2_lm_unlock(sdp, gl->gl_lock, gl->gl_state); - - if (gfs2_assert_withdraw(sdp, !(ret & LM_OUT_ERROR))) - return; - - if (!ret) - drop_bh(gl, ret); - else - gfs2_assert_warn(sdp, ret == LM_OUT_ASYNC); -} - -/** - * do_cancels - cancel requests for locks stuck waiting on an expire flag - * @gh: the LM_FLAG_PRIORITY holder waiting to acquire the lock - * - * Don't cancel GL_NOCANCEL requests. - */ - -static void do_cancels(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - - spin_lock(&gl->gl_spin); - - while (gl->gl_req_gh != gh && - !test_bit(HIF_HOLDER, &gh->gh_iflags) && - !list_empty(&gh->gh_list)) { - if (gl->gl_req_bh && !(gl->gl_req_gh && - (gl->gl_req_gh->gh_flags & GL_NOCANCEL))) { - spin_unlock(&gl->gl_spin); - gfs2_lm_cancel(gl->gl_sbd, gl->gl_lock); - msleep(100); - spin_lock(&gl->gl_spin); - } else { - spin_unlock(&gl->gl_spin); - msleep(100); - spin_lock(&gl->gl_spin); - } - } - - spin_unlock(&gl->gl_spin); -} - -/** - * glock_wait_internal - wait on a glock acquisition - * @gh: the glock holder - * - * Returns: 0 on success - */ - -static int glock_wait_internal(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - - if (test_bit(HIF_ABORTED, &gh->gh_iflags)) - return -EIO; - - if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) { - spin_lock(&gl->gl_spin); - if (gl->gl_req_gh != gh && - !test_bit(HIF_HOLDER, &gh->gh_iflags) && - !list_empty(&gh->gh_list)) { - list_del_init(&gh->gh_list); - gh->gh_error = GLR_TRYFAILED; - run_queue(gl); - spin_unlock(&gl->gl_spin); - return gh->gh_error; - } - spin_unlock(&gl->gl_spin); - } - - if (gh->gh_flags & LM_FLAG_PRIORITY) - do_cancels(gh); - - wait_for_completion(&gh->gh_wait); - - if (gh->gh_error) - return gh->gh_error; - - gfs2_assert_withdraw(sdp, test_bit(HIF_HOLDER, &gh->gh_iflags)); - gfs2_assert_withdraw(sdp, relaxed_state_ok(gl->gl_state, gh->gh_state, - gh->gh_flags)); - - if (test_bit(HIF_FIRST, &gh->gh_iflags)) { - gfs2_assert_warn(sdp, test_bit(GLF_LOCK, &gl->gl_flags)); - - if (glops->go_lock) { - gh->gh_error = glops->go_lock(gh); - if (gh->gh_error) { - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - spin_unlock(&gl->gl_spin); - } - } - - spin_lock(&gl->gl_spin); - gl->gl_req_gh = NULL; - gl->gl_req_bh = NULL; - clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); - spin_unlock(&gl->gl_spin); - } - - return gh->gh_error; -} - -static inline struct gfs2_holder * -find_holder_by_owner(struct list_head *head, struct task_struct *owner) -{ - struct gfs2_holder *gh; - - list_for_each_entry(gh, head, gh_list) { - if (gh->gh_owner == owner) - return gh; - } - - return NULL; -} - -/** - * add_to_queue - Add a holder to the wait queue (but look for recursion) - * @gh: the holder structure to add - * - */ - -static void add_to_queue(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_holder *existing; - - BUG_ON(!gh->gh_owner); - - existing = find_holder_by_owner(&gl->gl_holders, gh->gh_owner); - if (existing) { - print_symbol(KERN_WARNING "original: %s\n", existing->gh_ip); - printk(KERN_INFO "pid : %d\n", existing->gh_owner->pid); - printk(KERN_INFO "lock type : %d lock state : %d\n", - existing->gh_gl->gl_name.ln_type, existing->gh_gl->gl_state); - print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); - printk(KERN_INFO "pid : %d\n", gh->gh_owner->pid); - printk(KERN_INFO "lock type : %d lock state : %d\n", - gl->gl_name.ln_type, gl->gl_state); - BUG(); - } - - existing = find_holder_by_owner(&gl->gl_waiters3, gh->gh_owner); - if (existing) { - print_symbol(KERN_WARNING "original: %s\n", existing->gh_ip); - print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); - BUG(); - } - - if (gh->gh_flags & LM_FLAG_PRIORITY) - list_add(&gh->gh_list, &gl->gl_waiters3); - else - list_add_tail(&gh->gh_list, &gl->gl_waiters3); -} - -/** - * gfs2_glock_nq - enqueue a struct gfs2_holder onto a glock (acquire a glock) - * @gh: the holder structure - * - * if (gh->gh_flags & GL_ASYNC), this never returns an error - * - * Returns: 0, GLR_TRYFAILED, or errno on failure - */ - -int gfs2_glock_nq(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_sbd *sdp = gl->gl_sbd; - int error = 0; - -restart: - if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) { - set_bit(HIF_ABORTED, &gh->gh_iflags); - return -EIO; - } - - set_bit(HIF_PROMOTE, &gh->gh_iflags); - - spin_lock(&gl->gl_spin); - add_to_queue(gh); - run_queue(gl); - spin_unlock(&gl->gl_spin); - - if (!(gh->gh_flags & GL_ASYNC)) { - error = glock_wait_internal(gh); - if (error == GLR_CANCELED) { - msleep(100); - goto restart; - } - } - - clear_bit(GLF_PREFETCH, &gl->gl_flags); - - if (error == GLR_TRYFAILED && (gh->gh_flags & GL_DUMP)) - dump_glock(gl); - - return error; -} - -/** - * gfs2_glock_poll - poll to see if an async request has been completed - * @gh: the holder - * - * Returns: 1 if the request is ready to be gfs2_glock_wait()ed on - */ - -int gfs2_glock_poll(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - int ready = 0; - - spin_lock(&gl->gl_spin); - - if (test_bit(HIF_HOLDER, &gh->gh_iflags)) - ready = 1; - else if (list_empty(&gh->gh_list)) { - if (gh->gh_error == GLR_CANCELED) { - spin_unlock(&gl->gl_spin); - msleep(100); - if (gfs2_glock_nq(gh)) - return 1; - return 0; - } else - ready = 1; - } - - spin_unlock(&gl->gl_spin); - - return ready; -} - -/** - * gfs2_glock_wait - wait for a lock acquisition that ended in a GLR_ASYNC - * @gh: the holder structure - * - * Returns: 0, GLR_TRYFAILED, or errno on failure - */ - -int gfs2_glock_wait(struct gfs2_holder *gh) -{ - int error; - - error = glock_wait_internal(gh); - if (error == GLR_CANCELED) { - msleep(100); - gh->gh_flags &= ~GL_ASYNC; - error = gfs2_glock_nq(gh); - } - - return error; -} - -/** - * gfs2_glock_dq - dequeue a struct gfs2_holder from a glock (release a glock) - * @gh: the glock holder - * - */ - -void gfs2_glock_dq(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - const struct gfs2_glock_operations *glops = gl->gl_ops; - - if (gh->gh_flags & GL_NOCACHE) - handle_callback(gl, LM_ST_UNLOCKED); - - gfs2_glmutex_lock(gl); - - spin_lock(&gl->gl_spin); - list_del_init(&gh->gh_list); - - if (list_empty(&gl->gl_holders)) { - spin_unlock(&gl->gl_spin); - - if (glops->go_unlock) - glops->go_unlock(gh); - - gl->gl_stamp = jiffies; - - spin_lock(&gl->gl_spin); - } - - clear_bit(GLF_LOCK, &gl->gl_flags); - run_queue(gl); - spin_unlock(&gl->gl_spin); -} - -/** - * gfs2_glock_prefetch - Try to prefetch a glock - * @gl: the glock - * @state: the state to prefetch in - * @flags: flags passed to go_xmote_th() - * - */ - -static void gfs2_glock_prefetch(struct gfs2_glock *gl, unsigned int state, - int flags) -{ - const struct gfs2_glock_operations *glops = gl->gl_ops; - - spin_lock(&gl->gl_spin); - - if (test_bit(GLF_LOCK, &gl->gl_flags) || !list_empty(&gl->gl_holders) || - !list_empty(&gl->gl_waiters1) || !list_empty(&gl->gl_waiters2) || - !list_empty(&gl->gl_waiters3) || - relaxed_state_ok(gl->gl_state, state, flags)) { - spin_unlock(&gl->gl_spin); - return; - } - - set_bit(GLF_PREFETCH, &gl->gl_flags); - set_bit(GLF_LOCK, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - - glops->go_xmote_th(gl, state, flags); -} - -static void greedy_work(void *data) -{ - struct greedy *gr = data; - struct gfs2_holder *gh = &gr->gr_gh; - struct gfs2_glock *gl = gh->gh_gl; - const struct gfs2_glock_operations *glops = gl->gl_ops; - - clear_bit(GLF_SKIP_WAITERS2, &gl->gl_flags); - - if (glops->go_greedy) - glops->go_greedy(gl); - - spin_lock(&gl->gl_spin); - - if (list_empty(&gl->gl_waiters2)) { - clear_bit(GLF_GREEDY, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - gfs2_holder_uninit(gh); - kfree(gr); - } else { - gfs2_glock_hold(gl); - list_add_tail(&gh->gh_list, &gl->gl_waiters2); - run_queue(gl); - spin_unlock(&gl->gl_spin); - gfs2_glock_put(gl); - } -} - -/** - * gfs2_glock_be_greedy - - * @gl: - * @time: - * - * Returns: 0 if go_greedy will be called, 1 otherwise - */ - -int gfs2_glock_be_greedy(struct gfs2_glock *gl, unsigned int time) -{ - struct greedy *gr; - struct gfs2_holder *gh; - - if (!time || gl->gl_sbd->sd_args.ar_localcaching || - test_and_set_bit(GLF_GREEDY, &gl->gl_flags)) - return 1; - - gr = kmalloc(sizeof(struct greedy), GFP_KERNEL); - if (!gr) { - clear_bit(GLF_GREEDY, &gl->gl_flags); - return 1; - } - gh = &gr->gr_gh; - - gfs2_holder_init(gl, 0, 0, gh); - set_bit(HIF_GREEDY, &gh->gh_iflags); - INIT_WORK(&gr->gr_work, greedy_work, gr); - - set_bit(GLF_SKIP_WAITERS2, &gl->gl_flags); - schedule_delayed_work(&gr->gr_work, time); - - return 0; -} - -/** - * gfs2_glock_dq_uninit - dequeue a holder from a glock and initialize it - * @gh: the holder structure - * - */ - -void gfs2_glock_dq_uninit(struct gfs2_holder *gh) -{ - gfs2_glock_dq(gh); - gfs2_holder_uninit(gh); -} - -/** - * gfs2_glock_nq_num - acquire a glock based on lock number - * @sdp: the filesystem - * @number: the lock number - * @glops: the glock operations for the type of glock - * @state: the state to acquire the glock in - * @flags: modifier flags for the aquisition - * @gh: the struct gfs2_holder - * - * Returns: errno - */ - -int gfs2_glock_nq_num(struct gfs2_sbd *sdp, u64 number, - const struct gfs2_glock_operations *glops, - unsigned int state, int flags, struct gfs2_holder *gh) -{ - struct gfs2_glock *gl; - int error; - - error = gfs2_glock_get(sdp, number, glops, CREATE, &gl); - if (!error) { - error = gfs2_glock_nq_init(gl, state, flags, gh); - gfs2_glock_put(gl); - } - - return error; -} - -/** - * glock_compare - Compare two struct gfs2_glock structures for sorting - * @arg_a: the first structure - * @arg_b: the second structure - * - */ - -static int glock_compare(const void *arg_a, const void *arg_b) -{ - const struct gfs2_holder *gh_a = *(const struct gfs2_holder **)arg_a; - const struct gfs2_holder *gh_b = *(const struct gfs2_holder **)arg_b; - const struct lm_lockname *a = &gh_a->gh_gl->gl_name; - const struct lm_lockname *b = &gh_b->gh_gl->gl_name; - - if (a->ln_number > b->ln_number) - return 1; - if (a->ln_number < b->ln_number) - return -1; - if (gh_a->gh_state == LM_ST_SHARED && gh_b->gh_state == LM_ST_EXCLUSIVE) - return 1; - if (!(gh_a->gh_flags & GL_LOCAL_EXCL) && (gh_b->gh_flags & GL_LOCAL_EXCL)) - return 1; - return 0; -} - -/** - * nq_m_sync - synchonously acquire more than one glock in deadlock free order - * @num_gh: the number of structures - * @ghs: an array of struct gfs2_holder structures - * - * Returns: 0 on success (all glocks acquired), - * errno on failure (no glocks acquired) - */ - -static int nq_m_sync(unsigned int num_gh, struct gfs2_holder *ghs, - struct gfs2_holder **p) -{ - unsigned int x; - int error = 0; - - for (x = 0; x < num_gh; x++) - p[x] = &ghs[x]; - - sort(p, num_gh, sizeof(struct gfs2_holder *), glock_compare, NULL); - - for (x = 0; x < num_gh; x++) { - p[x]->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC); - - error = gfs2_glock_nq(p[x]); - if (error) { - while (x--) - gfs2_glock_dq(p[x]); - break; - } - } - - return error; -} - -/** - * gfs2_glock_nq_m - acquire multiple glocks - * @num_gh: the number of structures - * @ghs: an array of struct gfs2_holder structures - * - * Figure out how big an impact this function has. Either: - * 1) Replace this code with code that calls gfs2_glock_prefetch() - * 2) Forget async stuff and just call nq_m_sync() - * 3) Leave it like it is - * - * Returns: 0 on success (all glocks acquired), - * errno on failure (no glocks acquired) - */ - -int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs) -{ - int *e; - unsigned int x; - int borked = 0, serious = 0; - int error = 0; - - if (!num_gh) - return 0; - - if (num_gh == 1) { - ghs->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC); - return gfs2_glock_nq(ghs); - } - - e = kcalloc(num_gh, sizeof(struct gfs2_holder *), GFP_KERNEL); - if (!e) - return -ENOMEM; - - for (x = 0; x < num_gh; x++) { - ghs[x].gh_flags |= LM_FLAG_TRY | GL_ASYNC; - error = gfs2_glock_nq(&ghs[x]); - if (error) { - borked = 1; - serious = error; - num_gh = x; - break; - } - } - - for (x = 0; x < num_gh; x++) { - error = e[x] = glock_wait_internal(&ghs[x]); - if (error) { - borked = 1; - if (error != GLR_TRYFAILED && error != GLR_CANCELED) - serious = error; - } - } - - if (!borked) { - kfree(e); - return 0; - } - - for (x = 0; x < num_gh; x++) - if (!e[x]) - gfs2_glock_dq(&ghs[x]); - - if (serious) - error = serious; - else { - for (x = 0; x < num_gh; x++) - gfs2_holder_reinit(ghs[x].gh_state, ghs[x].gh_flags, - &ghs[x]); - error = nq_m_sync(num_gh, ghs, (struct gfs2_holder **)e); - } - - kfree(e); - - return error; -} - -/** - * gfs2_glock_dq_m - release multiple glocks - * @num_gh: the number of structures - * @ghs: an array of struct gfs2_holder structures - * - */ - -void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs) -{ - unsigned int x; - - for (x = 0; x < num_gh; x++) - gfs2_glock_dq(&ghs[x]); -} - -/** - * gfs2_glock_dq_uninit_m - release multiple glocks - * @num_gh: the number of structures - * @ghs: an array of struct gfs2_holder structures - * - */ - -void gfs2_glock_dq_uninit_m(unsigned int num_gh, struct gfs2_holder *ghs) -{ - unsigned int x; - - for (x = 0; x < num_gh; x++) - gfs2_glock_dq_uninit(&ghs[x]); -} - -/** - * gfs2_glock_prefetch_num - prefetch a glock based on lock number - * @sdp: the filesystem - * @number: the lock number - * @glops: the glock operations for the type of glock - * @state: the state to acquire the glock in - * @flags: modifier flags for the aquisition - * - * Returns: errno - */ - -void gfs2_glock_prefetch_num(struct gfs2_sbd *sdp, u64 number, - const struct gfs2_glock_operations *glops, - unsigned int state, int flags) -{ - struct gfs2_glock *gl; - int error; - - if (atomic_read(&sdp->sd_reclaim_count) < - gfs2_tune_get(sdp, gt_reclaim_limit)) { - error = gfs2_glock_get(sdp, number, glops, CREATE, &gl); - if (!error) { - gfs2_glock_prefetch(gl, state, flags); - gfs2_glock_put(gl); - } - } -} - -/** - * gfs2_lvb_hold - attach a LVB from a glock - * @gl: The glock in question - * - */ - -int gfs2_lvb_hold(struct gfs2_glock *gl) -{ - int error; - - gfs2_glmutex_lock(gl); - - if (!atomic_read(&gl->gl_lvb_count)) { - error = gfs2_lm_hold_lvb(gl->gl_sbd, gl->gl_lock, &gl->gl_lvb); - if (error) { - gfs2_glmutex_unlock(gl); - return error; - } - gfs2_glock_hold(gl); - } - atomic_inc(&gl->gl_lvb_count); - - gfs2_glmutex_unlock(gl); - - return 0; -} - -/** - * gfs2_lvb_unhold - detach a LVB from a glock - * @gl: The glock in question - * - */ - -void gfs2_lvb_unhold(struct gfs2_glock *gl) -{ - gfs2_glock_hold(gl); - gfs2_glmutex_lock(gl); - - gfs2_assert(gl->gl_sbd, atomic_read(&gl->gl_lvb_count) > 0); - if (atomic_dec_and_test(&gl->gl_lvb_count)) { - gfs2_lm_unhold_lvb(gl->gl_sbd, gl->gl_lock, gl->gl_lvb); - gl->gl_lvb = NULL; - gfs2_glock_put(gl); - } - - gfs2_glmutex_unlock(gl); - gfs2_glock_put(gl); -} - -static void blocking_cb(struct gfs2_sbd *sdp, struct lm_lockname *name, - unsigned int state) -{ - struct gfs2_glock *gl; - - gl = gfs2_glock_find(sdp, name); - if (!gl) - return; - - if (gl->gl_ops->go_callback) - gl->gl_ops->go_callback(gl, state); - handle_callback(gl, state); - - spin_lock(&gl->gl_spin); - run_queue(gl); - spin_unlock(&gl->gl_spin); - - gfs2_glock_put(gl); -} - -/** - * gfs2_glock_cb - Callback used by locking module - * @sdp: Pointer to the superblock - * @type: Type of callback - * @data: Type dependent data pointer - * - * Called by the locking module when it wants to tell us something. - * Either we need to drop a lock, one of our ASYNC requests completed, or - * a journal from another client needs to be recovered. - */ - -void gfs2_glock_cb(void *cb_data, unsigned int type, void *data) -{ - struct gfs2_sbd *sdp = cb_data; - - switch (type) { - case LM_CB_NEED_E: - blocking_cb(sdp, data, LM_ST_UNLOCKED); - return; - - case LM_CB_NEED_D: - blocking_cb(sdp, data, LM_ST_DEFERRED); - return; - - case LM_CB_NEED_S: - blocking_cb(sdp, data, LM_ST_SHARED); - return; - - case LM_CB_ASYNC: { - struct lm_async_cb *async = data; - struct gfs2_glock *gl; - - gl = gfs2_glock_find(sdp, &async->lc_name); - if (gfs2_assert_warn(sdp, gl)) - return; - if (!gfs2_assert_warn(sdp, gl->gl_req_bh)) - gl->gl_req_bh(gl, async->lc_ret); - gfs2_glock_put(gl); - return; - } - - case LM_CB_NEED_RECOVERY: - gfs2_jdesc_make_dirty(sdp, *(unsigned int *)data); - if (sdp->sd_recoverd_process) - wake_up_process(sdp->sd_recoverd_process); - return; - - case LM_CB_DROPLOCKS: - gfs2_gl_hash_clear(sdp, NO_WAIT); - gfs2_quota_scan(sdp); - return; - - default: - gfs2_assert_warn(sdp, 0); - return; - } -} - -/** - * demote_ok - Check to see if it's ok to unlock a glock - * @gl: the glock - * - * Returns: 1 if it's ok - */ - -static int demote_ok(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - const struct gfs2_glock_operations *glops = gl->gl_ops; - int demote = 1; - - if (test_bit(GLF_STICKY, &gl->gl_flags)) - demote = 0; - else if (test_bit(GLF_PREFETCH, &gl->gl_flags)) - demote = time_after_eq(jiffies, gl->gl_stamp + - gfs2_tune_get(sdp, gt_prefetch_secs) * HZ); - else if (glops->go_demote_ok) - demote = glops->go_demote_ok(gl); - - return demote; -} - -/** - * gfs2_glock_schedule_for_reclaim - Add a glock to the reclaim list - * @gl: the glock - * - */ - -void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - - spin_lock(&sdp->sd_reclaim_lock); - if (list_empty(&gl->gl_reclaim)) { - gfs2_glock_hold(gl); - list_add(&gl->gl_reclaim, &sdp->sd_reclaim_list); - atomic_inc(&sdp->sd_reclaim_count); - } - spin_unlock(&sdp->sd_reclaim_lock); - - wake_up(&sdp->sd_reclaim_wq); -} - -/** - * gfs2_reclaim_glock - process the next glock on the filesystem's reclaim list - * @sdp: the filesystem - * - * Called from gfs2_glockd() glock reclaim daemon, or when promoting a - * different glock and we notice that there are a lot of glocks in the - * reclaim list. - * - */ - -void gfs2_reclaim_glock(struct gfs2_sbd *sdp) -{ - struct gfs2_glock *gl; - - spin_lock(&sdp->sd_reclaim_lock); - if (list_empty(&sdp->sd_reclaim_list)) { - spin_unlock(&sdp->sd_reclaim_lock); - return; - } - gl = list_entry(sdp->sd_reclaim_list.next, - struct gfs2_glock, gl_reclaim); - list_del_init(&gl->gl_reclaim); - spin_unlock(&sdp->sd_reclaim_lock); - - atomic_dec(&sdp->sd_reclaim_count); - atomic_inc(&sdp->sd_reclaimed); - - if (gfs2_glmutex_trylock(gl)) { - if (queue_empty(gl, &gl->gl_holders) && - gl->gl_state != LM_ST_UNLOCKED && demote_ok(gl)) - handle_callback(gl, LM_ST_UNLOCKED); - gfs2_glmutex_unlock(gl); - } - - gfs2_glock_put(gl); -} - -/** - * examine_bucket - Call a function for glock in a hash bucket - * @examiner: the function - * @sdp: the filesystem - * @bucket: the bucket - * - * Returns: 1 if the bucket has entries - */ - -static int examine_bucket(glock_examiner examiner, struct gfs2_sbd *sdp, - unsigned int hash) -{ - struct gfs2_glock *gl, *prev = NULL; - int has_entries = 0; - struct hlist_head *head = &gl_hash_table[hash].hb_list; - - read_lock(gl_lock_addr(hash)); - /* Can't use hlist_for_each_entry - don't want prefetch here */ - if (hlist_empty(head)) - goto out; - gl = list_entry(head->first, struct gfs2_glock, gl_list); - while(1) { - if (gl->gl_sbd == sdp) { - gfs2_glock_hold(gl); - read_unlock(gl_lock_addr(hash)); - if (prev) - gfs2_glock_put(prev); - prev = gl; - examiner(gl); - has_entries = 1; - read_lock(gl_lock_addr(hash)); - } - if (gl->gl_list.next == NULL) - break; - gl = list_entry(gl->gl_list.next, struct gfs2_glock, gl_list); - } -out: - read_unlock(gl_lock_addr(hash)); - if (prev) - gfs2_glock_put(prev); - return has_entries; -} - -/** - * scan_glock - look at a glock and see if we can reclaim it - * @gl: the glock to look at - * - */ - -static void scan_glock(struct gfs2_glock *gl) -{ - if (gl->gl_ops == &gfs2_inode_glops) - return; - - if (gfs2_glmutex_trylock(gl)) { - if (queue_empty(gl, &gl->gl_holders) && - gl->gl_state != LM_ST_UNLOCKED && demote_ok(gl)) - goto out_schedule; - gfs2_glmutex_unlock(gl); - } - return; - -out_schedule: - gfs2_glmutex_unlock(gl); - gfs2_glock_schedule_for_reclaim(gl); -} - -/** - * gfs2_scand_internal - Look for glocks and inodes to toss from memory - * @sdp: the filesystem - * - */ - -void gfs2_scand_internal(struct gfs2_sbd *sdp) -{ - unsigned int x; - - for (x = 0; x < GFS2_GL_HASH_SIZE; x++) - examine_bucket(scan_glock, sdp, x); -} - -/** - * clear_glock - look at a glock and see if we can free it from glock cache - * @gl: the glock to look at - * - */ - -static void clear_glock(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - int released; - - spin_lock(&sdp->sd_reclaim_lock); - if (!list_empty(&gl->gl_reclaim)) { - list_del_init(&gl->gl_reclaim); - atomic_dec(&sdp->sd_reclaim_count); - spin_unlock(&sdp->sd_reclaim_lock); - released = gfs2_glock_put(gl); - gfs2_assert(sdp, !released); - } else { - spin_unlock(&sdp->sd_reclaim_lock); - } - - if (gfs2_glmutex_trylock(gl)) { - if (queue_empty(gl, &gl->gl_holders) && - gl->gl_state != LM_ST_UNLOCKED) - handle_callback(gl, LM_ST_UNLOCKED); - gfs2_glmutex_unlock(gl); - } -} - -/** - * gfs2_gl_hash_clear - Empty out the glock hash table - * @sdp: the filesystem - * @wait: wait until it's all gone - * - * Called when unmounting the filesystem, or when inter-node lock manager - * requests DROPLOCKS because it is running out of capacity. - */ - -void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait) -{ - unsigned long t; - unsigned int x; - int cont; - - t = jiffies; - - for (;;) { - cont = 0; - for (x = 0; x < GFS2_GL_HASH_SIZE; x++) { - if (examine_bucket(clear_glock, sdp, x)) - cont = 1; - } - - if (!wait || !cont) - break; - - if (time_after_eq(jiffies, - t + gfs2_tune_get(sdp, gt_stall_secs) * HZ)) { - fs_warn(sdp, "Unmount seems to be stalled. " - "Dumping lock state...\n"); - gfs2_dump_lockstate(sdp); - t = jiffies; - } - - invalidate_inodes(sdp->sd_vfs); - msleep(10); - } -} - -/* - * Diagnostic routines to help debug distributed deadlock - */ - -/** - * dump_holder - print information about a glock holder - * @str: a string naming the type of holder - * @gh: the glock holder - * - * Returns: 0 on success, -ENOBUFS when we run out of space - */ - -static int dump_holder(char *str, struct gfs2_holder *gh) -{ - unsigned int x; - int error = -ENOBUFS; - - printk(KERN_INFO " %s\n", str); - printk(KERN_INFO " owner = %ld\n", - (gh->gh_owner) ? (long)gh->gh_owner->pid : -1); - printk(KERN_INFO " gh_state = %u\n", gh->gh_state); - printk(KERN_INFO " gh_flags ="); - for (x = 0; x < 32; x++) - if (gh->gh_flags & (1 << x)) - printk(" %u", x); - printk(" \n"); - printk(KERN_INFO " error = %d\n", gh->gh_error); - printk(KERN_INFO " gh_iflags ="); - for (x = 0; x < 32; x++) - if (test_bit(x, &gh->gh_iflags)) - printk(" %u", x); - printk(" \n"); - print_symbol(KERN_INFO " initialized at: %s\n", gh->gh_ip); - - error = 0; - - return error; -} - -/** - * dump_inode - print information about an inode - * @ip: the inode - * - * Returns: 0 on success, -ENOBUFS when we run out of space - */ - -static int dump_inode(struct gfs2_inode *ip) -{ - unsigned int x; - int error = -ENOBUFS; - - printk(KERN_INFO " Inode:\n"); - printk(KERN_INFO " num = %llu %llu\n", - (unsigned long long)ip->i_num.no_formal_ino, - (unsigned long long)ip->i_num.no_addr); - printk(KERN_INFO " type = %u\n", IF2DT(ip->i_di.di_mode)); - printk(KERN_INFO " i_flags ="); - for (x = 0; x < 32; x++) - if (test_bit(x, &ip->i_flags)) - printk(" %u", x); - printk(" \n"); - - error = 0; - - return error; -} - -/** - * dump_glock - print information about a glock - * @gl: the glock - * @count: where we are in the buffer - * - * Returns: 0 on success, -ENOBUFS when we run out of space - */ - -static int dump_glock(struct gfs2_glock *gl) -{ - struct gfs2_holder *gh; - unsigned int x; - int error = -ENOBUFS; - - spin_lock(&gl->gl_spin); - - printk(KERN_INFO "Glock 0x%p (%u, %llu)\n", gl, gl->gl_name.ln_type, - (unsigned long long)gl->gl_name.ln_number); - printk(KERN_INFO " gl_flags ="); - for (x = 0; x < 32; x++) { - if (test_bit(x, &gl->gl_flags)) - printk(" %u", x); - } - printk(" \n"); - printk(KERN_INFO " gl_ref = %d\n", atomic_read(&gl->gl_ref)); - printk(KERN_INFO " gl_state = %u\n", gl->gl_state); - printk(KERN_INFO " gl_owner = %s\n", gl->gl_owner->comm); - print_symbol(KERN_INFO " gl_ip = %s\n", gl->gl_ip); - printk(KERN_INFO " req_gh = %s\n", (gl->gl_req_gh) ? "yes" : "no"); - printk(KERN_INFO " req_bh = %s\n", (gl->gl_req_bh) ? "yes" : "no"); - printk(KERN_INFO " lvb_count = %d\n", atomic_read(&gl->gl_lvb_count)); - printk(KERN_INFO " object = %s\n", (gl->gl_object) ? "yes" : "no"); - printk(KERN_INFO " le = %s\n", - (list_empty(&gl->gl_le.le_list)) ? "no" : "yes"); - printk(KERN_INFO " reclaim = %s\n", - (list_empty(&gl->gl_reclaim)) ? "no" : "yes"); - if (gl->gl_aspace) - printk(KERN_INFO " aspace = 0x%p nrpages = %lu\n", gl->gl_aspace, - gl->gl_aspace->i_mapping->nrpages); - else - printk(KERN_INFO " aspace = no\n"); - printk(KERN_INFO " ail = %d\n", atomic_read(&gl->gl_ail_count)); - if (gl->gl_req_gh) { - error = dump_holder("Request", gl->gl_req_gh); - if (error) - goto out; - } - list_for_each_entry(gh, &gl->gl_holders, gh_list) { - error = dump_holder("Holder", gh); - if (error) - goto out; - } - list_for_each_entry(gh, &gl->gl_waiters1, gh_list) { - error = dump_holder("Waiter1", gh); - if (error) - goto out; - } - list_for_each_entry(gh, &gl->gl_waiters2, gh_list) { - error = dump_holder("Waiter2", gh); - if (error) - goto out; - } - list_for_each_entry(gh, &gl->gl_waiters3, gh_list) { - error = dump_holder("Waiter3", gh); - if (error) - goto out; - } - if (gl->gl_ops == &gfs2_inode_glops && gl->gl_object) { - if (!test_bit(GLF_LOCK, &gl->gl_flags) && - list_empty(&gl->gl_holders)) { - error = dump_inode(gl->gl_object); - if (error) - goto out; - } else { - error = -ENOBUFS; - printk(KERN_INFO " Inode: busy\n"); - } - } - - error = 0; - -out: - spin_unlock(&gl->gl_spin); - return error; -} - -/** - * gfs2_dump_lockstate - print out the current lockstate - * @sdp: the filesystem - * @ub: the buffer to copy the information into - * - * If @ub is NULL, dump the lockstate to the console. - * - */ - -static int gfs2_dump_lockstate(struct gfs2_sbd *sdp) -{ - struct gfs2_glock *gl; - struct hlist_node *h; - unsigned int x; - int error = 0; - - for (x = 0; x < GFS2_GL_HASH_SIZE; x++) { - - read_lock(gl_lock_addr(x)); - - hlist_for_each_entry(gl, h, &gl_hash_table[x].hb_list, gl_list) { - if (gl->gl_sbd != sdp) - continue; - - error = dump_glock(gl); - if (error) - break; - } - - read_unlock(gl_lock_addr(x)); - - if (error) - break; - } - - - return error; -} - -int __init gfs2_glock_init(void) -{ - unsigned i; - for(i = 0; i < GFS2_GL_HASH_SIZE; i++) { - INIT_HLIST_HEAD(&gl_hash_table[i].hb_list); - } -#ifdef GL_HASH_LOCK_SZ - for(i = 0; i < GL_HASH_LOCK_SZ; i++) { - rwlock_init(&gl_hash_locks[i]); - } -#endif - return 0; -} - diff --git a/trunk/fs/gfs2/glock.h b/trunk/fs/gfs2/glock.h deleted file mode 100644 index 2b2a889ee2cc..000000000000 --- a/trunk/fs/gfs2/glock.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __GLOCK_DOT_H__ -#define __GLOCK_DOT_H__ - -#include "incore.h" - -/* Flags for lock requests; used in gfs2_holder gh_flag field. - From lm_interface.h: -#define LM_FLAG_TRY 0x00000001 -#define LM_FLAG_TRY_1CB 0x00000002 -#define LM_FLAG_NOEXP 0x00000004 -#define LM_FLAG_ANY 0x00000008 -#define LM_FLAG_PRIORITY 0x00000010 */ - -#define GL_LOCAL_EXCL 0x00000020 -#define GL_ASYNC 0x00000040 -#define GL_EXACT 0x00000080 -#define GL_SKIP 0x00000100 -#define GL_ATIME 0x00000200 -#define GL_NOCACHE 0x00000400 -#define GL_NOCANCEL 0x00001000 -#define GL_AOP 0x00004000 -#define GL_DUMP 0x00008000 - -#define GLR_TRYFAILED 13 -#define GLR_CANCELED 14 - -static inline int gfs2_glock_is_locked_by_me(struct gfs2_glock *gl) -{ - struct gfs2_holder *gh; - int locked = 0; - - /* Look in glock's list of holders for one with current task as owner */ - spin_lock(&gl->gl_spin); - list_for_each_entry(gh, &gl->gl_holders, gh_list) { - if (gh->gh_owner == current) { - locked = 1; - break; - } - } - spin_unlock(&gl->gl_spin); - - return locked; -} - -static inline int gfs2_glock_is_held_excl(struct gfs2_glock *gl) -{ - return gl->gl_state == LM_ST_EXCLUSIVE; -} - -static inline int gfs2_glock_is_held_dfrd(struct gfs2_glock *gl) -{ - return gl->gl_state == LM_ST_DEFERRED; -} - -static inline int gfs2_glock_is_held_shrd(struct gfs2_glock *gl) -{ - return gl->gl_state == LM_ST_SHARED; -} - -static inline int gfs2_glock_is_blocking(struct gfs2_glock *gl) -{ - int ret; - spin_lock(&gl->gl_spin); - ret = !list_empty(&gl->gl_waiters2) || !list_empty(&gl->gl_waiters3); - spin_unlock(&gl->gl_spin); - return ret; -} - -int gfs2_glock_get(struct gfs2_sbd *sdp, - u64 number, const struct gfs2_glock_operations *glops, - int create, struct gfs2_glock **glp); -void gfs2_glock_hold(struct gfs2_glock *gl); -int gfs2_glock_put(struct gfs2_glock *gl); -void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, unsigned flags, - struct gfs2_holder *gh); -void gfs2_holder_reinit(unsigned int state, unsigned flags, - struct gfs2_holder *gh); -void gfs2_holder_uninit(struct gfs2_holder *gh); - -void gfs2_glock_xmote_th(struct gfs2_glock *gl, unsigned int state, int flags); -void gfs2_glock_drop_th(struct gfs2_glock *gl); - -int gfs2_glock_nq(struct gfs2_holder *gh); -int gfs2_glock_poll(struct gfs2_holder *gh); -int gfs2_glock_wait(struct gfs2_holder *gh); -void gfs2_glock_dq(struct gfs2_holder *gh); - -int gfs2_glock_be_greedy(struct gfs2_glock *gl, unsigned int time); - -void gfs2_glock_dq_uninit(struct gfs2_holder *gh); -int gfs2_glock_nq_num(struct gfs2_sbd *sdp, - u64 number, const struct gfs2_glock_operations *glops, - unsigned int state, int flags, struct gfs2_holder *gh); - -int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs); -void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs); -void gfs2_glock_dq_uninit_m(unsigned int num_gh, struct gfs2_holder *ghs); - -void gfs2_glock_prefetch_num(struct gfs2_sbd *sdp, u64 number, - const struct gfs2_glock_operations *glops, - unsigned int state, int flags); -void gfs2_glock_inode_squish(struct inode *inode); - -/** - * gfs2_glock_nq_init - intialize a holder and enqueue it on a glock - * @gl: the glock - * @state: the state we're requesting - * @flags: the modifier flags - * @gh: the holder structure - * - * Returns: 0, GLR_*, or errno - */ - -static inline int gfs2_glock_nq_init(struct gfs2_glock *gl, - unsigned int state, int flags, - struct gfs2_holder *gh) -{ - int error; - - gfs2_holder_init(gl, state, flags, gh); - - error = gfs2_glock_nq(gh); - if (error) - gfs2_holder_uninit(gh); - - return error; -} - -/* Lock Value Block functions */ - -int gfs2_lvb_hold(struct gfs2_glock *gl); -void gfs2_lvb_unhold(struct gfs2_glock *gl); - -void gfs2_glock_cb(void *cb_data, unsigned int type, void *data); - -void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); -void gfs2_reclaim_glock(struct gfs2_sbd *sdp); - -void gfs2_scand_internal(struct gfs2_sbd *sdp); -void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait); - -int __init gfs2_glock_init(void); - -#endif /* __GLOCK_DOT_H__ */ diff --git a/trunk/fs/gfs2/glops.c b/trunk/fs/gfs2/glops.c deleted file mode 100644 index 41a6b6818a50..000000000000 --- a/trunk/fs/gfs2/glops.c +++ /dev/null @@ -1,615 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "log.h" -#include "meta_io.h" -#include "recovery.h" -#include "rgrp.h" -#include "util.h" -#include "trans.h" - -/** - * ail_empty_gl - remove all buffers for a given lock from the AIL - * @gl: the glock - * - * None of the buffers should be dirty, locked, or pinned. - */ - -static void gfs2_ail_empty_gl(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - unsigned int blocks; - struct list_head *head = &gl->gl_ail_list; - struct gfs2_bufdata *bd; - struct buffer_head *bh; - u64 blkno; - int error; - - blocks = atomic_read(&gl->gl_ail_count); - if (!blocks) - return; - - error = gfs2_trans_begin(sdp, 0, blocks); - if (gfs2_assert_withdraw(sdp, !error)) - return; - - gfs2_log_lock(sdp); - while (!list_empty(head)) { - bd = list_entry(head->next, struct gfs2_bufdata, - bd_ail_gl_list); - bh = bd->bd_bh; - blkno = bh->b_blocknr; - gfs2_assert_withdraw(sdp, !buffer_busy(bh)); - - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&gl->gl_ail_count); - brelse(bh); - gfs2_log_unlock(sdp); - - gfs2_trans_add_revoke(sdp, blkno); - - gfs2_log_lock(sdp); - } - gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); - gfs2_log_unlock(sdp); - - gfs2_trans_end(sdp); - gfs2_log_flush(sdp, NULL); -} - -/** - * gfs2_pte_inval - Sync and invalidate all PTEs associated with a glock - * @gl: the glock - * - */ - -static void gfs2_pte_inval(struct gfs2_glock *gl) -{ - struct gfs2_inode *ip; - struct inode *inode; - - ip = gl->gl_object; - inode = &ip->i_inode; - if (!ip || !S_ISREG(ip->i_di.di_mode)) - return; - - if (!test_bit(GIF_PAGED, &ip->i_flags)) - return; - - unmap_shared_mapping_range(inode->i_mapping, 0, 0); - - if (test_bit(GIF_SW_PAGED, &ip->i_flags)) - set_bit(GLF_DIRTY, &gl->gl_flags); - - clear_bit(GIF_SW_PAGED, &ip->i_flags); -} - -/** - * gfs2_page_inval - Invalidate all pages associated with a glock - * @gl: the glock - * - */ - -static void gfs2_page_inval(struct gfs2_glock *gl) -{ - struct gfs2_inode *ip; - struct inode *inode; - - ip = gl->gl_object; - inode = &ip->i_inode; - if (!ip || !S_ISREG(ip->i_di.di_mode)) - return; - - truncate_inode_pages(inode->i_mapping, 0); - gfs2_assert_withdraw(GFS2_SB(&ip->i_inode), !inode->i_mapping->nrpages); - clear_bit(GIF_PAGED, &ip->i_flags); -} - -/** - * gfs2_page_wait - Wait for writeback of data - * @gl: the glock - * - * Syncs data (not metadata) for a regular file. - * No-op for all other types. - */ - -static void gfs2_page_wait(struct gfs2_glock *gl) -{ - struct gfs2_inode *ip = gl->gl_object; - struct inode *inode = &ip->i_inode; - struct address_space *mapping = inode->i_mapping; - int error; - - if (!S_ISREG(ip->i_di.di_mode)) - return; - - error = filemap_fdatawait(mapping); - - /* Put back any errors cleared by filemap_fdatawait() - so they can be caught by someone who can pass them - up to user space. */ - - if (error == -ENOSPC) - set_bit(AS_ENOSPC, &mapping->flags); - else if (error) - set_bit(AS_EIO, &mapping->flags); - -} - -static void gfs2_page_writeback(struct gfs2_glock *gl) -{ - struct gfs2_inode *ip = gl->gl_object; - struct inode *inode = &ip->i_inode; - struct address_space *mapping = inode->i_mapping; - - if (!S_ISREG(ip->i_di.di_mode)) - return; - - filemap_fdatawrite(mapping); -} - -/** - * meta_go_sync - sync out the metadata for this glock - * @gl: the glock - * @flags: DIO_* - * - * Called when demoting or unlocking an EX glock. We must flush - * to disk all dirty buffers/pages relating to this glock, and must not - * not return to caller to demote/unlock the glock until I/O is complete. - */ - -static void meta_go_sync(struct gfs2_glock *gl, int flags) -{ - if (!(flags & DIO_METADATA)) - return; - - if (test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) { - gfs2_log_flush(gl->gl_sbd, gl); - gfs2_meta_sync(gl); - if (flags & DIO_RELEASE) - gfs2_ail_empty_gl(gl); - } - -} - -/** - * meta_go_inval - invalidate the metadata for this glock - * @gl: the glock - * @flags: - * - */ - -static void meta_go_inval(struct gfs2_glock *gl, int flags) -{ - if (!(flags & DIO_METADATA)) - return; - - gfs2_meta_inval(gl); - gl->gl_vn++; -} - -/** - * inode_go_xmote_th - promote/demote a glock - * @gl: the glock - * @state: the requested state - * @flags: - * - */ - -static void inode_go_xmote_th(struct gfs2_glock *gl, unsigned int state, - int flags) -{ - if (gl->gl_state != LM_ST_UNLOCKED) - gfs2_pte_inval(gl); - gfs2_glock_xmote_th(gl, state, flags); -} - -/** - * inode_go_xmote_bh - After promoting/demoting a glock - * @gl: the glock - * - */ - -static void inode_go_xmote_bh(struct gfs2_glock *gl) -{ - struct gfs2_holder *gh = gl->gl_req_gh; - struct buffer_head *bh; - int error; - - if (gl->gl_state != LM_ST_UNLOCKED && - (!gh || !(gh->gh_flags & GL_SKIP))) { - error = gfs2_meta_read(gl, gl->gl_name.ln_number, 0, &bh); - if (!error) - brelse(bh); - } -} - -/** - * inode_go_drop_th - unlock a glock - * @gl: the glock - * - * Invoked from rq_demote(). - * Another node needs the lock in EXCLUSIVE mode, or lock (unused for too long) - * is being purged from our node's glock cache; we're dropping lock. - */ - -static void inode_go_drop_th(struct gfs2_glock *gl) -{ - gfs2_pte_inval(gl); - gfs2_glock_drop_th(gl); -} - -/** - * inode_go_sync - Sync the dirty data and/or metadata for an inode glock - * @gl: the glock protecting the inode - * @flags: - * - */ - -static void inode_go_sync(struct gfs2_glock *gl, int flags) -{ - int meta = (flags & DIO_METADATA); - int data = (flags & DIO_DATA); - - if (test_bit(GLF_DIRTY, &gl->gl_flags)) { - if (meta && data) { - gfs2_page_writeback(gl); - gfs2_log_flush(gl->gl_sbd, gl); - gfs2_meta_sync(gl); - gfs2_page_wait(gl); - clear_bit(GLF_DIRTY, &gl->gl_flags); - } else if (meta) { - gfs2_log_flush(gl->gl_sbd, gl); - gfs2_meta_sync(gl); - } else if (data) { - gfs2_page_writeback(gl); - gfs2_page_wait(gl); - } - if (flags & DIO_RELEASE) - gfs2_ail_empty_gl(gl); - } -} - -/** - * inode_go_inval - prepare a inode glock to be released - * @gl: the glock - * @flags: - * - */ - -static void inode_go_inval(struct gfs2_glock *gl, int flags) -{ - int meta = (flags & DIO_METADATA); - int data = (flags & DIO_DATA); - - if (meta) { - gfs2_meta_inval(gl); - gl->gl_vn++; - } - if (data) - gfs2_page_inval(gl); -} - -/** - * inode_go_demote_ok - Check to see if it's ok to unlock an inode glock - * @gl: the glock - * - * Returns: 1 if it's ok - */ - -static int inode_go_demote_ok(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - int demote = 0; - - if (!gl->gl_object && !gl->gl_aspace->i_mapping->nrpages) - demote = 1; - else if (!sdp->sd_args.ar_localcaching && - time_after_eq(jiffies, gl->gl_stamp + - gfs2_tune_get(sdp, gt_demote_secs) * HZ)) - demote = 1; - - return demote; -} - -/** - * inode_go_lock - operation done after an inode lock is locked by a process - * @gl: the glock - * @flags: - * - * Returns: errno - */ - -static int inode_go_lock(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_inode *ip = gl->gl_object; - int error = 0; - - if (!ip) - return 0; - - if (ip->i_vn != gl->gl_vn) { - error = gfs2_inode_refresh(ip); - if (error) - return error; - gfs2_inode_attr_in(ip); - } - - if ((ip->i_di.di_flags & GFS2_DIF_TRUNC_IN_PROG) && - (gl->gl_state == LM_ST_EXCLUSIVE) && - (gh->gh_flags & GL_LOCAL_EXCL)) - error = gfs2_truncatei_resume(ip); - - return error; -} - -/** - * inode_go_unlock - operation done before an inode lock is unlocked by a - * process - * @gl: the glock - * @flags: - * - */ - -static void inode_go_unlock(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_inode *ip = gl->gl_object; - - if (ip == NULL) - return; - if (test_bit(GLF_DIRTY, &gl->gl_flags)) - gfs2_inode_attr_in(ip); - gfs2_meta_cache_flush(ip); -} - -/** - * inode_greedy - - * @gl: the glock - * - */ - -static void inode_greedy(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - struct gfs2_inode *ip = gl->gl_object; - unsigned int quantum = gfs2_tune_get(sdp, gt_greedy_quantum); - unsigned int max = gfs2_tune_get(sdp, gt_greedy_max); - unsigned int new_time; - - spin_lock(&ip->i_spin); - - if (time_after(ip->i_last_pfault + quantum, jiffies)) { - new_time = ip->i_greedy + quantum; - if (new_time > max) - new_time = max; - } else { - new_time = ip->i_greedy - quantum; - if (!new_time || new_time > max) - new_time = 1; - } - - ip->i_greedy = new_time; - - spin_unlock(&ip->i_spin); - - iput(&ip->i_inode); -} - -/** - * rgrp_go_demote_ok - Check to see if it's ok to unlock a RG's glock - * @gl: the glock - * - * Returns: 1 if it's ok - */ - -static int rgrp_go_demote_ok(struct gfs2_glock *gl) -{ - return !gl->gl_aspace->i_mapping->nrpages; -} - -/** - * rgrp_go_lock - operation done after an rgrp lock is locked by - * a first holder on this node. - * @gl: the glock - * @flags: - * - * Returns: errno - */ - -static int rgrp_go_lock(struct gfs2_holder *gh) -{ - return gfs2_rgrp_bh_get(gh->gh_gl->gl_object); -} - -/** - * rgrp_go_unlock - operation done before an rgrp lock is unlocked by - * a last holder on this node. - * @gl: the glock - * @flags: - * - */ - -static void rgrp_go_unlock(struct gfs2_holder *gh) -{ - gfs2_rgrp_bh_put(gh->gh_gl->gl_object); -} - -/** - * trans_go_xmote_th - promote/demote the transaction glock - * @gl: the glock - * @state: the requested state - * @flags: - * - */ - -static void trans_go_xmote_th(struct gfs2_glock *gl, unsigned int state, - int flags) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - - if (gl->gl_state != LM_ST_UNLOCKED && - test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { - gfs2_meta_syncfs(sdp); - gfs2_log_shutdown(sdp); - } - - gfs2_glock_xmote_th(gl, state, flags); -} - -/** - * trans_go_xmote_bh - After promoting/demoting the transaction glock - * @gl: the glock - * - */ - -static void trans_go_xmote_bh(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode); - struct gfs2_glock *j_gl = ip->i_gl; - struct gfs2_log_header head; - int error; - - if (gl->gl_state != LM_ST_UNLOCKED && - test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { - gfs2_meta_cache_flush(GFS2_I(sdp->sd_jdesc->jd_inode)); - j_gl->gl_ops->go_inval(j_gl, DIO_METADATA | DIO_DATA); - - error = gfs2_find_jhead(sdp->sd_jdesc, &head); - if (error) - gfs2_consist(sdp); - if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) - gfs2_consist(sdp); - - /* Initialize some head of the log stuff */ - if (!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) { - sdp->sd_log_sequence = head.lh_sequence + 1; - gfs2_log_pointers_init(sdp, head.lh_blkno); - } - } -} - -/** - * trans_go_drop_th - unlock the transaction glock - * @gl: the glock - * - * We want to sync the device even with localcaching. Remember - * that localcaching journal replay only marks buffers dirty. - */ - -static void trans_go_drop_th(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - - if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { - gfs2_meta_syncfs(sdp); - gfs2_log_shutdown(sdp); - } - - gfs2_glock_drop_th(gl); -} - -/** - * quota_go_demote_ok - Check to see if it's ok to unlock a quota glock - * @gl: the glock - * - * Returns: 1 if it's ok - */ - -static int quota_go_demote_ok(struct gfs2_glock *gl) -{ - return !atomic_read(&gl->gl_lvb_count); -} - -const struct gfs2_glock_operations gfs2_meta_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_type = LM_TYPE_META, -}; - -const struct gfs2_glock_operations gfs2_inode_glops = { - .go_xmote_th = inode_go_xmote_th, - .go_xmote_bh = inode_go_xmote_bh, - .go_drop_th = inode_go_drop_th, - .go_sync = inode_go_sync, - .go_inval = inode_go_inval, - .go_demote_ok = inode_go_demote_ok, - .go_lock = inode_go_lock, - .go_unlock = inode_go_unlock, - .go_greedy = inode_greedy, - .go_type = LM_TYPE_INODE, -}; - -const struct gfs2_glock_operations gfs2_rgrp_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_sync = meta_go_sync, - .go_inval = meta_go_inval, - .go_demote_ok = rgrp_go_demote_ok, - .go_lock = rgrp_go_lock, - .go_unlock = rgrp_go_unlock, - .go_type = LM_TYPE_RGRP, -}; - -const struct gfs2_glock_operations gfs2_trans_glops = { - .go_xmote_th = trans_go_xmote_th, - .go_xmote_bh = trans_go_xmote_bh, - .go_drop_th = trans_go_drop_th, - .go_type = LM_TYPE_NONDISK, -}; - -const struct gfs2_glock_operations gfs2_iopen_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_type = LM_TYPE_IOPEN, -}; - -const struct gfs2_glock_operations gfs2_flock_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_type = LM_TYPE_FLOCK, -}; - -const struct gfs2_glock_operations gfs2_nondisk_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_type = LM_TYPE_NONDISK, -}; - -const struct gfs2_glock_operations gfs2_quota_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_demote_ok = quota_go_demote_ok, - .go_type = LM_TYPE_QUOTA, -}; - -const struct gfs2_glock_operations gfs2_journal_glops = { - .go_xmote_th = gfs2_glock_xmote_th, - .go_drop_th = gfs2_glock_drop_th, - .go_type = LM_TYPE_JOURNAL, -}; - diff --git a/trunk/fs/gfs2/glops.h b/trunk/fs/gfs2/glops.h deleted file mode 100644 index a1d9b5b024e6..000000000000 --- a/trunk/fs/gfs2/glops.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __GLOPS_DOT_H__ -#define __GLOPS_DOT_H__ - -#include "incore.h" - -extern const struct gfs2_glock_operations gfs2_meta_glops; -extern const struct gfs2_glock_operations gfs2_inode_glops; -extern const struct gfs2_glock_operations gfs2_rgrp_glops; -extern const struct gfs2_glock_operations gfs2_trans_glops; -extern const struct gfs2_glock_operations gfs2_iopen_glops; -extern const struct gfs2_glock_operations gfs2_flock_glops; -extern const struct gfs2_glock_operations gfs2_nondisk_glops; -extern const struct gfs2_glock_operations gfs2_quota_glops; -extern const struct gfs2_glock_operations gfs2_journal_glops; - -#endif /* __GLOPS_DOT_H__ */ diff --git a/trunk/fs/gfs2/incore.h b/trunk/fs/gfs2/incore.h deleted file mode 100644 index 118dc693d111..000000000000 --- a/trunk/fs/gfs2/incore.h +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __INCORE_DOT_H__ -#define __INCORE_DOT_H__ - -#include - -#define DIO_WAIT 0x00000010 -#define DIO_METADATA 0x00000020 -#define DIO_DATA 0x00000040 -#define DIO_RELEASE 0x00000080 -#define DIO_ALL 0x00000100 - -struct gfs2_log_operations; -struct gfs2_log_element; -struct gfs2_holder; -struct gfs2_glock; -struct gfs2_quota_data; -struct gfs2_trans; -struct gfs2_ail; -struct gfs2_jdesc; -struct gfs2_sbd; - -typedef void (*gfs2_glop_bh_t) (struct gfs2_glock *gl, unsigned int ret); - -/* - * Structure of operations that are associated with each - * type of element in the log. - */ - -struct gfs2_log_operations { - void (*lo_add) (struct gfs2_sbd *sdp, struct gfs2_log_element *le); - void (*lo_incore_commit) (struct gfs2_sbd *sdp, struct gfs2_trans *tr); - void (*lo_before_commit) (struct gfs2_sbd *sdp); - void (*lo_after_commit) (struct gfs2_sbd *sdp, struct gfs2_ail *ai); - void (*lo_before_scan) (struct gfs2_jdesc *jd, - struct gfs2_log_header *head, int pass); - int (*lo_scan_elements) (struct gfs2_jdesc *jd, unsigned int start, - struct gfs2_log_descriptor *ld, __be64 *ptr, - int pass); - void (*lo_after_scan) (struct gfs2_jdesc *jd, int error, int pass); - const char *lo_name; -}; - -struct gfs2_log_element { - struct list_head le_list; - const struct gfs2_log_operations *le_ops; -}; - -struct gfs2_bitmap { - struct buffer_head *bi_bh; - char *bi_clone; - u32 bi_offset; - u32 bi_start; - u32 bi_len; -}; - -struct gfs2_rgrpd { - struct list_head rd_list; /* Link with superblock */ - struct list_head rd_list_mru; - struct list_head rd_recent; /* Recently used rgrps */ - struct gfs2_glock *rd_gl; /* Glock for this rgrp */ - struct gfs2_rindex rd_ri; - struct gfs2_rgrp rd_rg; - u64 rd_rg_vn; - struct gfs2_bitmap *rd_bits; - unsigned int rd_bh_count; - struct mutex rd_mutex; - u32 rd_free_clone; - struct gfs2_log_element rd_le; - u32 rd_last_alloc_data; - u32 rd_last_alloc_meta; - struct gfs2_sbd *rd_sbd; -}; - -enum gfs2_state_bits { - BH_Pinned = BH_PrivateStart, - BH_Escaped = BH_PrivateStart + 1, -}; - -BUFFER_FNS(Pinned, pinned) -TAS_BUFFER_FNS(Pinned, pinned) -BUFFER_FNS(Escaped, escaped) -TAS_BUFFER_FNS(Escaped, escaped) - -struct gfs2_bufdata { - struct buffer_head *bd_bh; - struct gfs2_glock *bd_gl; - - struct list_head bd_list_tr; - struct gfs2_log_element bd_le; - - struct gfs2_ail *bd_ail; - struct list_head bd_ail_st_list; - struct list_head bd_ail_gl_list; -}; - -struct gfs2_glock_operations { - void (*go_xmote_th) (struct gfs2_glock * gl, unsigned int state, - int flags); - void (*go_xmote_bh) (struct gfs2_glock * gl); - void (*go_drop_th) (struct gfs2_glock * gl); - void (*go_drop_bh) (struct gfs2_glock * gl); - void (*go_sync) (struct gfs2_glock * gl, int flags); - void (*go_inval) (struct gfs2_glock * gl, int flags); - int (*go_demote_ok) (struct gfs2_glock * gl); - int (*go_lock) (struct gfs2_holder * gh); - void (*go_unlock) (struct gfs2_holder * gh); - void (*go_callback) (struct gfs2_glock * gl, unsigned int state); - void (*go_greedy) (struct gfs2_glock * gl); - const int go_type; -}; - -enum { - /* Actions */ - HIF_MUTEX = 0, - HIF_PROMOTE = 1, - HIF_DEMOTE = 2, - HIF_GREEDY = 3, - - /* States */ - HIF_ALLOCED = 4, - HIF_DEALLOC = 5, - HIF_HOLDER = 6, - HIF_FIRST = 7, - HIF_ABORTED = 9, -}; - -struct gfs2_holder { - struct list_head gh_list; - - struct gfs2_glock *gh_gl; - struct task_struct *gh_owner; - unsigned int gh_state; - unsigned gh_flags; - - int gh_error; - unsigned long gh_iflags; - struct completion gh_wait; - unsigned long gh_ip; -}; - -enum { - GLF_LOCK = 1, - GLF_STICKY = 2, - GLF_PREFETCH = 3, - GLF_DIRTY = 5, - GLF_SKIP_WAITERS2 = 6, - GLF_GREEDY = 7, -}; - -struct gfs2_glock { - struct hlist_node gl_list; - unsigned long gl_flags; /* GLF_... */ - struct lm_lockname gl_name; - atomic_t gl_ref; - - spinlock_t gl_spin; - - unsigned int gl_state; - unsigned int gl_hash; - struct task_struct *gl_owner; - unsigned long gl_ip; - struct list_head gl_holders; - struct list_head gl_waiters1; /* HIF_MUTEX */ - struct list_head gl_waiters2; /* HIF_DEMOTE, HIF_GREEDY */ - struct list_head gl_waiters3; /* HIF_PROMOTE */ - - const struct gfs2_glock_operations *gl_ops; - - struct gfs2_holder *gl_req_gh; - gfs2_glop_bh_t gl_req_bh; - - void *gl_lock; - char *gl_lvb; - atomic_t gl_lvb_count; - - u64 gl_vn; - unsigned long gl_stamp; - void *gl_object; - - struct list_head gl_reclaim; - - struct gfs2_sbd *gl_sbd; - - struct inode *gl_aspace; - struct gfs2_log_element gl_le; - struct list_head gl_ail_list; - atomic_t gl_ail_count; -}; - -struct gfs2_alloc { - /* Quota stuff */ - - struct gfs2_quota_data *al_qd[2*MAXQUOTAS]; - struct gfs2_holder al_qd_ghs[2*MAXQUOTAS]; - unsigned int al_qd_num; - - u32 al_requested; /* Filled in by caller of gfs2_inplace_reserve() */ - u32 al_alloced; /* Filled in by gfs2_alloc_*() */ - - /* Filled in by gfs2_inplace_reserve() */ - - unsigned int al_line; - char *al_file; - struct gfs2_holder al_ri_gh; - struct gfs2_holder al_rgd_gh; - struct gfs2_rgrpd *al_rgd; - -}; - -enum { - GIF_QD_LOCKED = 1, - GIF_PAGED = 2, - GIF_SW_PAGED = 3, -}; - -struct gfs2_inode { - struct inode i_inode; - struct gfs2_inum i_num; - - unsigned long i_flags; /* GIF_... */ - - u64 i_vn; - struct gfs2_dinode i_di; /* To be replaced by ref to block */ - - struct gfs2_glock *i_gl; /* Move into i_gh? */ - struct gfs2_holder i_iopen_gh; - struct gfs2_holder i_gh; /* for prepare/commit_write only */ - struct gfs2_alloc i_alloc; - u64 i_last_rg_alloc; - - spinlock_t i_spin; - struct rw_semaphore i_rw_mutex; - unsigned int i_greedy; - unsigned long i_last_pfault; - - struct buffer_head *i_cache[GFS2_MAX_META_HEIGHT]; -}; - -/* - * Since i_inode is the first element of struct gfs2_inode, - * this is effectively a cast. - */ -static inline struct gfs2_inode *GFS2_I(struct inode *inode) -{ - return container_of(inode, struct gfs2_inode, i_inode); -} - -/* To be removed? */ -static inline struct gfs2_sbd *GFS2_SB(struct inode *inode) -{ - return inode->i_sb->s_fs_info; -} - -enum { - GFF_DID_DIRECT_ALLOC = 0, - GFF_EXLOCK = 1, -}; - -struct gfs2_file { - unsigned long f_flags; /* GFF_... */ - struct mutex f_fl_mutex; - struct gfs2_holder f_fl_gh; -}; - -struct gfs2_revoke { - struct gfs2_log_element rv_le; - u64 rv_blkno; -}; - -struct gfs2_revoke_replay { - struct list_head rr_list; - u64 rr_blkno; - unsigned int rr_where; -}; - -enum { - QDF_USER = 0, - QDF_CHANGE = 1, - QDF_LOCKED = 2, -}; - -struct gfs2_quota_lvb { - __be32 qb_magic; - u32 __pad; - __be64 qb_limit; /* Hard limit of # blocks to alloc */ - __be64 qb_warn; /* Warn user when alloc is above this # */ - __be64 qb_value; /* Current # blocks allocated */ -}; - -struct gfs2_quota_data { - struct list_head qd_list; - unsigned int qd_count; - - u32 qd_id; - unsigned long qd_flags; /* QDF_... */ - - s64 qd_change; - s64 qd_change_sync; - - unsigned int qd_slot; - unsigned int qd_slot_count; - - struct buffer_head *qd_bh; - struct gfs2_quota_change *qd_bh_qc; - unsigned int qd_bh_count; - - struct gfs2_glock *qd_gl; - struct gfs2_quota_lvb qd_qb; - - u64 qd_sync_gen; - unsigned long qd_last_warn; - unsigned long qd_last_touched; -}; - -struct gfs2_log_buf { - struct list_head lb_list; - struct buffer_head *lb_bh; - struct buffer_head *lb_real; -}; - -struct gfs2_trans { - unsigned long tr_ip; - - unsigned int tr_blocks; - unsigned int tr_revokes; - unsigned int tr_reserved; - - struct gfs2_holder tr_t_gh; - - int tr_touched; - - unsigned int tr_num_buf; - unsigned int tr_num_buf_new; - unsigned int tr_num_buf_rm; - struct list_head tr_list_buf; - - unsigned int tr_num_revoke; - unsigned int tr_num_revoke_rm; -}; - -struct gfs2_ail { - struct list_head ai_list; - - unsigned int ai_first; - struct list_head ai_ail1_list; - struct list_head ai_ail2_list; - - u64 ai_sync_gen; -}; - -struct gfs2_jdesc { - struct list_head jd_list; - - struct inode *jd_inode; - unsigned int jd_jid; - int jd_dirty; - - unsigned int jd_blocks; -}; - -#define GFS2_GLOCKD_DEFAULT 1 -#define GFS2_GLOCKD_MAX 16 - -#define GFS2_QUOTA_DEFAULT GFS2_QUOTA_OFF -#define GFS2_QUOTA_OFF 0 -#define GFS2_QUOTA_ACCOUNT 1 -#define GFS2_QUOTA_ON 2 - -#define GFS2_DATA_DEFAULT GFS2_DATA_ORDERED -#define GFS2_DATA_WRITEBACK 1 -#define GFS2_DATA_ORDERED 2 - -struct gfs2_args { - char ar_lockproto[GFS2_LOCKNAME_LEN]; /* Name of the Lock Protocol */ - char ar_locktable[GFS2_LOCKNAME_LEN]; /* Name of the Lock Table */ - char ar_hostdata[GFS2_LOCKNAME_LEN]; /* Host specific data */ - int ar_spectator; /* Don't get a journal because we're always RO */ - int ar_ignore_local_fs; /* Don't optimize even if local_fs is 1 */ - int ar_localflocks; /* Let the VFS do flock|fcntl locks for us */ - int ar_localcaching; /* Local-style caching (dangerous on multihost) */ - int ar_debug; /* Oops on errors instead of trying to be graceful */ - int ar_upgrade; /* Upgrade ondisk/multihost format */ - unsigned int ar_num_glockd; /* Number of glockd threads */ - int ar_posix_acl; /* Enable posix acls */ - int ar_quota; /* off/account/on */ - int ar_suiddir; /* suiddir support */ - int ar_data; /* ordered/writeback */ -}; - -struct gfs2_tune { - spinlock_t gt_spin; - - unsigned int gt_ilimit; - unsigned int gt_ilimit_tries; - unsigned int gt_ilimit_min; - unsigned int gt_demote_secs; /* Cache retention for unheld glock */ - unsigned int gt_incore_log_blocks; - unsigned int gt_log_flush_secs; - unsigned int gt_jindex_refresh_secs; /* Check for new journal index */ - - unsigned int gt_scand_secs; - unsigned int gt_recoverd_secs; - unsigned int gt_logd_secs; - unsigned int gt_quotad_secs; - - unsigned int gt_quota_simul_sync; /* Max quotavals to sync at once */ - unsigned int gt_quota_warn_period; /* Secs between quota warn msgs */ - unsigned int gt_quota_scale_num; /* Numerator */ - unsigned int gt_quota_scale_den; /* Denominator */ - unsigned int gt_quota_cache_secs; - unsigned int gt_quota_quantum; /* Secs between syncs to quota file */ - unsigned int gt_atime_quantum; /* Min secs between atime updates */ - unsigned int gt_new_files_jdata; - unsigned int gt_new_files_directio; - unsigned int gt_max_atomic_write; /* Split big writes into this size */ - unsigned int gt_max_readahead; /* Max bytes to read-ahead from disk */ - unsigned int gt_lockdump_size; - unsigned int gt_stall_secs; /* Detects trouble! */ - unsigned int gt_complain_secs; - unsigned int gt_reclaim_limit; /* Max num of glocks in reclaim list */ - unsigned int gt_entries_per_readdir; - unsigned int gt_prefetch_secs; /* Usage window for prefetched glocks */ - unsigned int gt_greedy_default; - unsigned int gt_greedy_quantum; - unsigned int gt_greedy_max; - unsigned int gt_statfs_quantum; - unsigned int gt_statfs_slow; -}; - -enum { - SDF_JOURNAL_CHECKED = 0, - SDF_JOURNAL_LIVE = 1, - SDF_SHUTDOWN = 2, - SDF_NOATIME = 3, -}; - -#define GFS2_FSNAME_LEN 256 - -struct gfs2_sbd { - struct super_block *sd_vfs; - struct super_block *sd_vfs_meta; - struct kobject sd_kobj; - unsigned long sd_flags; /* SDF_... */ - struct gfs2_sb sd_sb; - - /* Constants computed on mount */ - - u32 sd_fsb2bb; - u32 sd_fsb2bb_shift; - u32 sd_diptrs; /* Number of pointers in a dinode */ - u32 sd_inptrs; /* Number of pointers in a indirect block */ - u32 sd_jbsize; /* Size of a journaled data block */ - u32 sd_hash_bsize; /* sizeof(exhash block) */ - u32 sd_hash_bsize_shift; - u32 sd_hash_ptrs; /* Number of pointers in a hash block */ - u32 sd_qc_per_block; - u32 sd_max_dirres; /* Max blocks needed to add a directory entry */ - u32 sd_max_height; /* Max height of a file's metadata tree */ - u64 sd_heightsize[GFS2_MAX_META_HEIGHT]; - u32 sd_max_jheight; /* Max height of journaled file's meta tree */ - u64 sd_jheightsize[GFS2_MAX_META_HEIGHT]; - - struct gfs2_args sd_args; /* Mount arguments */ - struct gfs2_tune sd_tune; /* Filesystem tuning structure */ - - /* Lock Stuff */ - - struct lm_lockstruct sd_lockstruct; - struct list_head sd_reclaim_list; - spinlock_t sd_reclaim_lock; - wait_queue_head_t sd_reclaim_wq; - atomic_t sd_reclaim_count; - struct gfs2_holder sd_live_gh; - struct gfs2_glock *sd_rename_gl; - struct gfs2_glock *sd_trans_gl; - - /* Inode Stuff */ - - struct inode *sd_master_dir; - struct inode *sd_jindex; - struct inode *sd_inum_inode; - struct inode *sd_statfs_inode; - struct inode *sd_ir_inode; - struct inode *sd_sc_inode; - struct inode *sd_qc_inode; - struct inode *sd_rindex; - struct inode *sd_quota_inode; - - /* Inum stuff */ - - struct mutex sd_inum_mutex; - - /* StatFS stuff */ - - spinlock_t sd_statfs_spin; - struct mutex sd_statfs_mutex; - struct gfs2_statfs_change sd_statfs_master; - struct gfs2_statfs_change sd_statfs_local; - unsigned long sd_statfs_sync_time; - - /* Resource group stuff */ - - u64 sd_rindex_vn; - spinlock_t sd_rindex_spin; - struct mutex sd_rindex_mutex; - struct list_head sd_rindex_list; - struct list_head sd_rindex_mru_list; - struct list_head sd_rindex_recent_list; - struct gfs2_rgrpd *sd_rindex_forward; - unsigned int sd_rgrps; - - /* Journal index stuff */ - - struct list_head sd_jindex_list; - spinlock_t sd_jindex_spin; - struct mutex sd_jindex_mutex; - unsigned int sd_journals; - unsigned long sd_jindex_refresh_time; - - struct gfs2_jdesc *sd_jdesc; - struct gfs2_holder sd_journal_gh; - struct gfs2_holder sd_jinode_gh; - - struct gfs2_holder sd_ir_gh; - struct gfs2_holder sd_sc_gh; - struct gfs2_holder sd_qc_gh; - - /* Daemon stuff */ - - struct task_struct *sd_scand_process; - struct task_struct *sd_recoverd_process; - struct task_struct *sd_logd_process; - struct task_struct *sd_quotad_process; - struct task_struct *sd_glockd_process[GFS2_GLOCKD_MAX]; - unsigned int sd_glockd_num; - - /* Quota stuff */ - - struct list_head sd_quota_list; - atomic_t sd_quota_count; - spinlock_t sd_quota_spin; - struct mutex sd_quota_mutex; - - unsigned int sd_quota_slots; - unsigned int sd_quota_chunks; - unsigned char **sd_quota_bitmap; - - u64 sd_quota_sync_gen; - unsigned long sd_quota_sync_time; - - /* Log stuff */ - - spinlock_t sd_log_lock; - - unsigned int sd_log_blks_reserved; - unsigned int sd_log_commited_buf; - unsigned int sd_log_commited_revoke; - - unsigned int sd_log_num_gl; - unsigned int sd_log_num_buf; - unsigned int sd_log_num_revoke; - unsigned int sd_log_num_rg; - unsigned int sd_log_num_databuf; - unsigned int sd_log_num_jdata; - unsigned int sd_log_num_hdrs; - - struct list_head sd_log_le_gl; - struct list_head sd_log_le_buf; - struct list_head sd_log_le_revoke; - struct list_head sd_log_le_rg; - struct list_head sd_log_le_databuf; - - unsigned int sd_log_blks_free; - struct mutex sd_log_reserve_mutex; - - u64 sd_log_sequence; - unsigned int sd_log_head; - unsigned int sd_log_tail; - int sd_log_idle; - - unsigned long sd_log_flush_time; - struct rw_semaphore sd_log_flush_lock; - struct list_head sd_log_flush_list; - - unsigned int sd_log_flush_head; - u64 sd_log_flush_wrapped; - - struct list_head sd_ail1_list; - struct list_head sd_ail2_list; - u64 sd_ail_sync_gen; - - /* Replay stuff */ - - struct list_head sd_revoke_list; - unsigned int sd_replay_tail; - - unsigned int sd_found_blocks; - unsigned int sd_found_revokes; - unsigned int sd_replayed_blocks; - - /* For quiescing the filesystem */ - - struct gfs2_holder sd_freeze_gh; - struct mutex sd_freeze_lock; - unsigned int sd_freeze_count; - - /* Counters */ - - atomic_t sd_glock_count; - atomic_t sd_glock_held_count; - atomic_t sd_inode_count; - atomic_t sd_reclaimed; - - char sd_fsname[GFS2_FSNAME_LEN]; - char sd_table_name[GFS2_FSNAME_LEN]; - char sd_proto_name[GFS2_FSNAME_LEN]; - - /* Debugging crud */ - - unsigned long sd_last_warning; - struct vfsmount *sd_gfs2mnt; -}; - -#endif /* __INCORE_DOT_H__ */ - diff --git a/trunk/fs/gfs2/inode.c b/trunk/fs/gfs2/inode.c deleted file mode 100644 index 57c43ac47925..000000000000 --- a/trunk/fs/gfs2/inode.c +++ /dev/null @@ -1,1379 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "acl.h" -#include "bmap.h" -#include "dir.h" -#include "eattr.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "log.h" -#include "meta_io.h" -#include "ops_address.h" -#include "ops_file.h" -#include "ops_inode.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" - -/** - * gfs2_inode_attr_in - Copy attributes from the dinode into the VFS inode - * @ip: The GFS2 inode (with embedded disk inode data) - * @inode: The Linux VFS inode - * - */ - -void gfs2_inode_attr_in(struct gfs2_inode *ip) -{ - struct inode *inode = &ip->i_inode; - struct gfs2_dinode *di = &ip->i_di; - - inode->i_ino = ip->i_num.no_addr; - - switch (di->di_mode & S_IFMT) { - case S_IFBLK: - case S_IFCHR: - inode->i_rdev = MKDEV(di->di_major, di->di_minor); - break; - default: - inode->i_rdev = 0; - break; - }; - - inode->i_mode = di->di_mode; - inode->i_nlink = di->di_nlink; - inode->i_uid = di->di_uid; - inode->i_gid = di->di_gid; - i_size_write(inode, di->di_size); - inode->i_atime.tv_sec = di->di_atime; - inode->i_mtime.tv_sec = di->di_mtime; - inode->i_ctime.tv_sec = di->di_ctime; - inode->i_atime.tv_nsec = 0; - inode->i_mtime.tv_nsec = 0; - inode->i_ctime.tv_nsec = 0; - inode->i_blocks = di->di_blocks << - (GFS2_SB(inode)->sd_sb.sb_bsize_shift - GFS2_BASIC_BLOCK_SHIFT); - - if (di->di_flags & GFS2_DIF_IMMUTABLE) - inode->i_flags |= S_IMMUTABLE; - else - inode->i_flags &= ~S_IMMUTABLE; - - if (di->di_flags & GFS2_DIF_APPENDONLY) - inode->i_flags |= S_APPEND; - else - inode->i_flags &= ~S_APPEND; -} - -/** - * gfs2_inode_attr_out - Copy attributes from VFS inode into the dinode - * @ip: The GFS2 inode - * - * Only copy out the attributes that we want the VFS layer - * to be able to modify. - */ - -void gfs2_inode_attr_out(struct gfs2_inode *ip) -{ - struct inode *inode = &ip->i_inode; - struct gfs2_dinode *di = &ip->i_di; - gfs2_assert_withdraw(GFS2_SB(inode), - (di->di_mode & S_IFMT) == (inode->i_mode & S_IFMT)); - di->di_mode = inode->i_mode; - di->di_uid = inode->i_uid; - di->di_gid = inode->i_gid; - di->di_atime = inode->i_atime.tv_sec; - di->di_mtime = inode->i_mtime.tv_sec; - di->di_ctime = inode->i_ctime.tv_sec; -} - -static int iget_test(struct inode *inode, void *opaque) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_inum *inum = opaque; - - if (ip && ip->i_num.no_addr == inum->no_addr) - return 1; - - return 0; -} - -static int iget_set(struct inode *inode, void *opaque) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_inum *inum = opaque; - - ip->i_num = *inum; - return 0; -} - -struct inode *gfs2_ilookup(struct super_block *sb, struct gfs2_inum *inum) -{ - return ilookup5(sb, (unsigned long)inum->no_formal_ino, - iget_test, inum); -} - -static struct inode *gfs2_iget(struct super_block *sb, struct gfs2_inum *inum) -{ - return iget5_locked(sb, (unsigned long)inum->no_formal_ino, - iget_test, iget_set, inum); -} - -/** - * gfs2_inode_lookup - Lookup an inode - * @sb: The super block - * @inum: The inode number - * @type: The type of the inode - * - * Returns: A VFS inode, or an error - */ - -struct inode *gfs2_inode_lookup(struct super_block *sb, struct gfs2_inum *inum, unsigned int type) -{ - struct inode *inode = gfs2_iget(sb, inum); - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_glock *io_gl; - int error; - - if (inode->i_state & I_NEW) { - struct gfs2_sbd *sdp = GFS2_SB(inode); - umode_t mode = DT2IF(type); - inode->i_private = ip; - inode->i_mode = mode; - - if (S_ISREG(mode)) { - inode->i_op = &gfs2_file_iops; - inode->i_fop = &gfs2_file_fops; - inode->i_mapping->a_ops = &gfs2_file_aops; - } else if (S_ISDIR(mode)) { - inode->i_op = &gfs2_dir_iops; - inode->i_fop = &gfs2_dir_fops; - } else if (S_ISLNK(mode)) { - inode->i_op = &gfs2_symlink_iops; - } else { - inode->i_op = &gfs2_dev_iops; - } - - error = gfs2_glock_get(sdp, inum->no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); - if (unlikely(error)) - goto fail; - ip->i_gl->gl_object = ip; - - error = gfs2_glock_get(sdp, inum->no_addr, &gfs2_iopen_glops, CREATE, &io_gl); - if (unlikely(error)) - goto fail_put; - - ip->i_vn = ip->i_gl->gl_vn - 1; - error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); - if (unlikely(error)) - goto fail_iopen; - - gfs2_glock_put(io_gl); - unlock_new_inode(inode); - } - - return inode; -fail_iopen: - gfs2_glock_put(io_gl); -fail_put: - ip->i_gl->gl_object = NULL; - gfs2_glock_put(ip->i_gl); -fail: - iput(inode); - return ERR_PTR(error); -} - -/** - * gfs2_inode_refresh - Refresh the incore copy of the dinode - * @ip: The GFS2 inode - * - * Returns: errno - */ - -int gfs2_inode_refresh(struct gfs2_inode *ip) -{ - struct buffer_head *dibh; - int error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), dibh, GFS2_METATYPE_DI)) { - brelse(dibh); - return -EIO; - } - - gfs2_dinode_in(&ip->i_di, dibh->b_data); - - brelse(dibh); - - if (ip->i_num.no_addr != ip->i_di.di_num.no_addr) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(&ip->i_di); - return -EIO; - } - if (ip->i_num.no_formal_ino != ip->i_di.di_num.no_formal_ino) - return -ESTALE; - - ip->i_vn = ip->i_gl->gl_vn; - - return 0; -} - -int gfs2_dinode_dealloc(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al; - struct gfs2_rgrpd *rgd; - int error; - - if (ip->i_di.di_blocks != 1) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(&ip->i_di); - return -EIO; - } - - al = gfs2_alloc_get(ip); - - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out; - - error = gfs2_rindex_hold(sdp, &al->al_ri_gh); - if (error) - goto out_qs; - - rgd = gfs2_blk2rgrpd(sdp, ip->i_num.no_addr); - if (!rgd) { - gfs2_consist_inode(ip); - error = -EIO; - goto out_rindex_relse; - } - - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, - &al->al_rgd_gh); - if (error) - goto out_rindex_relse; - - error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA, 1); - if (error) - goto out_rg_gunlock; - - gfs2_trans_add_gl(ip->i_gl); - - gfs2_free_di(rgd, ip); - - gfs2_trans_end(sdp); - clear_bit(GLF_STICKY, &ip->i_gl->gl_flags); - -out_rg_gunlock: - gfs2_glock_dq_uninit(&al->al_rgd_gh); -out_rindex_relse: - gfs2_glock_dq_uninit(&al->al_ri_gh); -out_qs: - gfs2_quota_unhold(ip); -out: - gfs2_alloc_put(ip); - return error; -} - -/** - * gfs2_change_nlink - Change nlink count on inode - * @ip: The GFS2 inode - * @diff: The change in the nlink count required - * - * Returns: errno - */ - -int gfs2_change_nlink(struct gfs2_inode *ip, int diff) -{ - struct gfs2_sbd *sdp = ip->i_inode.i_sb->s_fs_info; - struct buffer_head *dibh; - u32 nlink; - int error; - - BUG_ON(ip->i_di.di_nlink != ip->i_inode.i_nlink); - nlink = ip->i_di.di_nlink + diff; - - /* If we are reducing the nlink count, but the new value ends up being - bigger than the old one, we must have underflowed. */ - if (diff < 0 && nlink > ip->i_di.di_nlink) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(&ip->i_di); - return -EIO; - } - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - ip->i_di.di_nlink = nlink; - ip->i_di.di_ctime = get_seconds(); - ip->i_inode.i_nlink = nlink; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - mark_inode_dirty(&ip->i_inode); - - if (ip->i_di.di_nlink == 0) { - struct gfs2_rgrpd *rgd; - struct gfs2_holder ri_gh, rg_gh; - - error = gfs2_rindex_hold(sdp, &ri_gh); - if (error) - goto out; - error = -EIO; - rgd = gfs2_blk2rgrpd(sdp, ip->i_num.no_addr); - if (!rgd) - goto out_norgrp; - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, &rg_gh); - if (error) - goto out_norgrp; - - clear_nlink(&ip->i_inode); - gfs2_unlink_di(&ip->i_inode); /* mark inode unlinked */ - gfs2_glock_dq_uninit(&rg_gh); -out_norgrp: - gfs2_glock_dq_uninit(&ri_gh); - } -out: - return error; -} - -struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) -{ - struct qstr qstr; - gfs2_str2qstr(&qstr, name); - return gfs2_lookupi(dip, &qstr, 1, NULL); -} - - -/** - * gfs2_lookupi - Look up a filename in a directory and return its inode - * @d_gh: An initialized holder for the directory glock - * @name: The name of the inode to look for - * @is_root: If 1, ignore the caller's permissions - * @i_gh: An uninitialized holder for the new inode glock - * - * There will always be a vnode (Linux VFS inode) for the d_gh inode unless - * @is_root is true. - * - * Returns: errno - */ - -struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, - int is_root, struct nameidata *nd) -{ - struct super_block *sb = dir->i_sb; - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_holder d_gh; - struct gfs2_inum inum; - unsigned int type; - int error = 0; - struct inode *inode = NULL; - - if (!name->len || name->len > GFS2_FNAMESIZE) - return ERR_PTR(-ENAMETOOLONG); - - if ((name->len == 1 && memcmp(name->name, ".", 1) == 0) || - (name->len == 2 && memcmp(name->name, "..", 2) == 0 && - dir == sb->s_root->d_inode)) { - igrab(dir); - return dir; - } - - error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); - if (error) - return ERR_PTR(error); - - if (!is_root) { - error = permission(dir, MAY_EXEC, NULL); - if (error) - goto out; - } - - error = gfs2_dir_search(dir, name, &inum, &type); - if (error) - goto out; - - inode = gfs2_inode_lookup(sb, &inum, type); - -out: - gfs2_glock_dq_uninit(&d_gh); - if (error == -ENOENT) - return NULL; - return inode; -} - -static int pick_formal_ino_1(struct gfs2_sbd *sdp, u64 *formal_ino) -{ - struct gfs2_inode *ip = GFS2_I(sdp->sd_ir_inode); - struct buffer_head *bh; - struct gfs2_inum_range ir; - int error; - - error = gfs2_trans_begin(sdp, RES_DINODE, 0); - if (error) - return error; - mutex_lock(&sdp->sd_inum_mutex); - - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) { - mutex_unlock(&sdp->sd_inum_mutex); - gfs2_trans_end(sdp); - return error; - } - - gfs2_inum_range_in(&ir, bh->b_data + sizeof(struct gfs2_dinode)); - - if (ir.ir_length) { - *formal_ino = ir.ir_start++; - ir.ir_length--; - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_inum_range_out(&ir, - bh->b_data + sizeof(struct gfs2_dinode)); - brelse(bh); - mutex_unlock(&sdp->sd_inum_mutex); - gfs2_trans_end(sdp); - return 0; - } - - brelse(bh); - - mutex_unlock(&sdp->sd_inum_mutex); - gfs2_trans_end(sdp); - - return 1; -} - -static int pick_formal_ino_2(struct gfs2_sbd *sdp, u64 *formal_ino) -{ - struct gfs2_inode *ip = GFS2_I(sdp->sd_ir_inode); - struct gfs2_inode *m_ip = GFS2_I(sdp->sd_inum_inode); - struct gfs2_holder gh; - struct buffer_head *bh; - struct gfs2_inum_range ir; - int error; - - error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); - if (error) - return error; - - error = gfs2_trans_begin(sdp, 2 * RES_DINODE, 0); - if (error) - goto out; - mutex_lock(&sdp->sd_inum_mutex); - - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - goto out_end_trans; - - gfs2_inum_range_in(&ir, bh->b_data + sizeof(struct gfs2_dinode)); - - if (!ir.ir_length) { - struct buffer_head *m_bh; - u64 x, y; - - error = gfs2_meta_inode_buffer(m_ip, &m_bh); - if (error) - goto out_brelse; - - x = *(u64 *)(m_bh->b_data + sizeof(struct gfs2_dinode)); - x = y = be64_to_cpu(x); - ir.ir_start = x; - ir.ir_length = GFS2_INUM_QUANTUM; - x += GFS2_INUM_QUANTUM; - if (x < y) - gfs2_consist_inode(m_ip); - x = cpu_to_be64(x); - gfs2_trans_add_bh(m_ip->i_gl, m_bh, 1); - *(u64 *)(m_bh->b_data + sizeof(struct gfs2_dinode)) = x; - - brelse(m_bh); - } - - *formal_ino = ir.ir_start++; - ir.ir_length--; - - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_inum_range_out(&ir, bh->b_data + sizeof(struct gfs2_dinode)); - -out_brelse: - brelse(bh); -out_end_trans: - mutex_unlock(&sdp->sd_inum_mutex); - gfs2_trans_end(sdp); -out: - gfs2_glock_dq_uninit(&gh); - return error; -} - -static int pick_formal_ino(struct gfs2_sbd *sdp, u64 *inum) -{ - int error; - - error = pick_formal_ino_1(sdp, inum); - if (error <= 0) - return error; - - error = pick_formal_ino_2(sdp, inum); - - return error; -} - -/** - * create_ok - OK to create a new on-disk inode here? - * @dip: Directory in which dinode is to be created - * @name: Name of new dinode - * @mode: - * - * Returns: errno - */ - -static int create_ok(struct gfs2_inode *dip, const struct qstr *name, - unsigned int mode) -{ - int error; - - error = permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, NULL); - if (error) - return error; - - /* Don't create entries in an unlinked directory */ - if (!dip->i_di.di_nlink) - return -EPERM; - - error = gfs2_dir_search(&dip->i_inode, name, NULL, NULL); - switch (error) { - case -ENOENT: - error = 0; - break; - case 0: - return -EEXIST; - default: - return error; - } - - if (dip->i_di.di_entries == (u32)-1) - return -EFBIG; - if (S_ISDIR(mode) && dip->i_di.di_nlink == (u32)-1) - return -EMLINK; - - return 0; -} - -static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, - unsigned int *uid, unsigned int *gid) -{ - if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && - (dip->i_di.di_mode & S_ISUID) && dip->i_di.di_uid) { - if (S_ISDIR(*mode)) - *mode |= S_ISUID; - else if (dip->i_di.di_uid != current->fsuid) - *mode &= ~07111; - *uid = dip->i_di.di_uid; - } else - *uid = current->fsuid; - - if (dip->i_di.di_mode & S_ISGID) { - if (S_ISDIR(*mode)) - *mode |= S_ISGID; - *gid = dip->i_di.di_gid; - } else - *gid = current->fsgid; -} - -static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum *inum, - u64 *generation) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - int error; - - gfs2_alloc_get(dip); - - dip->i_alloc.al_requested = RES_DINODE; - error = gfs2_inplace_reserve(dip); - if (error) - goto out; - - error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS, 0); - if (error) - goto out_ipreserv; - - inum->no_addr = gfs2_alloc_di(dip, generation); - - gfs2_trans_end(sdp); - -out_ipreserv: - gfs2_inplace_release(dip); -out: - gfs2_alloc_put(dip); - return error; -} - -/** - * init_dinode - Fill in a new dinode structure - * @dip: the directory this inode is being created in - * @gl: The glock covering the new inode - * @inum: the inode number - * @mode: the file permissions - * @uid: - * @gid: - * - */ - -static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - const struct gfs2_inum *inum, unsigned int mode, - unsigned int uid, unsigned int gid, - const u64 *generation) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_dinode *di; - struct buffer_head *dibh; - - dibh = gfs2_meta_new(gl, inum->no_addr); - gfs2_trans_add_bh(gl, dibh, 1); - gfs2_metatype_set(dibh, GFS2_METATYPE_DI, GFS2_FORMAT_DI); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - di = (struct gfs2_dinode *)dibh->b_data; - - di->di_num.no_formal_ino = cpu_to_be64(inum->no_formal_ino); - di->di_num.no_addr = cpu_to_be64(inum->no_addr); - di->di_mode = cpu_to_be32(mode); - di->di_uid = cpu_to_be32(uid); - di->di_gid = cpu_to_be32(gid); - di->di_nlink = cpu_to_be32(0); - di->di_size = cpu_to_be64(0); - di->di_blocks = cpu_to_be64(1); - di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(get_seconds()); - di->di_major = di->di_minor = cpu_to_be32(0); - di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); - di->di_generation = cpu_to_be64(*generation); - di->di_flags = cpu_to_be32(0); - - if (S_ISREG(mode)) { - if ((dip->i_di.di_flags & GFS2_DIF_INHERIT_JDATA) || - gfs2_tune_get(sdp, gt_new_files_jdata)) - di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA); - if ((dip->i_di.di_flags & GFS2_DIF_INHERIT_DIRECTIO) || - gfs2_tune_get(sdp, gt_new_files_directio)) - di->di_flags |= cpu_to_be32(GFS2_DIF_DIRECTIO); - } else if (S_ISDIR(mode)) { - di->di_flags |= cpu_to_be32(dip->i_di.di_flags & - GFS2_DIF_INHERIT_DIRECTIO); - di->di_flags |= cpu_to_be32(dip->i_di.di_flags & - GFS2_DIF_INHERIT_JDATA); - } - - di->__pad1 = 0; - di->di_payload_format = cpu_to_be32(0); - di->di_height = cpu_to_be32(0); - di->__pad2 = 0; - di->__pad3 = 0; - di->di_depth = cpu_to_be16(0); - di->di_entries = cpu_to_be32(0); - memset(&di->__pad4, 0, sizeof(di->__pad4)); - di->di_eattr = cpu_to_be64(0); - memset(&di->di_reserved, 0, sizeof(di->di_reserved)); - - brelse(dibh); -} - -static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - unsigned int mode, const struct gfs2_inum *inum, - const u64 *generation) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - unsigned int uid, gid; - int error; - - munge_mode_uid_gid(dip, &mode, &uid, &gid); - gfs2_alloc_get(dip); - - error = gfs2_quota_lock(dip, uid, gid); - if (error) - goto out; - - error = gfs2_quota_check(dip, uid, gid); - if (error) - goto out_quota; - - error = gfs2_trans_begin(sdp, RES_DINODE + RES_QUOTA, 0); - if (error) - goto out_quota; - - init_dinode(dip, gl, inum, mode, uid, gid, generation); - gfs2_quota_change(dip, +1, uid, gid); - gfs2_trans_end(sdp); - -out_quota: - gfs2_quota_unlock(dip); -out: - gfs2_alloc_put(dip); - return error; -} - -static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_alloc *al; - int alloc_required; - struct buffer_head *dibh; - int error; - - al = gfs2_alloc_get(dip); - - error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto fail; - - error = alloc_required = gfs2_diradd_alloc_required(&dip->i_inode, name); - if (alloc_required < 0) - goto fail; - if (alloc_required) { - error = gfs2_quota_check(dip, dip->i_di.di_uid, - dip->i_di.di_gid); - if (error) - goto fail_quota_locks; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve(dip); - if (error) - goto fail_quota_locks; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_ri.ri_length + - 2 * RES_DINODE + - RES_STATFS + RES_QUOTA, 0); - if (error) - goto fail_ipreserv; - } else { - error = gfs2_trans_begin(sdp, RES_LEAF + 2 * RES_DINODE, 0); - if (error) - goto fail_quota_locks; - } - - error = gfs2_dir_add(&dip->i_inode, name, &ip->i_num, IF2DT(ip->i_di.di_mode)); - if (error) - goto fail_end_trans; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto fail_end_trans; - ip->i_di.di_nlink = 1; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - return 0; - -fail_end_trans: - gfs2_trans_end(sdp); - -fail_ipreserv: - if (dip->i_alloc.al_rgd) - gfs2_inplace_release(dip); - -fail_quota_locks: - gfs2_quota_unlock(dip); - -fail: - gfs2_alloc_put(dip); - return error; -} - -static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip) -{ - int err; - size_t len; - void *value; - char *name; - struct gfs2_ea_request er; - - err = security_inode_init_security(&ip->i_inode, &dip->i_inode, - &name, &value, &len); - - if (err) { - if (err == -EOPNOTSUPP) - return 0; - return err; - } - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - - er.er_type = GFS2_EATYPE_SECURITY; - er.er_name = name; - er.er_data = value; - er.er_name_len = strlen(name); - er.er_data_len = len; - - err = gfs2_ea_set_i(ip, &er); - - kfree(value); - kfree(name); - - return err; -} - -/** - * gfs2_createi - Create a new inode - * @ghs: An array of two holders - * @name: The name of the new file - * @mode: the permissions on the new inode - * - * @ghs[0] is an initialized holder for the directory - * @ghs[1] is the holder for the inode lock - * - * If the return value is not NULL, the glocks on both the directory and the new - * file are held. A transaction has been started and an inplace reservation - * is held, as well. - * - * Returns: An inode - */ - -struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, - unsigned int mode) -{ - struct inode *inode; - struct gfs2_inode *dip = ghs->gh_gl->gl_object; - struct inode *dir = &dip->i_inode; - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_inum inum; - int error; - u64 generation; - - if (!name->len || name->len > GFS2_FNAMESIZE) - return ERR_PTR(-ENAMETOOLONG); - - gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); - error = gfs2_glock_nq(ghs); - if (error) - goto fail; - - error = create_ok(dip, name, mode); - if (error) - goto fail_gunlock; - - error = pick_formal_ino(sdp, &inum.no_formal_ino); - if (error) - goto fail_gunlock; - - error = alloc_dinode(dip, &inum, &generation); - if (error) - goto fail_gunlock; - - if (inum.no_addr < dip->i_num.no_addr) { - gfs2_glock_dq(ghs); - - error = gfs2_glock_nq_num(sdp, inum.no_addr, - &gfs2_inode_glops, LM_ST_EXCLUSIVE, - GL_SKIP, ghs + 1); - if (error) { - return ERR_PTR(error); - } - - gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); - error = gfs2_glock_nq(ghs); - if (error) { - gfs2_glock_dq_uninit(ghs + 1); - return ERR_PTR(error); - } - - error = create_ok(dip, name, mode); - if (error) - goto fail_gunlock2; - } else { - error = gfs2_glock_nq_num(sdp, inum.no_addr, - &gfs2_inode_glops, LM_ST_EXCLUSIVE, - GL_SKIP, ghs + 1); - if (error) - goto fail_gunlock; - } - - error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation); - if (error) - goto fail_gunlock2; - - inode = gfs2_inode_lookup(dir->i_sb, &inum, IF2DT(mode)); - if (IS_ERR(inode)) - goto fail_gunlock2; - - error = gfs2_inode_refresh(GFS2_I(inode)); - if (error) - goto fail_iput; - - error = gfs2_acl_create(dip, GFS2_I(inode)); - if (error) - goto fail_iput; - - error = gfs2_security_init(dip, GFS2_I(inode)); - if (error) - goto fail_iput; - - error = link_dinode(dip, name, GFS2_I(inode)); - if (error) - goto fail_iput; - - if (!inode) - return ERR_PTR(-ENOMEM); - return inode; - -fail_iput: - iput(inode); -fail_gunlock2: - gfs2_glock_dq_uninit(ghs + 1); -fail_gunlock: - gfs2_glock_dq(ghs); -fail: - return ERR_PTR(error); -} - -/** - * gfs2_rmdiri - Remove a directory - * @dip: The parent directory of the directory to be removed - * @name: The name of the directory to be removed - * @ip: The GFS2 inode of the directory to be removed - * - * Assumes Glocks on dip and ip are held - * - * Returns: errno - */ - -int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) -{ - struct qstr dotname; - int error; - - if (ip->i_di.di_entries != 2) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(&ip->i_di); - return -EIO; - } - - error = gfs2_dir_del(dip, name); - if (error) - return error; - - error = gfs2_change_nlink(dip, -1); - if (error) - return error; - - gfs2_str2qstr(&dotname, "."); - error = gfs2_dir_del(ip, &dotname); - if (error) - return error; - - gfs2_str2qstr(&dotname, ".."); - error = gfs2_dir_del(ip, &dotname); - if (error) - return error; - - error = gfs2_change_nlink(ip, -2); - if (error) - return error; - - return error; -} - -/* - * gfs2_unlink_ok - check to see that a inode is still in a directory - * @dip: the directory - * @name: the name of the file - * @ip: the inode - * - * Assumes that the lock on (at least) @dip is held. - * - * Returns: 0 if the parent/child relationship is correct, errno if it isn't - */ - -int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip) -{ - struct gfs2_inum inum; - unsigned int type; - int error; - - if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode)) - return -EPERM; - - if ((dip->i_di.di_mode & S_ISVTX) && - dip->i_di.di_uid != current->fsuid && - ip->i_di.di_uid != current->fsuid && !capable(CAP_FOWNER)) - return -EPERM; - - if (IS_APPEND(&dip->i_inode)) - return -EPERM; - - error = permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, NULL); - if (error) - return error; - - error = gfs2_dir_search(&dip->i_inode, name, &inum, &type); - if (error) - return error; - - if (!gfs2_inum_equal(&inum, &ip->i_num)) - return -ENOENT; - - if (IF2DT(ip->i_di.di_mode) != type) { - gfs2_consist_inode(dip); - return -EIO; - } - - return 0; -} - -/* - * gfs2_ok_to_move - check if it's ok to move a directory to another directory - * @this: move this - * @to: to here - * - * Follow @to back to the root and make sure we don't encounter @this - * Assumes we already hold the rename lock. - * - * Returns: errno - */ - -int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) -{ - struct inode *dir = &to->i_inode; - struct super_block *sb = dir->i_sb; - struct inode *tmp; - struct qstr dotdot; - int error = 0; - - gfs2_str2qstr(&dotdot, ".."); - - igrab(dir); - - for (;;) { - if (dir == &this->i_inode) { - error = -EINVAL; - break; - } - if (dir == sb->s_root->d_inode) { - error = 0; - break; - } - - tmp = gfs2_lookupi(dir, &dotdot, 1, NULL); - if (IS_ERR(tmp)) { - error = PTR_ERR(tmp); - break; - } - - iput(dir); - dir = tmp; - } - - iput(dir); - - return error; -} - -/** - * gfs2_readlinki - return the contents of a symlink - * @ip: the symlink's inode - * @buf: a pointer to the buffer to be filled - * @len: a pointer to the length of @buf - * - * If @buf is too small, a piece of memory is kmalloc()ed and needs - * to be freed by the caller. - * - * Returns: errno - */ - -int gfs2_readlinki(struct gfs2_inode *ip, char **buf, unsigned int *len) -{ - struct gfs2_holder i_gh; - struct buffer_head *dibh; - unsigned int x; - int error; - - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &i_gh); - error = gfs2_glock_nq_atime(&i_gh); - if (error) { - gfs2_holder_uninit(&i_gh); - return error; - } - - if (!ip->i_di.di_size) { - gfs2_consist_inode(ip); - error = -EIO; - goto out; - } - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out; - - x = ip->i_di.di_size + 1; - if (x > *len) { - *buf = kmalloc(x, GFP_KERNEL); - if (!*buf) { - error = -ENOMEM; - goto out_brelse; - } - } - - memcpy(*buf, dibh->b_data + sizeof(struct gfs2_dinode), x); - *len = x; - -out_brelse: - brelse(dibh); -out: - gfs2_glock_dq_uninit(&i_gh); - return error; -} - -/** - * gfs2_glock_nq_atime - Acquire a hold on an inode's glock, and - * conditionally update the inode's atime - * @gh: the holder to acquire - * - * Tests atime (access time) for gfs2_read, gfs2_readdir and gfs2_mmap - * Update if the difference between the current time and the inode's current - * atime is greater than an interval specified at mount. - * - * Returns: errno - */ - -int gfs2_glock_nq_atime(struct gfs2_holder *gh) -{ - struct gfs2_glock *gl = gh->gh_gl; - struct gfs2_sbd *sdp = gl->gl_sbd; - struct gfs2_inode *ip = gl->gl_object; - s64 curtime, quantum = gfs2_tune_get(sdp, gt_atime_quantum); - unsigned int state; - int flags; - int error; - - if (gfs2_assert_warn(sdp, gh->gh_flags & GL_ATIME) || - gfs2_assert_warn(sdp, !(gh->gh_flags & GL_ASYNC)) || - gfs2_assert_warn(sdp, gl->gl_ops == &gfs2_inode_glops)) - return -EINVAL; - - state = gh->gh_state; - flags = gh->gh_flags; - - error = gfs2_glock_nq(gh); - if (error) - return error; - - if (test_bit(SDF_NOATIME, &sdp->sd_flags) || - (sdp->sd_vfs->s_flags & MS_RDONLY)) - return 0; - - curtime = get_seconds(); - if (curtime - ip->i_di.di_atime >= quantum) { - gfs2_glock_dq(gh); - gfs2_holder_reinit(LM_ST_EXCLUSIVE, gh->gh_flags & ~LM_FLAG_ANY, - gh); - error = gfs2_glock_nq(gh); - if (error) - return error; - - /* Verify that atime hasn't been updated while we were - trying to get exclusive lock. */ - - curtime = get_seconds(); - if (curtime - ip->i_di.di_atime >= quantum) { - struct buffer_head *dibh; - struct gfs2_dinode *di; - - error = gfs2_trans_begin(sdp, RES_DINODE, 0); - if (error == -EROFS) - return 0; - if (error) - goto fail; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto fail_end_trans; - - ip->i_di.di_atime = curtime; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - di = (struct gfs2_dinode *)dibh->b_data; - di->di_atime = cpu_to_be64(ip->i_di.di_atime); - brelse(dibh); - - gfs2_trans_end(sdp); - } - - /* If someone else has asked for the glock, - unlock and let them have it. Then reacquire - in the original state. */ - if (gfs2_glock_is_blocking(gl)) { - gfs2_glock_dq(gh); - gfs2_holder_reinit(state, flags, gh); - return gfs2_glock_nq(gh); - } - } - - return 0; - -fail_end_trans: - gfs2_trans_end(sdp); -fail: - gfs2_glock_dq(gh); - return error; -} - -/** - * glock_compare_atime - Compare two struct gfs2_glock structures for sort - * @arg_a: the first structure - * @arg_b: the second structure - * - * Returns: 1 if A > B - * -1 if A < B - * 0 if A == B - */ - -static int glock_compare_atime(const void *arg_a, const void *arg_b) -{ - const struct gfs2_holder *gh_a = *(const struct gfs2_holder **)arg_a; - const struct gfs2_holder *gh_b = *(const struct gfs2_holder **)arg_b; - const struct lm_lockname *a = &gh_a->gh_gl->gl_name; - const struct lm_lockname *b = &gh_b->gh_gl->gl_name; - - if (a->ln_number > b->ln_number) - return 1; - if (a->ln_number < b->ln_number) - return -1; - if (gh_a->gh_state == LM_ST_SHARED && gh_b->gh_state == LM_ST_EXCLUSIVE) - return 1; - if (gh_a->gh_state == LM_ST_SHARED && (gh_b->gh_flags & GL_ATIME)) - return 1; - - return 0; -} - -/** - * gfs2_glock_nq_m_atime - acquire multiple glocks where one may need an - * atime update - * @num_gh: the number of structures - * @ghs: an array of struct gfs2_holder structures - * - * Returns: 0 on success (all glocks acquired), - * errno on failure (no glocks acquired) - */ - -int gfs2_glock_nq_m_atime(unsigned int num_gh, struct gfs2_holder *ghs) -{ - struct gfs2_holder **p; - unsigned int x; - int error = 0; - - if (!num_gh) - return 0; - - if (num_gh == 1) { - ghs->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC); - if (ghs->gh_flags & GL_ATIME) - error = gfs2_glock_nq_atime(ghs); - else - error = gfs2_glock_nq(ghs); - return error; - } - - p = kcalloc(num_gh, sizeof(struct gfs2_holder *), GFP_KERNEL); - if (!p) - return -ENOMEM; - - for (x = 0; x < num_gh; x++) - p[x] = &ghs[x]; - - sort(p, num_gh, sizeof(struct gfs2_holder *), glock_compare_atime,NULL); - - for (x = 0; x < num_gh; x++) { - p[x]->gh_flags &= ~(LM_FLAG_TRY | GL_ASYNC); - - if (p[x]->gh_flags & GL_ATIME) - error = gfs2_glock_nq_atime(p[x]); - else - error = gfs2_glock_nq(p[x]); - - if (error) { - while (x--) - gfs2_glock_dq(p[x]); - break; - } - } - - kfree(p); - return error; -} - - -static int -__gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) -{ - struct buffer_head *dibh; - int error; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - error = inode_setattr(&ip->i_inode, attr); - gfs2_assert_warn(GFS2_SB(&ip->i_inode), !error); - gfs2_inode_attr_out(ip); - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - return error; -} - -/** - * gfs2_setattr_simple - - * @ip: - * @attr: - * - * Called with a reference on the vnode. - * - * Returns: errno - */ - -int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) -{ - int error; - - if (current->journal_info) - return __gfs2_setattr_simple(ip, attr); - - error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); - if (error) - return error; - - error = __gfs2_setattr_simple(ip, attr); - gfs2_trans_end(GFS2_SB(&ip->i_inode)); - return error; -} - diff --git a/trunk/fs/gfs2/inode.h b/trunk/fs/gfs2/inode.h deleted file mode 100644 index f5d861760579..000000000000 --- a/trunk/fs/gfs2/inode.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __INODE_DOT_H__ -#define __INODE_DOT_H__ - -static inline int gfs2_is_stuffed(struct gfs2_inode *ip) -{ - return !ip->i_di.di_height; -} - -static inline int gfs2_is_jdata(struct gfs2_inode *ip) -{ - return ip->i_di.di_flags & GFS2_DIF_JDATA; -} - -static inline int gfs2_is_dir(struct gfs2_inode *ip) -{ - return S_ISDIR(ip->i_di.di_mode); -} - -void gfs2_inode_attr_in(struct gfs2_inode *ip); -void gfs2_inode_attr_out(struct gfs2_inode *ip); -struct inode *gfs2_inode_lookup(struct super_block *sb, struct gfs2_inum *inum, unsigned type); -struct inode *gfs2_ilookup(struct super_block *sb, struct gfs2_inum *inum); - -int gfs2_inode_refresh(struct gfs2_inode *ip); - -int gfs2_dinode_dealloc(struct gfs2_inode *inode); -int gfs2_change_nlink(struct gfs2_inode *ip, int diff); -struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, - int is_root, struct nameidata *nd); -struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, - unsigned int mode); -int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip); -int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, - struct gfs2_inode *ip); -int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to); -int gfs2_readlinki(struct gfs2_inode *ip, char **buf, unsigned int *len); - -int gfs2_glock_nq_atime(struct gfs2_holder *gh); -int gfs2_glock_nq_m_atime(unsigned int num_gh, struct gfs2_holder *ghs); - -int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr); - -struct inode *gfs2_lookup_simple(struct inode *dip, const char *name); - -#endif /* __INODE_DOT_H__ */ - diff --git a/trunk/fs/gfs2/lm.c b/trunk/fs/gfs2/lm.c deleted file mode 100644 index effe4a337c1d..000000000000 --- a/trunk/fs/gfs2/lm.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "lm.h" -#include "super.h" -#include "util.h" - -/** - * gfs2_lm_mount - mount a locking protocol - * @sdp: the filesystem - * @args: mount arguements - * @silent: if 1, don't complain if the FS isn't a GFS2 fs - * - * Returns: errno - */ - -int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent) -{ - char *proto = sdp->sd_proto_name; - char *table = sdp->sd_table_name; - int flags = 0; - int error; - - if (sdp->sd_args.ar_spectator) - flags |= LM_MFLAG_SPECTATOR; - - fs_info(sdp, "Trying to join cluster \"%s\", \"%s\"\n", proto, table); - - error = gfs2_mount_lockproto(proto, table, sdp->sd_args.ar_hostdata, - gfs2_glock_cb, sdp, - GFS2_MIN_LVB_SIZE, flags, - &sdp->sd_lockstruct, &sdp->sd_kobj); - if (error) { - fs_info(sdp, "can't mount proto=%s, table=%s, hostdata=%s\n", - proto, table, sdp->sd_args.ar_hostdata); - goto out; - } - - if (gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_lockspace) || - gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_ops) || - gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_lvb_size >= - GFS2_MIN_LVB_SIZE)) { - gfs2_unmount_lockproto(&sdp->sd_lockstruct); - goto out; - } - - if (sdp->sd_args.ar_spectator) - snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s.s", table); - else - snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s.%u", table, - sdp->sd_lockstruct.ls_jid); - - fs_info(sdp, "Joined cluster. Now mounting FS...\n"); - - if ((sdp->sd_lockstruct.ls_flags & LM_LSFLAG_LOCAL) && - !sdp->sd_args.ar_ignore_local_fs) { - sdp->sd_args.ar_localflocks = 1; - sdp->sd_args.ar_localcaching = 1; - } - -out: - return error; -} - -void gfs2_lm_others_may_mount(struct gfs2_sbd *sdp) -{ - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - sdp->sd_lockstruct.ls_ops->lm_others_may_mount( - sdp->sd_lockstruct.ls_lockspace); -} - -void gfs2_lm_unmount(struct gfs2_sbd *sdp) -{ - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - gfs2_unmount_lockproto(&sdp->sd_lockstruct); -} - -int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...) -{ - va_list args; - - if (test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags)) - return 0; - - va_start(args, fmt); - vprintk(fmt, args); - va_end(args); - - fs_err(sdp, "about to withdraw from the cluster\n"); - BUG_ON(sdp->sd_args.ar_debug); - - - fs_err(sdp, "waiting for outstanding I/O\n"); - - /* FIXME: suspend dm device so oustanding bio's complete - and all further io requests fail */ - - fs_err(sdp, "telling LM to withdraw\n"); - gfs2_withdraw_lockproto(&sdp->sd_lockstruct); - fs_err(sdp, "withdrawn\n"); - dump_stack(); - - return -1; -} - -int gfs2_lm_get_lock(struct gfs2_sbd *sdp, struct lm_lockname *name, - void **lockp) -{ - int error = -EIO; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - error = sdp->sd_lockstruct.ls_ops->lm_get_lock( - sdp->sd_lockstruct.ls_lockspace, name, lockp); - return error; -} - -void gfs2_lm_put_lock(struct gfs2_sbd *sdp, void *lock) -{ - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - sdp->sd_lockstruct.ls_ops->lm_put_lock(lock); -} - -unsigned int gfs2_lm_lock(struct gfs2_sbd *sdp, void *lock, - unsigned int cur_state, unsigned int req_state, - unsigned int flags) -{ - int ret = 0; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - ret = sdp->sd_lockstruct.ls_ops->lm_lock(lock, cur_state, - req_state, flags); - return ret; -} - -unsigned int gfs2_lm_unlock(struct gfs2_sbd *sdp, void *lock, - unsigned int cur_state) -{ - int ret = 0; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - ret = sdp->sd_lockstruct.ls_ops->lm_unlock(lock, cur_state); - return ret; -} - -void gfs2_lm_cancel(struct gfs2_sbd *sdp, void *lock) -{ - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - sdp->sd_lockstruct.ls_ops->lm_cancel(lock); -} - -int gfs2_lm_hold_lvb(struct gfs2_sbd *sdp, void *lock, char **lvbp) -{ - int error = -EIO; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - error = sdp->sd_lockstruct.ls_ops->lm_hold_lvb(lock, lvbp); - return error; -} - -void gfs2_lm_unhold_lvb(struct gfs2_sbd *sdp, void *lock, char *lvb) -{ - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - sdp->sd_lockstruct.ls_ops->lm_unhold_lvb(lock, lvb); -} - -int gfs2_lm_plock_get(struct gfs2_sbd *sdp, struct lm_lockname *name, - struct file *file, struct file_lock *fl) -{ - int error = -EIO; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - error = sdp->sd_lockstruct.ls_ops->lm_plock_get( - sdp->sd_lockstruct.ls_lockspace, name, file, fl); - return error; -} - -int gfs2_lm_plock(struct gfs2_sbd *sdp, struct lm_lockname *name, - struct file *file, int cmd, struct file_lock *fl) -{ - int error = -EIO; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - error = sdp->sd_lockstruct.ls_ops->lm_plock( - sdp->sd_lockstruct.ls_lockspace, name, file, cmd, fl); - return error; -} - -int gfs2_lm_punlock(struct gfs2_sbd *sdp, struct lm_lockname *name, - struct file *file, struct file_lock *fl) -{ - int error = -EIO; - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - error = sdp->sd_lockstruct.ls_ops->lm_punlock( - sdp->sd_lockstruct.ls_lockspace, name, file, fl); - return error; -} - -void gfs2_lm_recovery_done(struct gfs2_sbd *sdp, unsigned int jid, - unsigned int message) -{ - if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - sdp->sd_lockstruct.ls_ops->lm_recovery_done( - sdp->sd_lockstruct.ls_lockspace, jid, message); -} - diff --git a/trunk/fs/gfs2/lm.h b/trunk/fs/gfs2/lm.h deleted file mode 100644 index 21cdc30ee08c..000000000000 --- a/trunk/fs/gfs2/lm.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __LM_DOT_H__ -#define __LM_DOT_H__ - -struct gfs2_sbd; - -#define GFS2_MIN_LVB_SIZE 32 - -int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent); -void gfs2_lm_others_may_mount(struct gfs2_sbd *sdp); -void gfs2_lm_unmount(struct gfs2_sbd *sdp); -int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...) - __attribute__ ((format(printf, 2, 3))); -int gfs2_lm_get_lock(struct gfs2_sbd *sdp, struct lm_lockname *name, - void **lockp); -void gfs2_lm_put_lock(struct gfs2_sbd *sdp, void *lock); -unsigned int gfs2_lm_lock(struct gfs2_sbd *sdp, void *lock, - unsigned int cur_state, unsigned int req_state, - unsigned int flags); -unsigned int gfs2_lm_unlock(struct gfs2_sbd *sdp, void *lock, - unsigned int cur_state); -void gfs2_lm_cancel(struct gfs2_sbd *sdp, void *lock); -int gfs2_lm_hold_lvb(struct gfs2_sbd *sdp, void *lock, char **lvbp); -void gfs2_lm_unhold_lvb(struct gfs2_sbd *sdp, void *lock, char *lvb); -int gfs2_lm_plock_get(struct gfs2_sbd *sdp, struct lm_lockname *name, - struct file *file, struct file_lock *fl); -int gfs2_lm_plock(struct gfs2_sbd *sdp, struct lm_lockname *name, - struct file *file, int cmd, struct file_lock *fl); -int gfs2_lm_punlock(struct gfs2_sbd *sdp, struct lm_lockname *name, - struct file *file, struct file_lock *fl); -void gfs2_lm_recovery_done(struct gfs2_sbd *sdp, unsigned int jid, - unsigned int message); - -#endif /* __LM_DOT_H__ */ diff --git a/trunk/fs/gfs2/locking.c b/trunk/fs/gfs2/locking.c deleted file mode 100644 index 663fee728783..000000000000 --- a/trunk/fs/gfs2/locking.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct lmh_wrapper { - struct list_head lw_list; - const struct lm_lockops *lw_ops; -}; - -/* List of registered low-level locking protocols. A file system selects one - of them by name at mount time, e.g. lock_nolock, lock_dlm. */ - -static LIST_HEAD(lmh_list); -static DEFINE_MUTEX(lmh_lock); - -/** - * gfs2_register_lockproto - Register a low-level locking protocol - * @proto: the protocol definition - * - * Returns: 0 on success, -EXXX on failure - */ - -int gfs2_register_lockproto(const struct lm_lockops *proto) -{ - struct lmh_wrapper *lw; - - mutex_lock(&lmh_lock); - - list_for_each_entry(lw, &lmh_list, lw_list) { - if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) { - mutex_unlock(&lmh_lock); - printk(KERN_INFO "GFS2: protocol %s already exists\n", - proto->lm_proto_name); - return -EEXIST; - } - } - - lw = kzalloc(sizeof(struct lmh_wrapper), GFP_KERNEL); - if (!lw) { - mutex_unlock(&lmh_lock); - return -ENOMEM; - } - - lw->lw_ops = proto; - list_add(&lw->lw_list, &lmh_list); - - mutex_unlock(&lmh_lock); - - return 0; -} - -/** - * gfs2_unregister_lockproto - Unregister a low-level locking protocol - * @proto: the protocol definition - * - */ - -void gfs2_unregister_lockproto(const struct lm_lockops *proto) -{ - struct lmh_wrapper *lw; - - mutex_lock(&lmh_lock); - - list_for_each_entry(lw, &lmh_list, lw_list) { - if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) { - list_del(&lw->lw_list); - mutex_unlock(&lmh_lock); - kfree(lw); - return; - } - } - - mutex_unlock(&lmh_lock); - - printk(KERN_WARNING "GFS2: can't unregister lock protocol %s\n", - proto->lm_proto_name); -} - -/** - * gfs2_mount_lockproto - Mount a lock protocol - * @proto_name - the name of the protocol - * @table_name - the name of the lock space - * @host_data - data specific to this host - * @cb - the callback to the code using the lock module - * @sdp - The GFS2 superblock - * @min_lvb_size - the mininum LVB size that the caller can deal with - * @flags - LM_MFLAG_* - * @lockstruct - a structure returned describing the mount - * - * Returns: 0 on success, -EXXX on failure - */ - -int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data, - lm_callback_t cb, void *cb_data, - unsigned int min_lvb_size, int flags, - struct lm_lockstruct *lockstruct, - struct kobject *fskobj) -{ - struct lmh_wrapper *lw = NULL; - int try = 0; - int error, found; - -retry: - mutex_lock(&lmh_lock); - - found = 0; - list_for_each_entry(lw, &lmh_list, lw_list) { - if (!strcmp(lw->lw_ops->lm_proto_name, proto_name)) { - found = 1; - break; - } - } - - if (!found) { - if (!try && capable(CAP_SYS_MODULE)) { - try = 1; - mutex_unlock(&lmh_lock); - request_module(proto_name); - goto retry; - } - printk(KERN_INFO "GFS2: can't find protocol %s\n", proto_name); - error = -ENOENT; - goto out; - } - - if (!try_module_get(lw->lw_ops->lm_owner)) { - try = 0; - mutex_unlock(&lmh_lock); - msleep(1000); - goto retry; - } - - error = lw->lw_ops->lm_mount(table_name, host_data, cb, cb_data, - min_lvb_size, flags, lockstruct, fskobj); - if (error) - module_put(lw->lw_ops->lm_owner); -out: - mutex_unlock(&lmh_lock); - return error; -} - -void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct) -{ - mutex_lock(&lmh_lock); - lockstruct->ls_ops->lm_unmount(lockstruct->ls_lockspace); - if (lockstruct->ls_ops->lm_owner) - module_put(lockstruct->ls_ops->lm_owner); - mutex_unlock(&lmh_lock); -} - -/** - * gfs2_withdraw_lockproto - abnormally unmount a lock module - * @lockstruct: the lockstruct passed into mount - * - */ - -void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct) -{ - mutex_lock(&lmh_lock); - lockstruct->ls_ops->lm_withdraw(lockstruct->ls_lockspace); - if (lockstruct->ls_ops->lm_owner) - module_put(lockstruct->ls_ops->lm_owner); - mutex_unlock(&lmh_lock); -} - -EXPORT_SYMBOL_GPL(gfs2_register_lockproto); -EXPORT_SYMBOL_GPL(gfs2_unregister_lockproto); - diff --git a/trunk/fs/gfs2/locking/dlm/Makefile b/trunk/fs/gfs2/locking/dlm/Makefile deleted file mode 100644 index 89b93b6b45cf..000000000000 --- a/trunk/fs/gfs2/locking/dlm/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_GFS2_FS_LOCKING_DLM) += lock_dlm.o -lock_dlm-y := lock.o main.o mount.o sysfs.o thread.o plock.o - diff --git a/trunk/fs/gfs2/locking/dlm/lock.c b/trunk/fs/gfs2/locking/dlm/lock.c deleted file mode 100644 index b167addf9fd1..000000000000 --- a/trunk/fs/gfs2/locking/dlm/lock.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#include "lock_dlm.h" - -static char junk_lvb[GDLM_LVB_SIZE]; - -static void queue_complete(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - - clear_bit(LFL_ACTIVE, &lp->flags); - - spin_lock(&ls->async_lock); - list_add_tail(&lp->clist, &ls->complete); - spin_unlock(&ls->async_lock); - wake_up(&ls->thread_wait); -} - -static inline void gdlm_ast(void *astarg) -{ - queue_complete(astarg); -} - -static inline void gdlm_bast(void *astarg, int mode) -{ - struct gdlm_lock *lp = astarg; - struct gdlm_ls *ls = lp->ls; - - if (!mode) { - printk(KERN_INFO "lock_dlm: bast mode zero %x,%llx\n", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number); - return; - } - - spin_lock(&ls->async_lock); - if (!lp->bast_mode) { - list_add_tail(&lp->blist, &ls->blocking); - lp->bast_mode = mode; - } else if (lp->bast_mode < mode) - lp->bast_mode = mode; - spin_unlock(&ls->async_lock); - wake_up(&ls->thread_wait); -} - -void gdlm_queue_delayed(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - - spin_lock(&ls->async_lock); - list_add_tail(&lp->delay_list, &ls->delayed); - spin_unlock(&ls->async_lock); -} - -/* convert gfs lock-state to dlm lock-mode */ - -static s16 make_mode(s16 lmstate) -{ - switch (lmstate) { - case LM_ST_UNLOCKED: - return DLM_LOCK_NL; - case LM_ST_EXCLUSIVE: - return DLM_LOCK_EX; - case LM_ST_DEFERRED: - return DLM_LOCK_CW; - case LM_ST_SHARED: - return DLM_LOCK_PR; - } - gdlm_assert(0, "unknown LM state %d", lmstate); - return -1; -} - -/* convert dlm lock-mode to gfs lock-state */ - -s16 gdlm_make_lmstate(s16 dlmmode) -{ - switch (dlmmode) { - case DLM_LOCK_IV: - case DLM_LOCK_NL: - return LM_ST_UNLOCKED; - case DLM_LOCK_EX: - return LM_ST_EXCLUSIVE; - case DLM_LOCK_CW: - return LM_ST_DEFERRED; - case DLM_LOCK_PR: - return LM_ST_SHARED; - } - gdlm_assert(0, "unknown DLM mode %d", dlmmode); - return -1; -} - -/* verify agreement with GFS on the current lock state, NB: DLM_LOCK_NL and - DLM_LOCK_IV are both considered LM_ST_UNLOCKED by GFS. */ - -static void check_cur_state(struct gdlm_lock *lp, unsigned int cur_state) -{ - s16 cur = make_mode(cur_state); - if (lp->cur != DLM_LOCK_IV) - gdlm_assert(lp->cur == cur, "%d, %d", lp->cur, cur); -} - -static inline unsigned int make_flags(struct gdlm_lock *lp, - unsigned int gfs_flags, - s16 cur, s16 req) -{ - unsigned int lkf = 0; - - if (gfs_flags & LM_FLAG_TRY) - lkf |= DLM_LKF_NOQUEUE; - - if (gfs_flags & LM_FLAG_TRY_1CB) { - lkf |= DLM_LKF_NOQUEUE; - lkf |= DLM_LKF_NOQUEUEBAST; - } - - if (gfs_flags & LM_FLAG_PRIORITY) { - lkf |= DLM_LKF_NOORDER; - lkf |= DLM_LKF_HEADQUE; - } - - if (gfs_flags & LM_FLAG_ANY) { - if (req == DLM_LOCK_PR) - lkf |= DLM_LKF_ALTCW; - else if (req == DLM_LOCK_CW) - lkf |= DLM_LKF_ALTPR; - } - - if (lp->lksb.sb_lkid != 0) { - lkf |= DLM_LKF_CONVERT; - - /* Conversion deadlock avoidance by DLM */ - - if (!test_bit(LFL_FORCE_PROMOTE, &lp->flags) && - !(lkf & DLM_LKF_NOQUEUE) && - cur > DLM_LOCK_NL && req > DLM_LOCK_NL && cur != req) - lkf |= DLM_LKF_CONVDEADLK; - } - - if (lp->lvb) - lkf |= DLM_LKF_VALBLK; - - return lkf; -} - -/* make_strname - convert GFS lock numbers to a string */ - -static inline void make_strname(struct lm_lockname *lockname, - struct gdlm_strname *str) -{ - sprintf(str->name, "%8x%16llx", lockname->ln_type, - (unsigned long long)lockname->ln_number); - str->namelen = GDLM_STRNAME_BYTES; -} - -static int gdlm_create_lp(struct gdlm_ls *ls, struct lm_lockname *name, - struct gdlm_lock **lpp) -{ - struct gdlm_lock *lp; - - lp = kzalloc(sizeof(struct gdlm_lock), GFP_KERNEL); - if (!lp) - return -ENOMEM; - - lp->lockname = *name; - lp->ls = ls; - lp->cur = DLM_LOCK_IV; - lp->lvb = NULL; - lp->hold_null = NULL; - init_completion(&lp->ast_wait); - INIT_LIST_HEAD(&lp->clist); - INIT_LIST_HEAD(&lp->blist); - INIT_LIST_HEAD(&lp->delay_list); - - spin_lock(&ls->async_lock); - list_add(&lp->all_list, &ls->all_locks); - ls->all_locks_count++; - spin_unlock(&ls->async_lock); - - *lpp = lp; - return 0; -} - -void gdlm_delete_lp(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - - spin_lock(&ls->async_lock); - if (!list_empty(&lp->clist)) - list_del_init(&lp->clist); - if (!list_empty(&lp->blist)) - list_del_init(&lp->blist); - if (!list_empty(&lp->delay_list)) - list_del_init(&lp->delay_list); - gdlm_assert(!list_empty(&lp->all_list), "%x,%llx", lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number); - list_del_init(&lp->all_list); - ls->all_locks_count--; - spin_unlock(&ls->async_lock); - - kfree(lp); -} - -int gdlm_get_lock(void *lockspace, struct lm_lockname *name, - void **lockp) -{ - struct gdlm_lock *lp; - int error; - - error = gdlm_create_lp(lockspace, name, &lp); - - *lockp = lp; - return error; -} - -void gdlm_put_lock(void *lock) -{ - gdlm_delete_lp(lock); -} - -unsigned int gdlm_do_lock(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - struct gdlm_strname str; - int error, bast = 1; - - /* - * When recovery is in progress, delay lock requests for submission - * once recovery is done. Requests for recovery (NOEXP) and unlocks - * can pass. - */ - - if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) && - !test_bit(LFL_NOBLOCK, &lp->flags) && lp->req != DLM_LOCK_NL) { - gdlm_queue_delayed(lp); - return LM_OUT_ASYNC; - } - - /* - * Submit the actual lock request. - */ - - if (test_bit(LFL_NOBAST, &lp->flags)) - bast = 0; - - make_strname(&lp->lockname, &str); - - set_bit(LFL_ACTIVE, &lp->flags); - - log_debug("lk %x,%llx id %x %d,%d %x", lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, lp->lksb.sb_lkid, - lp->cur, lp->req, lp->lkf); - - error = dlm_lock(ls->dlm_lockspace, lp->req, &lp->lksb, lp->lkf, - str.name, str.namelen, 0, gdlm_ast, lp, - bast ? gdlm_bast : NULL); - - if ((error == -EAGAIN) && (lp->lkf & DLM_LKF_NOQUEUE)) { - lp->lksb.sb_status = -EAGAIN; - queue_complete(lp); - error = 0; - } - - if (error) { - log_debug("%s: gdlm_lock %x,%llx err=%d cur=%d req=%d lkf=%x " - "flags=%lx", ls->fsname, lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, error, - lp->cur, lp->req, lp->lkf, lp->flags); - return LM_OUT_ERROR; - } - return LM_OUT_ASYNC; -} - -static unsigned int gdlm_do_unlock(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - unsigned int lkf = 0; - int error; - - set_bit(LFL_DLM_UNLOCK, &lp->flags); - set_bit(LFL_ACTIVE, &lp->flags); - - if (lp->lvb) - lkf = DLM_LKF_VALBLK; - - log_debug("un %x,%llx %x %d %x", lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, - lp->lksb.sb_lkid, lp->cur, lkf); - - error = dlm_unlock(ls->dlm_lockspace, lp->lksb.sb_lkid, lkf, NULL, lp); - - if (error) { - log_debug("%s: gdlm_unlock %x,%llx err=%d cur=%d req=%d lkf=%x " - "flags=%lx", ls->fsname, lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, error, - lp->cur, lp->req, lp->lkf, lp->flags); - return LM_OUT_ERROR; - } - return LM_OUT_ASYNC; -} - -unsigned int gdlm_lock(void *lock, unsigned int cur_state, - unsigned int req_state, unsigned int flags) -{ - struct gdlm_lock *lp = lock; - - clear_bit(LFL_DLM_CANCEL, &lp->flags); - if (flags & LM_FLAG_NOEXP) - set_bit(LFL_NOBLOCK, &lp->flags); - - check_cur_state(lp, cur_state); - lp->req = make_mode(req_state); - lp->lkf = make_flags(lp, flags, lp->cur, lp->req); - - return gdlm_do_lock(lp); -} - -unsigned int gdlm_unlock(void *lock, unsigned int cur_state) -{ - struct gdlm_lock *lp = lock; - - clear_bit(LFL_DLM_CANCEL, &lp->flags); - if (lp->cur == DLM_LOCK_IV) - return 0; - return gdlm_do_unlock(lp); -} - -void gdlm_cancel(void *lock) -{ - struct gdlm_lock *lp = lock; - struct gdlm_ls *ls = lp->ls; - int error, delay_list = 0; - - if (test_bit(LFL_DLM_CANCEL, &lp->flags)) - return; - - log_info("gdlm_cancel %x,%llx flags %lx", lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, lp->flags); - - spin_lock(&ls->async_lock); - if (!list_empty(&lp->delay_list)) { - list_del_init(&lp->delay_list); - delay_list = 1; - } - spin_unlock(&ls->async_lock); - - if (delay_list) { - set_bit(LFL_CANCEL, &lp->flags); - set_bit(LFL_ACTIVE, &lp->flags); - queue_complete(lp); - return; - } - - if (!test_bit(LFL_ACTIVE, &lp->flags) || - test_bit(LFL_DLM_UNLOCK, &lp->flags)) { - log_info("gdlm_cancel skip %x,%llx flags %lx", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, lp->flags); - return; - } - - /* the lock is blocked in the dlm */ - - set_bit(LFL_DLM_CANCEL, &lp->flags); - set_bit(LFL_ACTIVE, &lp->flags); - - error = dlm_unlock(ls->dlm_lockspace, lp->lksb.sb_lkid, DLM_LKF_CANCEL, - NULL, lp); - - log_info("gdlm_cancel rv %d %x,%llx flags %lx", error, - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, lp->flags); - - if (error == -EBUSY) - clear_bit(LFL_DLM_CANCEL, &lp->flags); -} - -static int gdlm_add_lvb(struct gdlm_lock *lp) -{ - char *lvb; - - lvb = kzalloc(GDLM_LVB_SIZE, GFP_KERNEL); - if (!lvb) - return -ENOMEM; - - lp->lksb.sb_lvbptr = lvb; - lp->lvb = lvb; - return 0; -} - -static void gdlm_del_lvb(struct gdlm_lock *lp) -{ - kfree(lp->lvb); - lp->lvb = NULL; - lp->lksb.sb_lvbptr = NULL; -} - -/* This can do a synchronous dlm request (requiring a lock_dlm thread to get - the completion) because gfs won't call hold_lvb() during a callback (from - the context of a lock_dlm thread). */ - -static int hold_null_lock(struct gdlm_lock *lp) -{ - struct gdlm_lock *lpn = NULL; - int error; - - if (lp->hold_null) { - printk(KERN_INFO "lock_dlm: lvb already held\n"); - return 0; - } - - error = gdlm_create_lp(lp->ls, &lp->lockname, &lpn); - if (error) - goto out; - - lpn->lksb.sb_lvbptr = junk_lvb; - lpn->lvb = junk_lvb; - - lpn->req = DLM_LOCK_NL; - lpn->lkf = DLM_LKF_VALBLK | DLM_LKF_EXPEDITE; - set_bit(LFL_NOBAST, &lpn->flags); - set_bit(LFL_INLOCK, &lpn->flags); - - init_completion(&lpn->ast_wait); - gdlm_do_lock(lpn); - wait_for_completion(&lpn->ast_wait); - error = lpn->lksb.sb_status; - if (error) { - printk(KERN_INFO "lock_dlm: hold_null_lock dlm error %d\n", - error); - gdlm_delete_lp(lpn); - lpn = NULL; - } -out: - lp->hold_null = lpn; - return error; -} - -/* This cannot do a synchronous dlm request (requiring a lock_dlm thread to get - the completion) because gfs may call unhold_lvb() during a callback (from - the context of a lock_dlm thread) which could cause a deadlock since the - other lock_dlm thread could be engaged in recovery. */ - -static void unhold_null_lock(struct gdlm_lock *lp) -{ - struct gdlm_lock *lpn = lp->hold_null; - - gdlm_assert(lpn, "%x,%llx", lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number); - lpn->lksb.sb_lvbptr = NULL; - lpn->lvb = NULL; - set_bit(LFL_UNLOCK_DELETE, &lpn->flags); - gdlm_do_unlock(lpn); - lp->hold_null = NULL; -} - -/* Acquire a NL lock because gfs requires the value block to remain - intact on the resource while the lvb is "held" even if it's holding no locks - on the resource. */ - -int gdlm_hold_lvb(void *lock, char **lvbp) -{ - struct gdlm_lock *lp = lock; - int error; - - error = gdlm_add_lvb(lp); - if (error) - return error; - - *lvbp = lp->lvb; - - error = hold_null_lock(lp); - if (error) - gdlm_del_lvb(lp); - - return error; -} - -void gdlm_unhold_lvb(void *lock, char *lvb) -{ - struct gdlm_lock *lp = lock; - - unhold_null_lock(lp); - gdlm_del_lvb(lp); -} - -void gdlm_submit_delayed(struct gdlm_ls *ls) -{ - struct gdlm_lock *lp, *safe; - - spin_lock(&ls->async_lock); - list_for_each_entry_safe(lp, safe, &ls->delayed, delay_list) { - list_del_init(&lp->delay_list); - list_add_tail(&lp->delay_list, &ls->submit); - } - spin_unlock(&ls->async_lock); - wake_up(&ls->thread_wait); -} - -int gdlm_release_all_locks(struct gdlm_ls *ls) -{ - struct gdlm_lock *lp, *safe; - int count = 0; - - spin_lock(&ls->async_lock); - list_for_each_entry_safe(lp, safe, &ls->all_locks, all_list) { - list_del_init(&lp->all_list); - - if (lp->lvb && lp->lvb != junk_lvb) - kfree(lp->lvb); - kfree(lp); - count++; - } - spin_unlock(&ls->async_lock); - - return count; -} - diff --git a/trunk/fs/gfs2/locking/dlm/lock_dlm.h b/trunk/fs/gfs2/locking/dlm/lock_dlm.h deleted file mode 100644 index 33af707a4d3f..000000000000 --- a/trunk/fs/gfs2/locking/dlm/lock_dlm.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#ifndef LOCK_DLM_DOT_H -#define LOCK_DLM_DOT_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * Internally, we prefix things with gdlm_ and GDLM_ (for gfs-dlm) since a - * prefix of lock_dlm_ gets awkward. Externally, GFS refers to this module - * as "lock_dlm". - */ - -#define GDLM_STRNAME_BYTES 24 -#define GDLM_LVB_SIZE 32 -#define GDLM_DROP_COUNT 50000 -#define GDLM_DROP_PERIOD 60 -#define GDLM_NAME_LEN 128 - -/* GFS uses 12 bytes to identify a resource (32 bit type + 64 bit number). - We sprintf these numbers into a 24 byte string of hex values to make them - human-readable (to make debugging simpler.) */ - -struct gdlm_strname { - unsigned char name[GDLM_STRNAME_BYTES]; - unsigned short namelen; -}; - -enum { - DFL_BLOCK_LOCKS = 0, - DFL_SPECTATOR = 1, - DFL_WITHDRAW = 2, -}; - -struct gdlm_ls { - u32 id; - int jid; - int first; - int first_done; - unsigned long flags; - struct kobject kobj; - char clustername[GDLM_NAME_LEN]; - char fsname[GDLM_NAME_LEN]; - int fsflags; - dlm_lockspace_t *dlm_lockspace; - lm_callback_t fscb; - struct gfs2_sbd *sdp; - int recover_jid; - int recover_jid_done; - int recover_jid_status; - spinlock_t async_lock; - struct list_head complete; - struct list_head blocking; - struct list_head delayed; - struct list_head submit; - struct list_head all_locks; - u32 all_locks_count; - wait_queue_head_t wait_control; - struct task_struct *thread1; - struct task_struct *thread2; - wait_queue_head_t thread_wait; - unsigned long drop_time; - int drop_locks_count; - int drop_locks_period; -}; - -enum { - LFL_NOBLOCK = 0, - LFL_NOCACHE = 1, - LFL_DLM_UNLOCK = 2, - LFL_DLM_CANCEL = 3, - LFL_SYNC_LVB = 4, - LFL_FORCE_PROMOTE = 5, - LFL_REREQUEST = 6, - LFL_ACTIVE = 7, - LFL_INLOCK = 8, - LFL_CANCEL = 9, - LFL_NOBAST = 10, - LFL_HEADQUE = 11, - LFL_UNLOCK_DELETE = 12, -}; - -struct gdlm_lock { - struct gdlm_ls *ls; - struct lm_lockname lockname; - char *lvb; - struct dlm_lksb lksb; - - s16 cur; - s16 req; - s16 prev_req; - u32 lkf; /* dlm flags DLM_LKF_ */ - unsigned long flags; /* lock_dlm flags LFL_ */ - - int bast_mode; /* protected by async_lock */ - struct completion ast_wait; - - struct list_head clist; /* complete */ - struct list_head blist; /* blocking */ - struct list_head delay_list; /* delayed */ - struct list_head all_list; /* all locks for the fs */ - struct gdlm_lock *hold_null; /* NL lock for hold_lvb */ -}; - -#define gdlm_assert(assertion, fmt, args...) \ -do { \ - if (unlikely(!(assertion))) { \ - printk(KERN_EMERG "lock_dlm: fatal assertion failed \"%s\"\n" \ - "lock_dlm: " fmt "\n", \ - #assertion, ##args); \ - BUG(); \ - } \ -} while (0) - -#define log_print(lev, fmt, arg...) printk(lev "lock_dlm: " fmt "\n" , ## arg) -#define log_info(fmt, arg...) log_print(KERN_INFO , fmt , ## arg) -#define log_error(fmt, arg...) log_print(KERN_ERR , fmt , ## arg) -#ifdef LOCK_DLM_LOG_DEBUG -#define log_debug(fmt, arg...) log_print(KERN_DEBUG , fmt , ## arg) -#else -#define log_debug(fmt, arg...) -#endif - -/* sysfs.c */ - -int gdlm_sysfs_init(void); -void gdlm_sysfs_exit(void); -int gdlm_kobject_setup(struct gdlm_ls *, struct kobject *); -void gdlm_kobject_release(struct gdlm_ls *); - -/* thread.c */ - -int gdlm_init_threads(struct gdlm_ls *); -void gdlm_release_threads(struct gdlm_ls *); - -/* lock.c */ - -s16 gdlm_make_lmstate(s16); -void gdlm_queue_delayed(struct gdlm_lock *); -void gdlm_submit_delayed(struct gdlm_ls *); -int gdlm_release_all_locks(struct gdlm_ls *); -void gdlm_delete_lp(struct gdlm_lock *); -unsigned int gdlm_do_lock(struct gdlm_lock *); - -int gdlm_get_lock(void *, struct lm_lockname *, void **); -void gdlm_put_lock(void *); -unsigned int gdlm_lock(void *, unsigned int, unsigned int, unsigned int); -unsigned int gdlm_unlock(void *, unsigned int); -void gdlm_cancel(void *); -int gdlm_hold_lvb(void *, char **); -void gdlm_unhold_lvb(void *, char *); - -/* plock.c */ - -int gdlm_plock_init(void); -void gdlm_plock_exit(void); -int gdlm_plock(void *, struct lm_lockname *, struct file *, int, - struct file_lock *); -int gdlm_plock_get(void *, struct lm_lockname *, struct file *, - struct file_lock *); -int gdlm_punlock(void *, struct lm_lockname *, struct file *, - struct file_lock *); -#endif - diff --git a/trunk/fs/gfs2/locking/dlm/main.c b/trunk/fs/gfs2/locking/dlm/main.c deleted file mode 100644 index 2194b1d5b5ec..000000000000 --- a/trunk/fs/gfs2/locking/dlm/main.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#include - -#include "lock_dlm.h" - -extern int gdlm_drop_count; -extern int gdlm_drop_period; - -extern struct lm_lockops gdlm_ops; - -static int __init init_lock_dlm(void) -{ - int error; - - error = gfs2_register_lockproto(&gdlm_ops); - if (error) { - printk(KERN_WARNING "lock_dlm: can't register protocol: %d\n", - error); - return error; - } - - error = gdlm_sysfs_init(); - if (error) { - gfs2_unregister_lockproto(&gdlm_ops); - return error; - } - - error = gdlm_plock_init(); - if (error) { - gdlm_sysfs_exit(); - gfs2_unregister_lockproto(&gdlm_ops); - return error; - } - - gdlm_drop_count = GDLM_DROP_COUNT; - gdlm_drop_period = GDLM_DROP_PERIOD; - - printk(KERN_INFO - "Lock_DLM (built %s %s) installed\n", __DATE__, __TIME__); - return 0; -} - -static void __exit exit_lock_dlm(void) -{ - gdlm_plock_exit(); - gdlm_sysfs_exit(); - gfs2_unregister_lockproto(&gdlm_ops); -} - -module_init(init_lock_dlm); -module_exit(exit_lock_dlm); - -MODULE_DESCRIPTION("GFS DLM Locking Module"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - diff --git a/trunk/fs/gfs2/locking/dlm/mount.c b/trunk/fs/gfs2/locking/dlm/mount.c deleted file mode 100644 index 1f94dd35a943..000000000000 --- a/trunk/fs/gfs2/locking/dlm/mount.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#include "lock_dlm.h" - -int gdlm_drop_count; -int gdlm_drop_period; -const struct lm_lockops gdlm_ops; - - -static struct gdlm_ls *init_gdlm(lm_callback_t cb, struct gfs2_sbd *sdp, - int flags, char *table_name) -{ - struct gdlm_ls *ls; - char buf[256], *p; - - ls = kzalloc(sizeof(struct gdlm_ls), GFP_KERNEL); - if (!ls) - return NULL; - - ls->drop_locks_count = gdlm_drop_count; - ls->drop_locks_period = gdlm_drop_period; - ls->fscb = cb; - ls->sdp = sdp; - ls->fsflags = flags; - spin_lock_init(&ls->async_lock); - INIT_LIST_HEAD(&ls->complete); - INIT_LIST_HEAD(&ls->blocking); - INIT_LIST_HEAD(&ls->delayed); - INIT_LIST_HEAD(&ls->submit); - INIT_LIST_HEAD(&ls->all_locks); - init_waitqueue_head(&ls->thread_wait); - init_waitqueue_head(&ls->wait_control); - ls->thread1 = NULL; - ls->thread2 = NULL; - ls->drop_time = jiffies; - ls->jid = -1; - - strncpy(buf, table_name, 256); - buf[255] = '\0'; - - p = strstr(buf, ":"); - if (!p) { - log_info("invalid table_name \"%s\"", table_name); - kfree(ls); - return NULL; - } - *p = '\0'; - p++; - - strncpy(ls->clustername, buf, GDLM_NAME_LEN); - strncpy(ls->fsname, p, GDLM_NAME_LEN); - - return ls; -} - -static int make_args(struct gdlm_ls *ls, char *data_arg, int *nodir) -{ - char data[256]; - char *options, *x, *y; - int error = 0; - - memset(data, 0, 256); - strncpy(data, data_arg, 255); - - for (options = data; (x = strsep(&options, ":")); ) { - if (!*x) - continue; - - y = strchr(x, '='); - if (y) - *y++ = 0; - - if (!strcmp(x, "jid")) { - if (!y) { - log_error("need argument to jid"); - error = -EINVAL; - break; - } - sscanf(y, "%u", &ls->jid); - - } else if (!strcmp(x, "first")) { - if (!y) { - log_error("need argument to first"); - error = -EINVAL; - break; - } - sscanf(y, "%u", &ls->first); - - } else if (!strcmp(x, "id")) { - if (!y) { - log_error("need argument to id"); - error = -EINVAL; - break; - } - sscanf(y, "%u", &ls->id); - - } else if (!strcmp(x, "nodir")) { - if (!y) { - log_error("need argument to nodir"); - error = -EINVAL; - break; - } - sscanf(y, "%u", nodir); - - } else { - log_error("unkonwn option: %s", x); - error = -EINVAL; - break; - } - } - - return error; -} - -static int gdlm_mount(char *table_name, char *host_data, - lm_callback_t cb, void *cb_data, - unsigned int min_lvb_size, int flags, - struct lm_lockstruct *lockstruct, - struct kobject *fskobj) -{ - struct gdlm_ls *ls; - int error = -ENOMEM, nodir = 0; - - if (min_lvb_size > GDLM_LVB_SIZE) - goto out; - - ls = init_gdlm(cb, cb_data, flags, table_name); - if (!ls) - goto out; - - error = make_args(ls, host_data, &nodir); - if (error) - goto out; - - error = gdlm_init_threads(ls); - if (error) - goto out_free; - - error = gdlm_kobject_setup(ls, fskobj); - if (error) - goto out_thread; - - error = dlm_new_lockspace(ls->fsname, strlen(ls->fsname), - &ls->dlm_lockspace, - nodir ? DLM_LSFL_NODIR : 0, - GDLM_LVB_SIZE); - if (error) { - log_error("dlm_new_lockspace error %d", error); - goto out_kobj; - } - - lockstruct->ls_jid = ls->jid; - lockstruct->ls_first = ls->first; - lockstruct->ls_lockspace = ls; - lockstruct->ls_ops = &gdlm_ops; - lockstruct->ls_flags = 0; - lockstruct->ls_lvb_size = GDLM_LVB_SIZE; - return 0; - -out_kobj: - gdlm_kobject_release(ls); -out_thread: - gdlm_release_threads(ls); -out_free: - kfree(ls); -out: - return error; -} - -static void gdlm_unmount(void *lockspace) -{ - struct gdlm_ls *ls = lockspace; - int rv; - - log_debug("unmount flags %lx", ls->flags); - - /* FIXME: serialize unmount and withdraw in case they - happen at once. Also, if unmount follows withdraw, - wait for withdraw to finish. */ - - if (test_bit(DFL_WITHDRAW, &ls->flags)) - goto out; - - gdlm_kobject_release(ls); - dlm_release_lockspace(ls->dlm_lockspace, 2); - gdlm_release_threads(ls); - rv = gdlm_release_all_locks(ls); - if (rv) - log_info("gdlm_unmount: %d stray locks freed", rv); -out: - kfree(ls); -} - -static void gdlm_recovery_done(void *lockspace, unsigned int jid, - unsigned int message) -{ - struct gdlm_ls *ls = lockspace; - ls->recover_jid_done = jid; - ls->recover_jid_status = message; - kobject_uevent(&ls->kobj, KOBJ_CHANGE); -} - -static void gdlm_others_may_mount(void *lockspace) -{ - struct gdlm_ls *ls = lockspace; - ls->first_done = 1; - kobject_uevent(&ls->kobj, KOBJ_CHANGE); -} - -/* Userspace gets the offline uevent, blocks new gfs locks on - other mounters, and lets us know (sets WITHDRAW flag). Then, - userspace leaves the mount group while we leave the lockspace. */ - -static void gdlm_withdraw(void *lockspace) -{ - struct gdlm_ls *ls = lockspace; - - kobject_uevent(&ls->kobj, KOBJ_OFFLINE); - - wait_event_interruptible(ls->wait_control, - test_bit(DFL_WITHDRAW, &ls->flags)); - - dlm_release_lockspace(ls->dlm_lockspace, 2); - gdlm_release_threads(ls); - gdlm_release_all_locks(ls); - gdlm_kobject_release(ls); -} - -const struct lm_lockops gdlm_ops = { - .lm_proto_name = "lock_dlm", - .lm_mount = gdlm_mount, - .lm_others_may_mount = gdlm_others_may_mount, - .lm_unmount = gdlm_unmount, - .lm_withdraw = gdlm_withdraw, - .lm_get_lock = gdlm_get_lock, - .lm_put_lock = gdlm_put_lock, - .lm_lock = gdlm_lock, - .lm_unlock = gdlm_unlock, - .lm_plock = gdlm_plock, - .lm_punlock = gdlm_punlock, - .lm_plock_get = gdlm_plock_get, - .lm_cancel = gdlm_cancel, - .lm_hold_lvb = gdlm_hold_lvb, - .lm_unhold_lvb = gdlm_unhold_lvb, - .lm_recovery_done = gdlm_recovery_done, - .lm_owner = THIS_MODULE, -}; - diff --git a/trunk/fs/gfs2/locking/dlm/plock.c b/trunk/fs/gfs2/locking/dlm/plock.c deleted file mode 100644 index 7365aec9511b..000000000000 --- a/trunk/fs/gfs2/locking/dlm/plock.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2005 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 version 2. - */ - -#include -#include - -#include "lock_dlm.h" - - -static spinlock_t ops_lock; -static struct list_head send_list; -static struct list_head recv_list; -static wait_queue_head_t send_wq; -static wait_queue_head_t recv_wq; - -struct plock_op { - struct list_head list; - int done; - struct gdlm_plock_info info; -}; - -static inline void set_version(struct gdlm_plock_info *info) -{ - info->version[0] = GDLM_PLOCK_VERSION_MAJOR; - info->version[1] = GDLM_PLOCK_VERSION_MINOR; - info->version[2] = GDLM_PLOCK_VERSION_PATCH; -} - -static int check_version(struct gdlm_plock_info *info) -{ - if ((GDLM_PLOCK_VERSION_MAJOR != info->version[0]) || - (GDLM_PLOCK_VERSION_MINOR < info->version[1])) { - log_error("plock device version mismatch: " - "kernel (%u.%u.%u), user (%u.%u.%u)", - GDLM_PLOCK_VERSION_MAJOR, - GDLM_PLOCK_VERSION_MINOR, - GDLM_PLOCK_VERSION_PATCH, - info->version[0], - info->version[1], - info->version[2]); - return -EINVAL; - } - return 0; -} - -static void send_op(struct plock_op *op) -{ - set_version(&op->info); - INIT_LIST_HEAD(&op->list); - spin_lock(&ops_lock); - list_add_tail(&op->list, &send_list); - spin_unlock(&ops_lock); - wake_up(&send_wq); -} - -int gdlm_plock(void *lockspace, struct lm_lockname *name, - struct file *file, int cmd, struct file_lock *fl) -{ - struct gdlm_ls *ls = lockspace; - struct plock_op *op; - int rv; - - op = kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) - return -ENOMEM; - - op->info.optype = GDLM_PLOCK_OP_LOCK; - op->info.pid = fl->fl_pid; - op->info.ex = (fl->fl_type == F_WRLCK); - op->info.wait = IS_SETLKW(cmd); - op->info.fsid = ls->id; - op->info.number = name->ln_number; - op->info.start = fl->fl_start; - op->info.end = fl->fl_end; - op->info.owner = (__u64)(long) fl->fl_owner; - - send_op(op); - wait_event(recv_wq, (op->done != 0)); - - spin_lock(&ops_lock); - if (!list_empty(&op->list)) { - printk(KERN_INFO "plock op on list\n"); - list_del(&op->list); - } - spin_unlock(&ops_lock); - - rv = op->info.rv; - - if (!rv) { - if (posix_lock_file_wait(file, fl) < 0) - log_error("gdlm_plock: vfs lock error %x,%llx", - name->ln_type, - (unsigned long long)name->ln_number); - } - - kfree(op); - return rv; -} - -int gdlm_punlock(void *lockspace, struct lm_lockname *name, - struct file *file, struct file_lock *fl) -{ - struct gdlm_ls *ls = lockspace; - struct plock_op *op; - int rv; - - op = kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) - return -ENOMEM; - - if (posix_lock_file_wait(file, fl) < 0) - log_error("gdlm_punlock: vfs unlock error %x,%llx", - name->ln_type, (unsigned long long)name->ln_number); - - op->info.optype = GDLM_PLOCK_OP_UNLOCK; - op->info.pid = fl->fl_pid; - op->info.fsid = ls->id; - op->info.number = name->ln_number; - op->info.start = fl->fl_start; - op->info.end = fl->fl_end; - op->info.owner = (__u64)(long) fl->fl_owner; - - send_op(op); - wait_event(recv_wq, (op->done != 0)); - - spin_lock(&ops_lock); - if (!list_empty(&op->list)) { - printk(KERN_INFO "punlock op on list\n"); - list_del(&op->list); - } - spin_unlock(&ops_lock); - - rv = op->info.rv; - - kfree(op); - return rv; -} - -int gdlm_plock_get(void *lockspace, struct lm_lockname *name, - struct file *file, struct file_lock *fl) -{ - struct gdlm_ls *ls = lockspace; - struct plock_op *op; - int rv; - - op = kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) - return -ENOMEM; - - op->info.optype = GDLM_PLOCK_OP_GET; - op->info.pid = fl->fl_pid; - op->info.ex = (fl->fl_type == F_WRLCK); - op->info.fsid = ls->id; - op->info.number = name->ln_number; - op->info.start = fl->fl_start; - op->info.end = fl->fl_end; - - send_op(op); - wait_event(recv_wq, (op->done != 0)); - - spin_lock(&ops_lock); - if (!list_empty(&op->list)) { - printk(KERN_INFO "plock_get op on list\n"); - list_del(&op->list); - } - spin_unlock(&ops_lock); - - rv = op->info.rv; - - if (rv == 0) - fl->fl_type = F_UNLCK; - else if (rv > 0) { - fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; - fl->fl_pid = op->info.pid; - fl->fl_start = op->info.start; - fl->fl_end = op->info.end; - } - - kfree(op); - return rv; -} - -/* a read copies out one plock request from the send list */ -static ssize_t dev_read(struct file *file, char __user *u, size_t count, - loff_t *ppos) -{ - struct gdlm_plock_info info; - struct plock_op *op = NULL; - - if (count < sizeof(info)) - return -EINVAL; - - spin_lock(&ops_lock); - if (!list_empty(&send_list)) { - op = list_entry(send_list.next, struct plock_op, list); - list_move(&op->list, &recv_list); - memcpy(&info, &op->info, sizeof(info)); - } - spin_unlock(&ops_lock); - - if (!op) - return -EAGAIN; - - if (copy_to_user(u, &info, sizeof(info))) - return -EFAULT; - return sizeof(info); -} - -/* a write copies in one plock result that should match a plock_op - on the recv list */ -static ssize_t dev_write(struct file *file, const char __user *u, size_t count, - loff_t *ppos) -{ - struct gdlm_plock_info info; - struct plock_op *op; - int found = 0; - - if (count != sizeof(info)) - return -EINVAL; - - if (copy_from_user(&info, u, sizeof(info))) - return -EFAULT; - - if (check_version(&info)) - return -EINVAL; - - spin_lock(&ops_lock); - list_for_each_entry(op, &recv_list, list) { - if (op->info.fsid == info.fsid && op->info.number == info.number && - op->info.owner == info.owner) { - list_del_init(&op->list); - found = 1; - op->done = 1; - memcpy(&op->info, &info, sizeof(info)); - break; - } - } - spin_unlock(&ops_lock); - - if (found) - wake_up(&recv_wq); - else - printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid, - (unsigned long long)info.number); - return count; -} - -static unsigned int dev_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &send_wq, wait); - - spin_lock(&ops_lock); - if (!list_empty(&send_list)) { - spin_unlock(&ops_lock); - return POLLIN | POLLRDNORM; - } - spin_unlock(&ops_lock); - return 0; -} - -static struct file_operations dev_fops = { - .read = dev_read, - .write = dev_write, - .poll = dev_poll, - .owner = THIS_MODULE -}; - -static struct miscdevice plock_dev_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = GDLM_PLOCK_MISC_NAME, - .fops = &dev_fops -}; - -int gdlm_plock_init(void) -{ - int rv; - - spin_lock_init(&ops_lock); - INIT_LIST_HEAD(&send_list); - INIT_LIST_HEAD(&recv_list); - init_waitqueue_head(&send_wq); - init_waitqueue_head(&recv_wq); - - rv = misc_register(&plock_dev_misc); - if (rv) - printk(KERN_INFO "gdlm_plock_init: misc_register failed %d", - rv); - return rv; -} - -void gdlm_plock_exit(void) -{ - if (misc_deregister(&plock_dev_misc) < 0) - printk(KERN_INFO "gdlm_plock_exit: misc_deregister failed"); -} - diff --git a/trunk/fs/gfs2/locking/dlm/sysfs.c b/trunk/fs/gfs2/locking/dlm/sysfs.c deleted file mode 100644 index 29ae06f94944..000000000000 --- a/trunk/fs/gfs2/locking/dlm/sysfs.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#include -#include - -#include "lock_dlm.h" - -extern struct lm_lockops gdlm_ops; - -static ssize_t proto_name_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%s\n", gdlm_ops.lm_proto_name); -} - -static ssize_t block_show(struct gdlm_ls *ls, char *buf) -{ - ssize_t ret; - int val = 0; - - if (test_bit(DFL_BLOCK_LOCKS, &ls->flags)) - val = 1; - ret = sprintf(buf, "%d\n", val); - return ret; -} - -static ssize_t block_store(struct gdlm_ls *ls, const char *buf, size_t len) -{ - ssize_t ret = len; - int val; - - val = simple_strtol(buf, NULL, 0); - - if (val == 1) - set_bit(DFL_BLOCK_LOCKS, &ls->flags); - else if (val == 0) { - clear_bit(DFL_BLOCK_LOCKS, &ls->flags); - gdlm_submit_delayed(ls); - } else { - ret = -EINVAL; - } - return ret; -} - -static ssize_t withdraw_show(struct gdlm_ls *ls, char *buf) -{ - ssize_t ret; - int val = 0; - - if (test_bit(DFL_WITHDRAW, &ls->flags)) - val = 1; - ret = sprintf(buf, "%d\n", val); - return ret; -} - -static ssize_t withdraw_store(struct gdlm_ls *ls, const char *buf, size_t len) -{ - ssize_t ret = len; - int val; - - val = simple_strtol(buf, NULL, 0); - - if (val == 1) - set_bit(DFL_WITHDRAW, &ls->flags); - else - ret = -EINVAL; - wake_up(&ls->wait_control); - return ret; -} - -static ssize_t id_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%u\n", ls->id); -} - -static ssize_t jid_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->jid); -} - -static ssize_t first_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->first); -} - -static ssize_t first_done_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->first_done); -} - -static ssize_t recover_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->recover_jid); -} - -static ssize_t recover_store(struct gdlm_ls *ls, const char *buf, size_t len) -{ - ls->recover_jid = simple_strtol(buf, NULL, 0); - ls->fscb(ls->sdp, LM_CB_NEED_RECOVERY, &ls->recover_jid); - return len; -} - -static ssize_t recover_done_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->recover_jid_done); -} - -static ssize_t recover_status_show(struct gdlm_ls *ls, char *buf) -{ - return sprintf(buf, "%d\n", ls->recover_jid_status); -} - -struct gdlm_attr { - struct attribute attr; - ssize_t (*show)(struct gdlm_ls *, char *); - ssize_t (*store)(struct gdlm_ls *, const char *, size_t); -}; - -#define GDLM_ATTR(_name,_mode,_show,_store) \ -static struct gdlm_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store) - -GDLM_ATTR(proto_name, 0444, proto_name_show, NULL); -GDLM_ATTR(block, 0644, block_show, block_store); -GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store); -GDLM_ATTR(id, 0444, id_show, NULL); -GDLM_ATTR(jid, 0444, jid_show, NULL); -GDLM_ATTR(first, 0444, first_show, NULL); -GDLM_ATTR(first_done, 0444, first_done_show, NULL); -GDLM_ATTR(recover, 0644, recover_show, recover_store); -GDLM_ATTR(recover_done, 0444, recover_done_show, NULL); -GDLM_ATTR(recover_status, 0444, recover_status_show, NULL); - -static struct attribute *gdlm_attrs[] = { - &gdlm_attr_proto_name.attr, - &gdlm_attr_block.attr, - &gdlm_attr_withdraw.attr, - &gdlm_attr_id.attr, - &gdlm_attr_jid.attr, - &gdlm_attr_first.attr, - &gdlm_attr_first_done.attr, - &gdlm_attr_recover.attr, - &gdlm_attr_recover_done.attr, - &gdlm_attr_recover_status.attr, - NULL, -}; - -static ssize_t gdlm_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct gdlm_ls *ls = container_of(kobj, struct gdlm_ls, kobj); - struct gdlm_attr *a = container_of(attr, struct gdlm_attr, attr); - return a->show ? a->show(ls, buf) : 0; -} - -static ssize_t gdlm_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t len) -{ - struct gdlm_ls *ls = container_of(kobj, struct gdlm_ls, kobj); - struct gdlm_attr *a = container_of(attr, struct gdlm_attr, attr); - return a->store ? a->store(ls, buf, len) : len; -} - -static struct sysfs_ops gdlm_attr_ops = { - .show = gdlm_attr_show, - .store = gdlm_attr_store, -}; - -static struct kobj_type gdlm_ktype = { - .default_attrs = gdlm_attrs, - .sysfs_ops = &gdlm_attr_ops, -}; - -static struct kset gdlm_kset = { - .subsys = &kernel_subsys, - .kobj = {.name = "lock_dlm",}, - .ktype = &gdlm_ktype, -}; - -int gdlm_kobject_setup(struct gdlm_ls *ls, struct kobject *fskobj) -{ - int error; - - error = kobject_set_name(&ls->kobj, "%s", "lock_module"); - if (error) { - log_error("can't set kobj name %d", error); - return error; - } - - ls->kobj.kset = &gdlm_kset; - ls->kobj.ktype = &gdlm_ktype; - ls->kobj.parent = fskobj; - - error = kobject_register(&ls->kobj); - if (error) - log_error("can't register kobj %d", error); - - return error; -} - -void gdlm_kobject_release(struct gdlm_ls *ls) -{ - kobject_unregister(&ls->kobj); -} - -int gdlm_sysfs_init(void) -{ - int error; - - error = kset_register(&gdlm_kset); - if (error) - printk("lock_dlm: cannot register kset %d\n", error); - - return error; -} - -void gdlm_sysfs_exit(void) -{ - kset_unregister(&gdlm_kset); -} - diff --git a/trunk/fs/gfs2/locking/dlm/thread.c b/trunk/fs/gfs2/locking/dlm/thread.c deleted file mode 100644 index 9cf1f168eaf8..000000000000 --- a/trunk/fs/gfs2/locking/dlm/thread.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#include "lock_dlm.h" - -/* A lock placed on this queue is re-submitted to DLM as soon as the lock_dlm - thread gets to it. */ - -static void queue_submit(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - - spin_lock(&ls->async_lock); - list_add_tail(&lp->delay_list, &ls->submit); - spin_unlock(&ls->async_lock); - wake_up(&ls->thread_wait); -} - -static void process_blocking(struct gdlm_lock *lp, int bast_mode) -{ - struct gdlm_ls *ls = lp->ls; - unsigned int cb = 0; - - switch (gdlm_make_lmstate(bast_mode)) { - case LM_ST_EXCLUSIVE: - cb = LM_CB_NEED_E; - break; - case LM_ST_DEFERRED: - cb = LM_CB_NEED_D; - break; - case LM_ST_SHARED: - cb = LM_CB_NEED_S; - break; - default: - gdlm_assert(0, "unknown bast mode %u", lp->bast_mode); - } - - ls->fscb(ls->sdp, cb, &lp->lockname); -} - -static void process_complete(struct gdlm_lock *lp) -{ - struct gdlm_ls *ls = lp->ls; - struct lm_async_cb acb; - s16 prev_mode = lp->cur; - - memset(&acb, 0, sizeof(acb)); - - if (lp->lksb.sb_status == -DLM_ECANCEL) { - log_info("complete dlm cancel %x,%llx flags %lx", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, - lp->flags); - - lp->req = lp->cur; - acb.lc_ret |= LM_OUT_CANCELED; - if (lp->cur == DLM_LOCK_IV) - lp->lksb.sb_lkid = 0; - goto out; - } - - if (test_and_clear_bit(LFL_DLM_UNLOCK, &lp->flags)) { - if (lp->lksb.sb_status != -DLM_EUNLOCK) { - log_info("unlock sb_status %d %x,%llx flags %lx", - lp->lksb.sb_status, lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, - lp->flags); - return; - } - - lp->cur = DLM_LOCK_IV; - lp->req = DLM_LOCK_IV; - lp->lksb.sb_lkid = 0; - - if (test_and_clear_bit(LFL_UNLOCK_DELETE, &lp->flags)) { - gdlm_delete_lp(lp); - return; - } - goto out; - } - - if (lp->lksb.sb_flags & DLM_SBF_VALNOTVALID) - memset(lp->lksb.sb_lvbptr, 0, GDLM_LVB_SIZE); - - if (lp->lksb.sb_flags & DLM_SBF_ALTMODE) { - if (lp->req == DLM_LOCK_PR) - lp->req = DLM_LOCK_CW; - else if (lp->req == DLM_LOCK_CW) - lp->req = DLM_LOCK_PR; - } - - /* - * A canceled lock request. The lock was just taken off the delayed - * list and was never even submitted to dlm. - */ - - if (test_and_clear_bit(LFL_CANCEL, &lp->flags)) { - log_info("complete internal cancel %x,%llx", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number); - lp->req = lp->cur; - acb.lc_ret |= LM_OUT_CANCELED; - goto out; - } - - /* - * An error occured. - */ - - if (lp->lksb.sb_status) { - /* a "normal" error */ - if ((lp->lksb.sb_status == -EAGAIN) && - (lp->lkf & DLM_LKF_NOQUEUE)) { - lp->req = lp->cur; - if (lp->cur == DLM_LOCK_IV) - lp->lksb.sb_lkid = 0; - goto out; - } - - /* this could only happen with cancels I think */ - log_info("ast sb_status %d %x,%llx flags %lx", - lp->lksb.sb_status, lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, - lp->flags); - return; - } - - /* - * This is an AST for an EX->EX conversion for sync_lvb from GFS. - */ - - if (test_and_clear_bit(LFL_SYNC_LVB, &lp->flags)) { - complete(&lp->ast_wait); - return; - } - - /* - * A lock has been demoted to NL because it initially completed during - * BLOCK_LOCKS. Now it must be requested in the originally requested - * mode. - */ - - if (test_and_clear_bit(LFL_REREQUEST, &lp->flags)) { - gdlm_assert(lp->req == DLM_LOCK_NL, "%x,%llx", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number); - gdlm_assert(lp->prev_req > DLM_LOCK_NL, "%x,%llx", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number); - - lp->cur = DLM_LOCK_NL; - lp->req = lp->prev_req; - lp->prev_req = DLM_LOCK_IV; - lp->lkf &= ~DLM_LKF_CONVDEADLK; - - set_bit(LFL_NOCACHE, &lp->flags); - - if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) && - !test_bit(LFL_NOBLOCK, &lp->flags)) - gdlm_queue_delayed(lp); - else - queue_submit(lp); - return; - } - - /* - * A request is granted during dlm recovery. It may be granted - * because the locks of a failed node were cleared. In that case, - * there may be inconsistent data beneath this lock and we must wait - * for recovery to complete to use it. When gfs recovery is done this - * granted lock will be converted to NL and then reacquired in this - * granted state. - */ - - if (test_bit(DFL_BLOCK_LOCKS, &ls->flags) && - !test_bit(LFL_NOBLOCK, &lp->flags) && - lp->req != DLM_LOCK_NL) { - - lp->cur = lp->req; - lp->prev_req = lp->req; - lp->req = DLM_LOCK_NL; - lp->lkf |= DLM_LKF_CONVERT; - lp->lkf &= ~DLM_LKF_CONVDEADLK; - - log_debug("rereq %x,%llx id %x %d,%d", - lp->lockname.ln_type, - (unsigned long long)lp->lockname.ln_number, - lp->lksb.sb_lkid, lp->cur, lp->req); - - set_bit(LFL_REREQUEST, &lp->flags); - queue_submit(lp); - return; - } - - /* - * DLM demoted the lock to NL before it was granted so GFS must be - * told it cannot cache data for this lock. - */ - - if (lp->lksb.sb_flags & DLM_SBF_DEMOTED) - set_bit(LFL_NOCACHE, &lp->flags); - -out: - /* - * This is an internal lock_dlm lock - */ - - if (test_bit(LFL_INLOCK, &lp->flags)) { - clear_bit(LFL_NOBLOCK, &lp->flags); - lp->cur = lp->req; - complete(&lp->ast_wait); - return; - } - - /* - * Normal completion of a lock request. Tell GFS it now has the lock. - */ - - clear_bit(LFL_NOBLOCK, &lp->flags); - lp->cur = lp->req; - - acb.lc_name = lp->lockname; - acb.lc_ret |= gdlm_make_lmstate(lp->cur); - - if (!test_and_clear_bit(LFL_NOCACHE, &lp->flags) && - (lp->cur > DLM_LOCK_NL) && (prev_mode > DLM_LOCK_NL)) - acb.lc_ret |= LM_OUT_CACHEABLE; - - ls->fscb(ls->sdp, LM_CB_ASYNC, &acb); -} - -static inline int no_work(struct gdlm_ls *ls, int blocking) -{ - int ret; - - spin_lock(&ls->async_lock); - ret = list_empty(&ls->complete) && list_empty(&ls->submit); - if (ret && blocking) - ret = list_empty(&ls->blocking); - spin_unlock(&ls->async_lock); - - return ret; -} - -static inline int check_drop(struct gdlm_ls *ls) -{ - if (!ls->drop_locks_count) - return 0; - - if (time_after(jiffies, ls->drop_time + ls->drop_locks_period * HZ)) { - ls->drop_time = jiffies; - if (ls->all_locks_count >= ls->drop_locks_count) - return 1; - } - return 0; -} - -static int gdlm_thread(void *data) -{ - struct gdlm_ls *ls = (struct gdlm_ls *) data; - struct gdlm_lock *lp = NULL; - int blist = 0; - uint8_t complete, blocking, submit, drop; - DECLARE_WAITQUEUE(wait, current); - - /* Only thread1 is allowed to do blocking callbacks since gfs - may wait for a completion callback within a blocking cb. */ - - if (current == ls->thread1) - blist = 1; - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&ls->thread_wait, &wait); - if (no_work(ls, blist)) - schedule(); - remove_wait_queue(&ls->thread_wait, &wait); - set_current_state(TASK_RUNNING); - - complete = blocking = submit = drop = 0; - - spin_lock(&ls->async_lock); - - if (blist && !list_empty(&ls->blocking)) { - lp = list_entry(ls->blocking.next, struct gdlm_lock, - blist); - list_del_init(&lp->blist); - blocking = lp->bast_mode; - lp->bast_mode = 0; - } else if (!list_empty(&ls->complete)) { - lp = list_entry(ls->complete.next, struct gdlm_lock, - clist); - list_del_init(&lp->clist); - complete = 1; - } else if (!list_empty(&ls->submit)) { - lp = list_entry(ls->submit.next, struct gdlm_lock, - delay_list); - list_del_init(&lp->delay_list); - submit = 1; - } - - drop = check_drop(ls); - spin_unlock(&ls->async_lock); - - if (complete) - process_complete(lp); - - else if (blocking) - process_blocking(lp, blocking); - - else if (submit) - gdlm_do_lock(lp); - - if (drop) - ls->fscb(ls->sdp, LM_CB_DROPLOCKS, NULL); - - schedule(); - } - - return 0; -} - -int gdlm_init_threads(struct gdlm_ls *ls) -{ - struct task_struct *p; - int error; - - p = kthread_run(gdlm_thread, ls, "lock_dlm1"); - error = IS_ERR(p); - if (error) { - log_error("can't start lock_dlm1 thread %d", error); - return error; - } - ls->thread1 = p; - - p = kthread_run(gdlm_thread, ls, "lock_dlm2"); - error = IS_ERR(p); - if (error) { - log_error("can't start lock_dlm2 thread %d", error); - kthread_stop(ls->thread1); - return error; - } - ls->thread2 = p; - - return 0; -} - -void gdlm_release_threads(struct gdlm_ls *ls) -{ - kthread_stop(ls->thread1); - kthread_stop(ls->thread2); -} - diff --git a/trunk/fs/gfs2/locking/nolock/Makefile b/trunk/fs/gfs2/locking/nolock/Makefile deleted file mode 100644 index 35e9730bc3a8..000000000000 --- a/trunk/fs/gfs2/locking/nolock/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_GFS2_FS_LOCKING_NOLOCK) += lock_nolock.o -lock_nolock-y := main.o - diff --git a/trunk/fs/gfs2/locking/nolock/main.c b/trunk/fs/gfs2/locking/nolock/main.c deleted file mode 100644 index acfbc941f319..000000000000 --- a/trunk/fs/gfs2/locking/nolock/main.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct nolock_lockspace { - unsigned int nl_lvb_size; -}; - -static const struct lm_lockops nolock_ops; - -static int nolock_mount(char *table_name, char *host_data, - lm_callback_t cb, void *cb_data, - unsigned int min_lvb_size, int flags, - struct lm_lockstruct *lockstruct, - struct kobject *fskobj) -{ - char *c; - unsigned int jid; - struct nolock_lockspace *nl; - - c = strstr(host_data, "jid="); - if (!c) - jid = 0; - else { - c += 4; - sscanf(c, "%u", &jid); - } - - nl = kzalloc(sizeof(struct nolock_lockspace), GFP_KERNEL); - if (!nl) - return -ENOMEM; - - nl->nl_lvb_size = min_lvb_size; - - lockstruct->ls_jid = jid; - lockstruct->ls_first = 1; - lockstruct->ls_lvb_size = min_lvb_size; - lockstruct->ls_lockspace = nl; - lockstruct->ls_ops = &nolock_ops; - lockstruct->ls_flags = LM_LSFLAG_LOCAL; - - return 0; -} - -static void nolock_others_may_mount(void *lockspace) -{ -} - -static void nolock_unmount(void *lockspace) -{ - struct nolock_lockspace *nl = lockspace; - kfree(nl); -} - -static void nolock_withdraw(void *lockspace) -{ -} - -/** - * nolock_get_lock - get a lm_lock_t given a descripton of the lock - * @lockspace: the lockspace the lock lives in - * @name: the name of the lock - * @lockp: return the lm_lock_t here - * - * Returns: 0 on success, -EXXX on failure - */ - -static int nolock_get_lock(void *lockspace, struct lm_lockname *name, - void **lockp) -{ - *lockp = lockspace; - return 0; -} - -/** - * nolock_put_lock - get rid of a lock structure - * @lock: the lock to throw away - * - */ - -static void nolock_put_lock(void *lock) -{ -} - -/** - * nolock_lock - acquire a lock - * @lock: the lock to manipulate - * @cur_state: the current state - * @req_state: the requested state - * @flags: modifier flags - * - * Returns: A bitmap of LM_OUT_* - */ - -static unsigned int nolock_lock(void *lock, unsigned int cur_state, - unsigned int req_state, unsigned int flags) -{ - return req_state | LM_OUT_CACHEABLE; -} - -/** - * nolock_unlock - unlock a lock - * @lock: the lock to manipulate - * @cur_state: the current state - * - * Returns: 0 - */ - -static unsigned int nolock_unlock(void *lock, unsigned int cur_state) -{ - return 0; -} - -static void nolock_cancel(void *lock) -{ -} - -/** - * nolock_hold_lvb - hold on to a lock value block - * @lock: the lock the LVB is associated with - * @lvbp: return the lm_lvb_t here - * - * Returns: 0 on success, -EXXX on failure - */ - -static int nolock_hold_lvb(void *lock, char **lvbp) -{ - struct nolock_lockspace *nl = lock; - int error = 0; - - *lvbp = kzalloc(nl->nl_lvb_size, GFP_KERNEL); - if (!*lvbp) - error = -ENOMEM; - - return error; -} - -/** - * nolock_unhold_lvb - release a LVB - * @lock: the lock the LVB is associated with - * @lvb: the lock value block - * - */ - -static void nolock_unhold_lvb(void *lock, char *lvb) -{ - kfree(lvb); -} - -static int nolock_plock_get(void *lockspace, struct lm_lockname *name, - struct file *file, struct file_lock *fl) -{ - struct file_lock tmp; - int ret; - - ret = posix_test_lock(file, fl, &tmp); - fl->fl_type = F_UNLCK; - if (ret) - memcpy(fl, &tmp, sizeof(struct file_lock)); - - return 0; -} - -static int nolock_plock(void *lockspace, struct lm_lockname *name, - struct file *file, int cmd, struct file_lock *fl) -{ - int error; - error = posix_lock_file_wait(file, fl); - return error; -} - -static int nolock_punlock(void *lockspace, struct lm_lockname *name, - struct file *file, struct file_lock *fl) -{ - int error; - error = posix_lock_file_wait(file, fl); - return error; -} - -static void nolock_recovery_done(void *lockspace, unsigned int jid, - unsigned int message) -{ -} - -static const struct lm_lockops nolock_ops = { - .lm_proto_name = "lock_nolock", - .lm_mount = nolock_mount, - .lm_others_may_mount = nolock_others_may_mount, - .lm_unmount = nolock_unmount, - .lm_withdraw = nolock_withdraw, - .lm_get_lock = nolock_get_lock, - .lm_put_lock = nolock_put_lock, - .lm_lock = nolock_lock, - .lm_unlock = nolock_unlock, - .lm_cancel = nolock_cancel, - .lm_hold_lvb = nolock_hold_lvb, - .lm_unhold_lvb = nolock_unhold_lvb, - .lm_plock_get = nolock_plock_get, - .lm_plock = nolock_plock, - .lm_punlock = nolock_punlock, - .lm_recovery_done = nolock_recovery_done, - .lm_owner = THIS_MODULE, -}; - -static int __init init_nolock(void) -{ - int error; - - error = gfs2_register_lockproto(&nolock_ops); - if (error) { - printk(KERN_WARNING - "lock_nolock: can't register protocol: %d\n", error); - return error; - } - - printk(KERN_INFO - "Lock_Nolock (built %s %s) installed\n", __DATE__, __TIME__); - return 0; -} - -static void __exit exit_nolock(void) -{ - gfs2_unregister_lockproto(&nolock_ops); -} - -module_init(init_nolock); -module_exit(exit_nolock); - -MODULE_DESCRIPTION("GFS Nolock Locking Module"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - diff --git a/trunk/fs/gfs2/log.c b/trunk/fs/gfs2/log.c deleted file mode 100644 index 554fe5bd1b72..000000000000 --- a/trunk/fs/gfs2/log.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "log.h" -#include "lops.h" -#include "meta_io.h" -#include "util.h" -#include "dir.h" - -#define PULL 1 - -/** - * gfs2_struct2blk - compute stuff - * @sdp: the filesystem - * @nstruct: the number of structures - * @ssize: the size of the structures - * - * Compute the number of log descriptor blocks needed to hold a certain number - * of structures of a certain size. - * - * Returns: the number of blocks needed (minimum is always 1) - */ - -unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, - unsigned int ssize) -{ - unsigned int blks; - unsigned int first, second; - - blks = 1; - first = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / ssize; - - if (nstruct > first) { - second = (sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_meta_header)) / ssize; - blks += DIV_ROUND_UP(nstruct - first, second); - } - - return blks; -} - -/** - * gfs2_ail1_start_one - Start I/O on a part of the AIL - * @sdp: the filesystem - * @tr: the part of the AIL - * - */ - -static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct gfs2_bufdata *bd, *s; - struct buffer_head *bh; - int retry; - - BUG_ON(!spin_is_locked(&sdp->sd_log_lock)); - - do { - retry = 0; - - list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, - bd_ail_st_list) { - bh = bd->bd_bh; - - gfs2_assert(sdp, bd->bd_ail == ai); - - if (!buffer_busy(bh)) { - if (!buffer_uptodate(bh)) { - gfs2_log_unlock(sdp); - gfs2_io_error_bh(sdp, bh); - gfs2_log_lock(sdp); - } - list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); - continue; - } - - if (!buffer_dirty(bh)) - continue; - - list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); - - gfs2_log_unlock(sdp); - wait_on_buffer(bh); - ll_rw_block(WRITE, 1, &bh); - gfs2_log_lock(sdp); - - retry = 1; - break; - } - } while (retry); -} - -/** - * gfs2_ail1_empty_one - Check whether or not a trans in the AIL has been synced - * @sdp: the filesystem - * @ai: the AIL entry - * - */ - -static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags) -{ - struct gfs2_bufdata *bd, *s; - struct buffer_head *bh; - - list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, - bd_ail_st_list) { - bh = bd->bd_bh; - - gfs2_assert(sdp, bd->bd_ail == ai); - - if (buffer_busy(bh)) { - if (flags & DIO_ALL) - continue; - else - break; - } - - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - - list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); - } - - return list_empty(&ai->ai_ail1_list); -} - -void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) -{ - struct list_head *head = &sdp->sd_ail1_list; - u64 sync_gen; - struct list_head *first; - struct gfs2_ail *first_ai, *ai, *tmp; - int done = 0; - - gfs2_log_lock(sdp); - if (list_empty(head)) { - gfs2_log_unlock(sdp); - return; - } - sync_gen = sdp->sd_ail_sync_gen++; - - first = head->prev; - first_ai = list_entry(first, struct gfs2_ail, ai_list); - first_ai->ai_sync_gen = sync_gen; - gfs2_ail1_start_one(sdp, first_ai); /* This may drop log lock */ - - if (flags & DIO_ALL) - first = NULL; - - while(!done) { - if (first && (head->prev != first || - gfs2_ail1_empty_one(sdp, first_ai, 0))) - break; - - done = 1; - list_for_each_entry_safe_reverse(ai, tmp, head, ai_list) { - if (ai->ai_sync_gen >= sync_gen) - continue; - ai->ai_sync_gen = sync_gen; - gfs2_ail1_start_one(sdp, ai); /* This may drop log lock */ - done = 0; - break; - } - } - - gfs2_log_unlock(sdp); -} - -int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags) -{ - struct gfs2_ail *ai, *s; - int ret; - - gfs2_log_lock(sdp); - - list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) { - if (gfs2_ail1_empty_one(sdp, ai, flags)) - list_move(&ai->ai_list, &sdp->sd_ail2_list); - else if (!(flags & DIO_ALL)) - break; - } - - ret = list_empty(&sdp->sd_ail1_list); - - gfs2_log_unlock(sdp); - - return ret; -} - - -/** - * gfs2_ail2_empty_one - Check whether or not a trans in the AIL has been synced - * @sdp: the filesystem - * @ai: the AIL entry - * - */ - -static void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct list_head *head = &ai->ai_ail2_list; - struct gfs2_bufdata *bd; - - while (!list_empty(head)) { - bd = list_entry(head->prev, struct gfs2_bufdata, - bd_ail_st_list); - gfs2_assert(sdp, bd->bd_ail == ai); - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&bd->bd_gl->gl_ail_count); - brelse(bd->bd_bh); - } -} - -static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) -{ - struct gfs2_ail *ai, *safe; - unsigned int old_tail = sdp->sd_log_tail; - int wrap = (new_tail < old_tail); - int a, b, rm; - - gfs2_log_lock(sdp); - - list_for_each_entry_safe(ai, safe, &sdp->sd_ail2_list, ai_list) { - a = (old_tail <= ai->ai_first); - b = (ai->ai_first < new_tail); - rm = (wrap) ? (a || b) : (a && b); - if (!rm) - continue; - - gfs2_ail2_empty_one(sdp, ai); - list_del(&ai->ai_list); - gfs2_assert_warn(sdp, list_empty(&ai->ai_ail1_list)); - gfs2_assert_warn(sdp, list_empty(&ai->ai_ail2_list)); - kfree(ai); - } - - gfs2_log_unlock(sdp); -} - -/** - * gfs2_log_reserve - Make a log reservation - * @sdp: The GFS2 superblock - * @blks: The number of blocks to reserve - * - * Returns: errno - */ - -int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks) -{ - unsigned int try = 0; - - if (gfs2_assert_warn(sdp, blks) || - gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks)) - return -EINVAL; - - mutex_lock(&sdp->sd_log_reserve_mutex); - gfs2_log_lock(sdp); - while(sdp->sd_log_blks_free <= blks) { - gfs2_log_unlock(sdp); - gfs2_ail1_empty(sdp, 0); - gfs2_log_flush(sdp, NULL); - - if (try++) - gfs2_ail1_start(sdp, 0); - gfs2_log_lock(sdp); - } - sdp->sd_log_blks_free -= blks; - gfs2_log_unlock(sdp); - mutex_unlock(&sdp->sd_log_reserve_mutex); - - down_read(&sdp->sd_log_flush_lock); - - return 0; -} - -/** - * gfs2_log_release - Release a given number of log blocks - * @sdp: The GFS2 superblock - * @blks: The number of blocks - * - */ - -void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) -{ - - gfs2_log_lock(sdp); - sdp->sd_log_blks_free += blks; - gfs2_assert_withdraw(sdp, - sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); - gfs2_log_unlock(sdp); - up_read(&sdp->sd_log_flush_lock); -} - -static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn) -{ - int error; - struct buffer_head bh_map; - - error = gfs2_block_map(sdp->sd_jdesc->jd_inode, lbn, 0, &bh_map, 1); - if (error || !bh_map.b_blocknr) - printk(KERN_INFO "error=%d, dbn=%llu lbn=%u", error, bh_map.b_blocknr, lbn); - gfs2_assert_withdraw(sdp, !error && bh_map.b_blocknr); - - return bh_map.b_blocknr; -} - -/** - * log_distance - Compute distance between two journal blocks - * @sdp: The GFS2 superblock - * @newer: The most recent journal block of the pair - * @older: The older journal block of the pair - * - * Compute the distance (in the journal direction) between two - * blocks in the journal - * - * Returns: the distance in blocks - */ - -static inline unsigned int log_distance(struct gfs2_sbd *sdp, unsigned int newer, - unsigned int older) -{ - int dist; - - dist = newer - older; - if (dist < 0) - dist += sdp->sd_jdesc->jd_blocks; - - return dist; -} - -static unsigned int current_tail(struct gfs2_sbd *sdp) -{ - struct gfs2_ail *ai; - unsigned int tail; - - gfs2_log_lock(sdp); - - if (list_empty(&sdp->sd_ail1_list)) { - tail = sdp->sd_log_head; - } else { - ai = list_entry(sdp->sd_ail1_list.prev, struct gfs2_ail, ai_list); - tail = ai->ai_first; - } - - gfs2_log_unlock(sdp); - - return tail; -} - -static inline void log_incr_head(struct gfs2_sbd *sdp) -{ - if (sdp->sd_log_flush_head == sdp->sd_log_tail) - gfs2_assert_withdraw(sdp, sdp->sd_log_flush_head == sdp->sd_log_head); - - if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) { - sdp->sd_log_flush_head = 0; - sdp->sd_log_flush_wrapped = 1; - } -} - -/** - * gfs2_log_get_buf - Get and initialize a buffer to use for log control data - * @sdp: The GFS2 superblock - * - * Returns: the buffer_head - */ - -struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp) -{ - u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); - struct gfs2_log_buf *lb; - struct buffer_head *bh; - - lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL); - list_add(&lb->lb_list, &sdp->sd_log_flush_list); - - bh = lb->lb_bh = sb_getblk(sdp->sd_vfs, blkno); - lock_buffer(bh); - memset(bh->b_data, 0, bh->b_size); - set_buffer_uptodate(bh); - clear_buffer_dirty(bh); - unlock_buffer(bh); - - log_incr_head(sdp); - - return bh; -} - -/** - * gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log - * @sdp: the filesystem - * @data: the data the buffer_head should point to - * - * Returns: the log buffer descriptor - */ - -struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, - struct buffer_head *real) -{ - u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); - struct gfs2_log_buf *lb; - struct buffer_head *bh; - - lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL); - list_add(&lb->lb_list, &sdp->sd_log_flush_list); - lb->lb_real = real; - - bh = lb->lb_bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL); - atomic_set(&bh->b_count, 1); - bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate); - set_bh_page(bh, real->b_page, bh_offset(real)); - bh->b_blocknr = blkno; - bh->b_size = sdp->sd_sb.sb_bsize; - bh->b_bdev = sdp->sd_vfs->s_bdev; - - log_incr_head(sdp); - - return bh; -} - -static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull) -{ - unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail); - - ail2_empty(sdp, new_tail); - - gfs2_log_lock(sdp); - sdp->sd_log_blks_free += dist - (pull ? 1 : 0); - gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks); - gfs2_log_unlock(sdp); - - sdp->sd_log_tail = new_tail; -} - -/** - * log_write_header - Get and initialize a journal header buffer - * @sdp: The GFS2 superblock - * - * Returns: the initialized log buffer descriptor - */ - -static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) -{ - u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); - struct buffer_head *bh; - struct gfs2_log_header *lh; - unsigned int tail; - u32 hash; - - bh = sb_getblk(sdp->sd_vfs, blkno); - lock_buffer(bh); - memset(bh->b_data, 0, bh->b_size); - set_buffer_uptodate(bh); - clear_buffer_dirty(bh); - unlock_buffer(bh); - - gfs2_ail1_empty(sdp, 0); - tail = current_tail(sdp); - - lh = (struct gfs2_log_header *)bh->b_data; - memset(lh, 0, sizeof(struct gfs2_log_header)); - lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH); - lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH); - lh->lh_sequence = cpu_to_be64(sdp->sd_log_sequence++); - lh->lh_flags = cpu_to_be32(flags); - lh->lh_tail = cpu_to_be32(tail); - lh->lh_blkno = cpu_to_be32(sdp->sd_log_flush_head); - hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header)); - lh->lh_hash = cpu_to_be32(hash); - - set_buffer_dirty(bh); - if (sync_dirty_buffer(bh)) - gfs2_io_error_bh(sdp, bh); - brelse(bh); - - if (sdp->sd_log_tail != tail) - log_pull_tail(sdp, tail, pull); - else - gfs2_assert_withdraw(sdp, !pull); - - sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); - log_incr_head(sdp); -} - -static void log_flush_commit(struct gfs2_sbd *sdp) -{ - struct list_head *head = &sdp->sd_log_flush_list; - struct gfs2_log_buf *lb; - struct buffer_head *bh; - - while (!list_empty(head)) { - lb = list_entry(head->next, struct gfs2_log_buf, lb_list); - list_del(&lb->lb_list); - bh = lb->lb_bh; - - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - if (lb->lb_real) { - while (atomic_read(&bh->b_count) != 1) /* Grrrr... */ - schedule(); - free_buffer_head(bh); - } else - brelse(bh); - kfree(lb); - } - - log_write_header(sdp, 0, 0); -} - -/** - * gfs2_log_flush - flush incore transaction(s) - * @sdp: the filesystem - * @gl: The glock structure to flush. If NULL, flush the whole incore log - * - */ - -void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) -{ - struct gfs2_ail *ai; - - down_write(&sdp->sd_log_flush_lock); - - if (gl) { - gfs2_log_lock(sdp); - if (list_empty(&gl->gl_le.le_list)) { - gfs2_log_unlock(sdp); - up_write(&sdp->sd_log_flush_lock); - return; - } - gfs2_log_unlock(sdp); - } - - ai = kzalloc(sizeof(struct gfs2_ail), GFP_NOFS | __GFP_NOFAIL); - INIT_LIST_HEAD(&ai->ai_ail1_list); - INIT_LIST_HEAD(&ai->ai_ail2_list); - - gfs2_assert_withdraw(sdp, sdp->sd_log_num_buf == sdp->sd_log_commited_buf); - gfs2_assert_withdraw(sdp, - sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke); - - sdp->sd_log_flush_head = sdp->sd_log_head; - sdp->sd_log_flush_wrapped = 0; - ai->ai_first = sdp->sd_log_flush_head; - - lops_before_commit(sdp); - if (!list_empty(&sdp->sd_log_flush_list)) - log_flush_commit(sdp); - else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle) - log_write_header(sdp, 0, PULL); - lops_after_commit(sdp, ai); - sdp->sd_log_head = sdp->sd_log_flush_head; - - sdp->sd_log_blks_free -= sdp->sd_log_num_hdrs; - - sdp->sd_log_blks_reserved = 0; - sdp->sd_log_commited_buf = 0; - sdp->sd_log_num_hdrs = 0; - sdp->sd_log_commited_revoke = 0; - - gfs2_log_lock(sdp); - if (!list_empty(&ai->ai_ail1_list)) { - list_add(&ai->ai_list, &sdp->sd_ail1_list); - ai = NULL; - } - gfs2_log_unlock(sdp); - - sdp->sd_vfs->s_dirt = 0; - up_write(&sdp->sd_log_flush_lock); - - kfree(ai); -} - -static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) -{ - unsigned int reserved = 0; - unsigned int old; - - gfs2_log_lock(sdp); - - sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm; - gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_buf) >= 0); - sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm; - gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0); - - if (sdp->sd_log_commited_buf) - reserved += sdp->sd_log_commited_buf; - if (sdp->sd_log_commited_revoke) - reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke, - sizeof(u64)); - if (reserved) - reserved++; - - old = sdp->sd_log_blks_free; - sdp->sd_log_blks_free += tr->tr_reserved - - (reserved - sdp->sd_log_blks_reserved); - - gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free >= old); - gfs2_assert_withdraw(sdp, - sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks + - sdp->sd_log_num_hdrs); - - sdp->sd_log_blks_reserved = reserved; - - gfs2_log_unlock(sdp); -} - -/** - * gfs2_log_commit - Commit a transaction to the log - * @sdp: the filesystem - * @tr: the transaction - * - * Returns: errno - */ - -void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) -{ - log_refund(sdp, tr); - lops_incore_commit(sdp, tr); - - sdp->sd_vfs->s_dirt = 1; - up_read(&sdp->sd_log_flush_lock); - - gfs2_log_lock(sdp); - if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) { - gfs2_log_unlock(sdp); - gfs2_log_flush(sdp, NULL); - } else { - gfs2_log_unlock(sdp); - } -} - -/** - * gfs2_log_shutdown - write a shutdown header into a journal - * @sdp: the filesystem - * - */ - -void gfs2_log_shutdown(struct gfs2_sbd *sdp) -{ - down_write(&sdp->sd_log_flush_lock); - - gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_gl); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_buf); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_jdata); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf); - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_hdrs); - gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list)); - - sdp->sd_log_flush_head = sdp->sd_log_head; - sdp->sd_log_flush_wrapped = 0; - - log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0); - - gfs2_assert_warn(sdp, sdp->sd_log_blks_free == sdp->sd_jdesc->jd_blocks); - gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail); - gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list)); - - sdp->sd_log_head = sdp->sd_log_flush_head; - sdp->sd_log_tail = sdp->sd_log_head; - - up_write(&sdp->sd_log_flush_lock); -} - diff --git a/trunk/fs/gfs2/log.h b/trunk/fs/gfs2/log.h deleted file mode 100644 index 7f5737d55612..000000000000 --- a/trunk/fs/gfs2/log.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __LOG_DOT_H__ -#define __LOG_DOT_H__ - -#include -#include -#include "incore.h" - -/** - * gfs2_log_lock - acquire the right to mess with the log manager - * @sdp: the filesystem - * - */ - -static inline void gfs2_log_lock(struct gfs2_sbd *sdp) -{ - spin_lock(&sdp->sd_log_lock); -} - -/** - * gfs2_log_unlock - release the right to mess with the log manager - * @sdp: the filesystem - * - */ - -static inline void gfs2_log_unlock(struct gfs2_sbd *sdp) -{ - spin_unlock(&sdp->sd_log_lock); -} - -static inline void gfs2_log_pointers_init(struct gfs2_sbd *sdp, - unsigned int value) -{ - if (++value == sdp->sd_jdesc->jd_blocks) { - value = 0; - } - sdp->sd_log_head = sdp->sd_log_tail = value; -} - -unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, - unsigned int ssize); - -void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags); -int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags); - -int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks); -void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); - -struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp); -struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, - struct buffer_head *real); -void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl); -void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); - -void gfs2_log_shutdown(struct gfs2_sbd *sdp); - -#endif /* __LOG_DOT_H__ */ diff --git a/trunk/fs/gfs2/lops.c b/trunk/fs/gfs2/lops.c deleted file mode 100644 index 881e337b6a70..000000000000 --- a/trunk/fs/gfs2/lops.c +++ /dev/null @@ -1,809 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "log.h" -#include "lops.h" -#include "meta_io.h" -#include "recovery.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" - -static void glock_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) -{ - struct gfs2_glock *gl; - struct gfs2_trans *tr = current->journal_info; - - tr->tr_touched = 1; - - if (!list_empty(&le->le_list)) - return; - - gl = container_of(le, struct gfs2_glock, gl_le); - if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(gl))) - return; - gfs2_glock_hold(gl); - set_bit(GLF_DIRTY, &gl->gl_flags); - - gfs2_log_lock(sdp); - sdp->sd_log_num_gl++; - list_add(&le->le_list, &sdp->sd_log_le_gl); - gfs2_log_unlock(sdp); -} - -static void glock_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct list_head *head = &sdp->sd_log_le_gl; - struct gfs2_glock *gl; - - while (!list_empty(head)) { - gl = list_entry(head->next, struct gfs2_glock, gl_le.le_list); - list_del_init(&gl->gl_le.le_list); - sdp->sd_log_num_gl--; - - gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(gl)); - gfs2_glock_put(gl); - } - gfs2_assert_warn(sdp, !sdp->sd_log_num_gl); -} - -static void buf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) -{ - struct gfs2_bufdata *bd = container_of(le, struct gfs2_bufdata, bd_le); - struct gfs2_trans *tr; - - if (!list_empty(&bd->bd_list_tr)) - return; - - tr = current->journal_info; - tr->tr_touched = 1; - tr->tr_num_buf++; - list_add(&bd->bd_list_tr, &tr->tr_list_buf); - - if (!list_empty(&le->le_list)) - return; - - gfs2_trans_add_gl(bd->bd_gl); - - gfs2_meta_check(sdp, bd->bd_bh); - gfs2_pin(sdp, bd->bd_bh); - - gfs2_log_lock(sdp); - sdp->sd_log_num_buf++; - list_add(&le->le_list, &sdp->sd_log_le_buf); - gfs2_log_unlock(sdp); - - tr->tr_num_buf_new++; -} - -static void buf_lo_incore_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) -{ - struct list_head *head = &tr->tr_list_buf; - struct gfs2_bufdata *bd; - - while (!list_empty(head)) { - bd = list_entry(head->next, struct gfs2_bufdata, bd_list_tr); - list_del_init(&bd->bd_list_tr); - tr->tr_num_buf--; - } - gfs2_assert_warn(sdp, !tr->tr_num_buf); -} - -static void buf_lo_before_commit(struct gfs2_sbd *sdp) -{ - struct buffer_head *bh; - struct gfs2_log_descriptor *ld; - struct gfs2_bufdata *bd1 = NULL, *bd2; - unsigned int total = sdp->sd_log_num_buf; - unsigned int offset = sizeof(struct gfs2_log_descriptor); - unsigned int limit; - unsigned int num; - unsigned n; - __be64 *ptr; - - offset += sizeof(__be64) - 1; - offset &= ~(sizeof(__be64) - 1); - limit = (sdp->sd_sb.sb_bsize - offset)/sizeof(__be64); - /* for 4k blocks, limit = 503 */ - - bd1 = bd2 = list_prepare_entry(bd1, &sdp->sd_log_le_buf, bd_le.le_list); - while(total) { - num = total; - if (total > limit) - num = limit; - bh = gfs2_log_get_buf(sdp); - sdp->sd_log_num_hdrs++; - ld = (struct gfs2_log_descriptor *)bh->b_data; - ptr = (__be64 *)(bh->b_data + offset); - ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - ld->ld_header.mh_type = cpu_to_be32(GFS2_METATYPE_LD); - ld->ld_header.mh_format = cpu_to_be32(GFS2_FORMAT_LD); - ld->ld_type = cpu_to_be32(GFS2_LOG_DESC_METADATA); - ld->ld_length = cpu_to_be32(num + 1); - ld->ld_data1 = cpu_to_be32(num); - ld->ld_data2 = cpu_to_be32(0); - memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); - - n = 0; - list_for_each_entry_continue(bd1, &sdp->sd_log_le_buf, - bd_le.le_list) { - *ptr++ = cpu_to_be64(bd1->bd_bh->b_blocknr); - if (++n >= num) - break; - } - - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - - n = 0; - list_for_each_entry_continue(bd2, &sdp->sd_log_le_buf, - bd_le.le_list) { - bh = gfs2_log_fake_buf(sdp, bd2->bd_bh); - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - if (++n >= num) - break; - } - - total -= num; - } -} - -static void buf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct list_head *head = &sdp->sd_log_le_buf; - struct gfs2_bufdata *bd; - - while (!list_empty(head)) { - bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list); - list_del_init(&bd->bd_le.le_list); - sdp->sd_log_num_buf--; - - gfs2_unpin(sdp, bd->bd_bh, ai); - } - gfs2_assert_warn(sdp, !sdp->sd_log_num_buf); -} - -static void buf_lo_before_scan(struct gfs2_jdesc *jd, - struct gfs2_log_header *head, int pass) -{ - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - - if (pass != 0) - return; - - sdp->sd_found_blocks = 0; - sdp->sd_replayed_blocks = 0; -} - -static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start, - struct gfs2_log_descriptor *ld, __be64 *ptr, - int pass) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - struct gfs2_glock *gl = ip->i_gl; - unsigned int blks = be32_to_cpu(ld->ld_data1); - struct buffer_head *bh_log, *bh_ip; - u64 blkno; - int error = 0; - - if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_METADATA) - return 0; - - gfs2_replay_incr_blk(sdp, &start); - - for (; blks; gfs2_replay_incr_blk(sdp, &start), blks--) { - blkno = be64_to_cpu(*ptr++); - - sdp->sd_found_blocks++; - - if (gfs2_revoke_check(sdp, blkno, start)) - continue; - - error = gfs2_replay_read_block(jd, start, &bh_log); - if (error) - return error; - - bh_ip = gfs2_meta_new(gl, blkno); - memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size); - - if (gfs2_meta_check(sdp, bh_ip)) - error = -EIO; - else - mark_buffer_dirty(bh_ip); - - brelse(bh_log); - brelse(bh_ip); - - if (error) - break; - - sdp->sd_replayed_blocks++; - } - - return error; -} - -static void buf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - - if (error) { - gfs2_meta_sync(ip->i_gl); - return; - } - if (pass != 1) - return; - - gfs2_meta_sync(ip->i_gl); - - fs_info(sdp, "jid=%u: Replayed %u of %u blocks\n", - jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks); -} - -static void revoke_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) -{ - struct gfs2_trans *tr; - - tr = current->journal_info; - tr->tr_touched = 1; - tr->tr_num_revoke++; - - gfs2_log_lock(sdp); - sdp->sd_log_num_revoke++; - list_add(&le->le_list, &sdp->sd_log_le_revoke); - gfs2_log_unlock(sdp); -} - -static void revoke_lo_before_commit(struct gfs2_sbd *sdp) -{ - struct gfs2_log_descriptor *ld; - struct gfs2_meta_header *mh; - struct buffer_head *bh; - unsigned int offset; - struct list_head *head = &sdp->sd_log_le_revoke; - struct gfs2_revoke *rv; - - if (!sdp->sd_log_num_revoke) - return; - - bh = gfs2_log_get_buf(sdp); - ld = (struct gfs2_log_descriptor *)bh->b_data; - ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - ld->ld_header.mh_type = cpu_to_be32(GFS2_METATYPE_LD); - ld->ld_header.mh_format = cpu_to_be32(GFS2_FORMAT_LD); - ld->ld_type = cpu_to_be32(GFS2_LOG_DESC_REVOKE); - ld->ld_length = cpu_to_be32(gfs2_struct2blk(sdp, sdp->sd_log_num_revoke, - sizeof(u64))); - ld->ld_data1 = cpu_to_be32(sdp->sd_log_num_revoke); - ld->ld_data2 = cpu_to_be32(0); - memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); - offset = sizeof(struct gfs2_log_descriptor); - - while (!list_empty(head)) { - rv = list_entry(head->next, struct gfs2_revoke, rv_le.le_list); - list_del_init(&rv->rv_le.le_list); - sdp->sd_log_num_revoke--; - - if (offset + sizeof(u64) > sdp->sd_sb.sb_bsize) { - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - - bh = gfs2_log_get_buf(sdp); - mh = (struct gfs2_meta_header *)bh->b_data; - mh->mh_magic = cpu_to_be32(GFS2_MAGIC); - mh->mh_type = cpu_to_be32(GFS2_METATYPE_LB); - mh->mh_format = cpu_to_be32(GFS2_FORMAT_LB); - offset = sizeof(struct gfs2_meta_header); - } - - *(__be64 *)(bh->b_data + offset) = cpu_to_be64(rv->rv_blkno); - kfree(rv); - - offset += sizeof(u64); - } - gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); - - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); -} - -static void revoke_lo_before_scan(struct gfs2_jdesc *jd, - struct gfs2_log_header *head, int pass) -{ - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - - if (pass != 0) - return; - - sdp->sd_found_revokes = 0; - sdp->sd_replay_tail = head->lh_tail; -} - -static int revoke_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start, - struct gfs2_log_descriptor *ld, __be64 *ptr, - int pass) -{ - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - unsigned int blks = be32_to_cpu(ld->ld_length); - unsigned int revokes = be32_to_cpu(ld->ld_data1); - struct buffer_head *bh; - unsigned int offset; - u64 blkno; - int first = 1; - int error; - - if (pass != 0 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_REVOKE) - return 0; - - offset = sizeof(struct gfs2_log_descriptor); - - for (; blks; gfs2_replay_incr_blk(sdp, &start), blks--) { - error = gfs2_replay_read_block(jd, start, &bh); - if (error) - return error; - - if (!first) - gfs2_metatype_check(sdp, bh, GFS2_METATYPE_LB); - - while (offset + sizeof(u64) <= sdp->sd_sb.sb_bsize) { - blkno = be64_to_cpu(*(__be64 *)(bh->b_data + offset)); - - error = gfs2_revoke_add(sdp, blkno, start); - if (error < 0) - return error; - else if (error) - sdp->sd_found_revokes++; - - if (!--revokes) - break; - offset += sizeof(u64); - } - - brelse(bh); - offset = sizeof(struct gfs2_meta_header); - first = 0; - } - - return 0; -} - -static void revoke_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass) -{ - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - - if (error) { - gfs2_revoke_clean(sdp); - return; - } - if (pass != 1) - return; - - fs_info(sdp, "jid=%u: Found %u revoke tags\n", - jd->jd_jid, sdp->sd_found_revokes); - - gfs2_revoke_clean(sdp); -} - -static void rg_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) -{ - struct gfs2_rgrpd *rgd; - struct gfs2_trans *tr = current->journal_info; - - tr->tr_touched = 1; - - if (!list_empty(&le->le_list)) - return; - - rgd = container_of(le, struct gfs2_rgrpd, rd_le); - gfs2_rgrp_bh_hold(rgd); - - gfs2_log_lock(sdp); - sdp->sd_log_num_rg++; - list_add(&le->le_list, &sdp->sd_log_le_rg); - gfs2_log_unlock(sdp); -} - -static void rg_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct list_head *head = &sdp->sd_log_le_rg; - struct gfs2_rgrpd *rgd; - - while (!list_empty(head)) { - rgd = list_entry(head->next, struct gfs2_rgrpd, rd_le.le_list); - list_del_init(&rgd->rd_le.le_list); - sdp->sd_log_num_rg--; - - gfs2_rgrp_repolish_clones(rgd); - gfs2_rgrp_bh_put(rgd); - } - gfs2_assert_warn(sdp, !sdp->sd_log_num_rg); -} - -/** - * databuf_lo_add - Add a databuf to the transaction. - * - * This is used in two distinct cases: - * i) In ordered write mode - * We put the data buffer on a list so that we can ensure that its - * synced to disk at the right time - * ii) In journaled data mode - * We need to journal the data block in the same way as metadata in - * the functions above. The difference is that here we have a tag - * which is two __be64's being the block number (as per meta data) - * and a flag which says whether the data block needs escaping or - * not. This means we need a new log entry for each 251 or so data - * blocks, which isn't an enormous overhead but twice as much as - * for normal metadata blocks. - */ -static void databuf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) -{ - struct gfs2_bufdata *bd = container_of(le, struct gfs2_bufdata, bd_le); - struct gfs2_trans *tr = current->journal_info; - struct address_space *mapping = bd->bd_bh->b_page->mapping; - struct gfs2_inode *ip = GFS2_I(mapping->host); - - tr->tr_touched = 1; - if (list_empty(&bd->bd_list_tr) && - (ip->i_di.di_flags & GFS2_DIF_JDATA)) { - tr->tr_num_buf++; - list_add(&bd->bd_list_tr, &tr->tr_list_buf); - gfs2_pin(sdp, bd->bd_bh); - tr->tr_num_buf_new++; - } - gfs2_trans_add_gl(bd->bd_gl); - gfs2_log_lock(sdp); - if (list_empty(&le->le_list)) { - if (ip->i_di.di_flags & GFS2_DIF_JDATA) - sdp->sd_log_num_jdata++; - sdp->sd_log_num_databuf++; - list_add(&le->le_list, &sdp->sd_log_le_databuf); - } - gfs2_log_unlock(sdp); -} - -static int gfs2_check_magic(struct buffer_head *bh) -{ - struct page *page = bh->b_page; - void *kaddr; - __be32 *ptr; - int rv = 0; - - kaddr = kmap_atomic(page, KM_USER0); - ptr = kaddr + bh_offset(bh); - if (*ptr == cpu_to_be32(GFS2_MAGIC)) - rv = 1; - kunmap_atomic(page, KM_USER0); - - return rv; -} - -/** - * databuf_lo_before_commit - Scan the data buffers, writing as we go - * - * Here we scan through the lists of buffers and make the assumption - * that any buffer thats been pinned is being journaled, and that - * any unpinned buffer is an ordered write data buffer and therefore - * will be written back rather than journaled. - */ -static void databuf_lo_before_commit(struct gfs2_sbd *sdp) -{ - LIST_HEAD(started); - struct gfs2_bufdata *bd1 = NULL, *bd2, *bdt; - struct buffer_head *bh = NULL; - unsigned int offset = sizeof(struct gfs2_log_descriptor); - struct gfs2_log_descriptor *ld; - unsigned int limit; - unsigned int total_dbuf = sdp->sd_log_num_databuf; - unsigned int total_jdata = sdp->sd_log_num_jdata; - unsigned int num, n; - __be64 *ptr = NULL; - - offset += 2*sizeof(__be64) - 1; - offset &= ~(2*sizeof(__be64) - 1); - limit = (sdp->sd_sb.sb_bsize - offset)/sizeof(__be64); - - /* - * Start writing ordered buffers, write journaled buffers - * into the log along with a header - */ - gfs2_log_lock(sdp); - bd2 = bd1 = list_prepare_entry(bd1, &sdp->sd_log_le_databuf, - bd_le.le_list); - while(total_dbuf) { - num = total_jdata; - if (num > limit) - num = limit; - n = 0; - list_for_each_entry_safe_continue(bd1, bdt, - &sdp->sd_log_le_databuf, - bd_le.le_list) { - /* An ordered write buffer */ - if (bd1->bd_bh && !buffer_pinned(bd1->bd_bh)) { - list_move(&bd1->bd_le.le_list, &started); - if (bd1 == bd2) { - bd2 = NULL; - bd2 = list_prepare_entry(bd2, - &sdp->sd_log_le_databuf, - bd_le.le_list); - } - total_dbuf--; - if (bd1->bd_bh) { - get_bh(bd1->bd_bh); - if (buffer_dirty(bd1->bd_bh)) { - gfs2_log_unlock(sdp); - wait_on_buffer(bd1->bd_bh); - ll_rw_block(WRITE, 1, - &bd1->bd_bh); - gfs2_log_lock(sdp); - } - brelse(bd1->bd_bh); - continue; - } - continue; - } else if (bd1->bd_bh) { /* A journaled buffer */ - int magic; - gfs2_log_unlock(sdp); - if (!bh) { - bh = gfs2_log_get_buf(sdp); - sdp->sd_log_num_hdrs++; - ld = (struct gfs2_log_descriptor *) - bh->b_data; - ptr = (__be64 *)(bh->b_data + offset); - ld->ld_header.mh_magic = - cpu_to_be32(GFS2_MAGIC); - ld->ld_header.mh_type = - cpu_to_be32(GFS2_METATYPE_LD); - ld->ld_header.mh_format = - cpu_to_be32(GFS2_FORMAT_LD); - ld->ld_type = - cpu_to_be32(GFS2_LOG_DESC_JDATA); - ld->ld_length = cpu_to_be32(num + 1); - ld->ld_data1 = cpu_to_be32(num); - ld->ld_data2 = cpu_to_be32(0); - memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved)); - } - magic = gfs2_check_magic(bd1->bd_bh); - *ptr++ = cpu_to_be64(bd1->bd_bh->b_blocknr); - *ptr++ = cpu_to_be64((__u64)magic); - clear_buffer_escaped(bd1->bd_bh); - if (unlikely(magic != 0)) - set_buffer_escaped(bd1->bd_bh); - gfs2_log_lock(sdp); - if (n++ > num) - break; - } else if (!bd1->bd_bh) { - total_dbuf--; - sdp->sd_log_num_databuf--; - list_del_init(&bd1->bd_le.le_list); - if (bd1 == bd2) { - bd2 = NULL; - bd2 = list_prepare_entry(bd2, - &sdp->sd_log_le_databuf, - bd_le.le_list); - } - kmem_cache_free(gfs2_bufdata_cachep, bd1); - } - } - gfs2_log_unlock(sdp); - if (bh) { - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - bh = NULL; - } - n = 0; - gfs2_log_lock(sdp); - list_for_each_entry_continue(bd2, &sdp->sd_log_le_databuf, - bd_le.le_list) { - if (!bd2->bd_bh) - continue; - /* copy buffer if it needs escaping */ - gfs2_log_unlock(sdp); - if (unlikely(buffer_escaped(bd2->bd_bh))) { - void *kaddr; - struct page *page = bd2->bd_bh->b_page; - bh = gfs2_log_get_buf(sdp); - kaddr = kmap_atomic(page, KM_USER0); - memcpy(bh->b_data, - kaddr + bh_offset(bd2->bd_bh), - sdp->sd_sb.sb_bsize); - kunmap_atomic(page, KM_USER0); - *(__be32 *)bh->b_data = 0; - } else { - bh = gfs2_log_fake_buf(sdp, bd2->bd_bh); - } - set_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - gfs2_log_lock(sdp); - if (++n >= num) - break; - } - bh = NULL; - total_dbuf -= num; - total_jdata -= num; - } - gfs2_log_unlock(sdp); - - /* Wait on all ordered buffers */ - while (!list_empty(&started)) { - gfs2_log_lock(sdp); - bd1 = list_entry(started.next, struct gfs2_bufdata, - bd_le.le_list); - list_del_init(&bd1->bd_le.le_list); - sdp->sd_log_num_databuf--; - bh = bd1->bd_bh; - if (bh) { - bh->b_private = NULL; - get_bh(bh); - gfs2_log_unlock(sdp); - wait_on_buffer(bh); - brelse(bh); - } else - gfs2_log_unlock(sdp); - - kmem_cache_free(gfs2_bufdata_cachep, bd1); - } - - /* We've removed all the ordered write bufs here, so only jdata left */ - gfs2_assert_warn(sdp, sdp->sd_log_num_databuf == sdp->sd_log_num_jdata); -} - -static int databuf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start, - struct gfs2_log_descriptor *ld, - __be64 *ptr, int pass) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - struct gfs2_glock *gl = ip->i_gl; - unsigned int blks = be32_to_cpu(ld->ld_data1); - struct buffer_head *bh_log, *bh_ip; - u64 blkno; - u64 esc; - int error = 0; - - if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_JDATA) - return 0; - - gfs2_replay_incr_blk(sdp, &start); - for (; blks; gfs2_replay_incr_blk(sdp, &start), blks--) { - blkno = be64_to_cpu(*ptr++); - esc = be64_to_cpu(*ptr++); - - sdp->sd_found_blocks++; - - if (gfs2_revoke_check(sdp, blkno, start)) - continue; - - error = gfs2_replay_read_block(jd, start, &bh_log); - if (error) - return error; - - bh_ip = gfs2_meta_new(gl, blkno); - memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size); - - /* Unescape */ - if (esc) { - __be32 *eptr = (__be32 *)bh_ip->b_data; - *eptr = cpu_to_be32(GFS2_MAGIC); - } - mark_buffer_dirty(bh_ip); - - brelse(bh_log); - brelse(bh_ip); - if (error) - break; - - sdp->sd_replayed_blocks++; - } - - return error; -} - -/* FIXME: sort out accounting for log blocks etc. */ - -static void databuf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - - if (error) { - gfs2_meta_sync(ip->i_gl); - return; - } - if (pass != 1) - return; - - /* data sync? */ - gfs2_meta_sync(ip->i_gl); - - fs_info(sdp, "jid=%u: Replayed %u of %u data blocks\n", - jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks); -} - -static void databuf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - struct list_head *head = &sdp->sd_log_le_databuf; - struct gfs2_bufdata *bd; - - while (!list_empty(head)) { - bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list); - list_del_init(&bd->bd_le.le_list); - sdp->sd_log_num_databuf--; - sdp->sd_log_num_jdata--; - gfs2_unpin(sdp, bd->bd_bh, ai); - } - gfs2_assert_warn(sdp, !sdp->sd_log_num_databuf); - gfs2_assert_warn(sdp, !sdp->sd_log_num_jdata); -} - - -const struct gfs2_log_operations gfs2_glock_lops = { - .lo_add = glock_lo_add, - .lo_after_commit = glock_lo_after_commit, - .lo_name = "glock", -}; - -const struct gfs2_log_operations gfs2_buf_lops = { - .lo_add = buf_lo_add, - .lo_incore_commit = buf_lo_incore_commit, - .lo_before_commit = buf_lo_before_commit, - .lo_after_commit = buf_lo_after_commit, - .lo_before_scan = buf_lo_before_scan, - .lo_scan_elements = buf_lo_scan_elements, - .lo_after_scan = buf_lo_after_scan, - .lo_name = "buf", -}; - -const struct gfs2_log_operations gfs2_revoke_lops = { - .lo_add = revoke_lo_add, - .lo_before_commit = revoke_lo_before_commit, - .lo_before_scan = revoke_lo_before_scan, - .lo_scan_elements = revoke_lo_scan_elements, - .lo_after_scan = revoke_lo_after_scan, - .lo_name = "revoke", -}; - -const struct gfs2_log_operations gfs2_rg_lops = { - .lo_add = rg_lo_add, - .lo_after_commit = rg_lo_after_commit, - .lo_name = "rg", -}; - -const struct gfs2_log_operations gfs2_databuf_lops = { - .lo_add = databuf_lo_add, - .lo_incore_commit = buf_lo_incore_commit, - .lo_before_commit = databuf_lo_before_commit, - .lo_after_commit = databuf_lo_after_commit, - .lo_scan_elements = databuf_lo_scan_elements, - .lo_after_scan = databuf_lo_after_scan, - .lo_name = "databuf", -}; - -const struct gfs2_log_operations *gfs2_log_ops[] = { - &gfs2_glock_lops, - &gfs2_buf_lops, - &gfs2_revoke_lops, - &gfs2_rg_lops, - &gfs2_databuf_lops, - NULL, -}; - diff --git a/trunk/fs/gfs2/lops.h b/trunk/fs/gfs2/lops.h deleted file mode 100644 index 5839c05ae6be..000000000000 --- a/trunk/fs/gfs2/lops.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __LOPS_DOT_H__ -#define __LOPS_DOT_H__ - -#include -#include "incore.h" - -extern const struct gfs2_log_operations gfs2_glock_lops; -extern const struct gfs2_log_operations gfs2_buf_lops; -extern const struct gfs2_log_operations gfs2_revoke_lops; -extern const struct gfs2_log_operations gfs2_rg_lops; -extern const struct gfs2_log_operations gfs2_databuf_lops; - -extern const struct gfs2_log_operations *gfs2_log_ops[]; - -static inline void lops_init_le(struct gfs2_log_element *le, - const struct gfs2_log_operations *lops) -{ - INIT_LIST_HEAD(&le->le_list); - le->le_ops = lops; -} - -static inline void lops_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) -{ - if (le->le_ops->lo_add) - le->le_ops->lo_add(sdp, le); -} - -static inline void lops_incore_commit(struct gfs2_sbd *sdp, - struct gfs2_trans *tr) -{ - int x; - for (x = 0; gfs2_log_ops[x]; x++) - if (gfs2_log_ops[x]->lo_incore_commit) - gfs2_log_ops[x]->lo_incore_commit(sdp, tr); -} - -static inline void lops_before_commit(struct gfs2_sbd *sdp) -{ - int x; - for (x = 0; gfs2_log_ops[x]; x++) - if (gfs2_log_ops[x]->lo_before_commit) - gfs2_log_ops[x]->lo_before_commit(sdp); -} - -static inline void lops_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai) -{ - int x; - for (x = 0; gfs2_log_ops[x]; x++) - if (gfs2_log_ops[x]->lo_after_commit) - gfs2_log_ops[x]->lo_after_commit(sdp, ai); -} - -static inline void lops_before_scan(struct gfs2_jdesc *jd, - struct gfs2_log_header *head, - unsigned int pass) -{ - int x; - for (x = 0; gfs2_log_ops[x]; x++) - if (gfs2_log_ops[x]->lo_before_scan) - gfs2_log_ops[x]->lo_before_scan(jd, head, pass); -} - -static inline int lops_scan_elements(struct gfs2_jdesc *jd, unsigned int start, - struct gfs2_log_descriptor *ld, - __be64 *ptr, - unsigned int pass) -{ - int x, error; - for (x = 0; gfs2_log_ops[x]; x++) - if (gfs2_log_ops[x]->lo_scan_elements) { - error = gfs2_log_ops[x]->lo_scan_elements(jd, start, - ld, ptr, pass); - if (error) - return error; - } - - return 0; -} - -static inline void lops_after_scan(struct gfs2_jdesc *jd, int error, - unsigned int pass) -{ - int x; - for (x = 0; gfs2_log_ops[x]; x++) - if (gfs2_log_ops[x]->lo_before_scan) - gfs2_log_ops[x]->lo_after_scan(jd, error, pass); -} - -#endif /* __LOPS_DOT_H__ */ - diff --git a/trunk/fs/gfs2/main.c b/trunk/fs/gfs2/main.c deleted file mode 100644 index 21508a13bb78..000000000000 --- a/trunk/fs/gfs2/main.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "ops_fstype.h" -#include "sys.h" -#include "util.h" -#include "glock.h" - -static void gfs2_init_inode_once(void *foo, kmem_cache_t *cachep, unsigned long flags) -{ - struct gfs2_inode *ip = foo; - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) { - inode_init_once(&ip->i_inode); - spin_lock_init(&ip->i_spin); - init_rwsem(&ip->i_rw_mutex); - memset(ip->i_cache, 0, sizeof(ip->i_cache)); - } -} - -static void gfs2_init_glock_once(void *foo, kmem_cache_t *cachep, unsigned long flags) -{ - struct gfs2_glock *gl = foo; - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) { - INIT_HLIST_NODE(&gl->gl_list); - spin_lock_init(&gl->gl_spin); - INIT_LIST_HEAD(&gl->gl_holders); - INIT_LIST_HEAD(&gl->gl_waiters1); - INIT_LIST_HEAD(&gl->gl_waiters2); - INIT_LIST_HEAD(&gl->gl_waiters3); - gl->gl_lvb = NULL; - atomic_set(&gl->gl_lvb_count, 0); - INIT_LIST_HEAD(&gl->gl_reclaim); - INIT_LIST_HEAD(&gl->gl_ail_list); - atomic_set(&gl->gl_ail_count, 0); - } -} - -/** - * init_gfs2_fs - Register GFS2 as a filesystem - * - * Returns: 0 on success, error code on failure - */ - -static int __init init_gfs2_fs(void) -{ - int error; - - error = gfs2_sys_init(); - if (error) - return error; - - error = gfs2_glock_init(); - if (error) - goto fail; - - error = -ENOMEM; - gfs2_glock_cachep = kmem_cache_create("gfs2_glock", - sizeof(struct gfs2_glock), - 0, 0, - gfs2_init_glock_once, NULL); - if (!gfs2_glock_cachep) - goto fail; - - gfs2_inode_cachep = kmem_cache_create("gfs2_inode", - sizeof(struct gfs2_inode), - 0, (SLAB_RECLAIM_ACCOUNT| - SLAB_PANIC|SLAB_MEM_SPREAD), - gfs2_init_inode_once, NULL); - if (!gfs2_inode_cachep) - goto fail; - - gfs2_bufdata_cachep = kmem_cache_create("gfs2_bufdata", - sizeof(struct gfs2_bufdata), - 0, 0, NULL, NULL); - if (!gfs2_bufdata_cachep) - goto fail; - - error = register_filesystem(&gfs2_fs_type); - if (error) - goto fail; - - error = register_filesystem(&gfs2meta_fs_type); - if (error) - goto fail_unregister; - - printk("GFS2 (built %s %s) installed\n", __DATE__, __TIME__); - - return 0; - -fail_unregister: - unregister_filesystem(&gfs2_fs_type); -fail: - if (gfs2_bufdata_cachep) - kmem_cache_destroy(gfs2_bufdata_cachep); - - if (gfs2_inode_cachep) - kmem_cache_destroy(gfs2_inode_cachep); - - if (gfs2_glock_cachep) - kmem_cache_destroy(gfs2_glock_cachep); - - gfs2_sys_uninit(); - return error; -} - -/** - * exit_gfs2_fs - Unregister the file system - * - */ - -static void __exit exit_gfs2_fs(void) -{ - unregister_filesystem(&gfs2_fs_type); - unregister_filesystem(&gfs2meta_fs_type); - - kmem_cache_destroy(gfs2_bufdata_cachep); - kmem_cache_destroy(gfs2_inode_cachep); - kmem_cache_destroy(gfs2_glock_cachep); - - gfs2_sys_uninit(); -} - -MODULE_DESCRIPTION("Global File System"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - -module_init(init_gfs2_fs); -module_exit(exit_gfs2_fs); - diff --git a/trunk/fs/gfs2/meta_io.c b/trunk/fs/gfs2/meta_io.c deleted file mode 100644 index 3912d6a4b1e6..000000000000 --- a/trunk/fs/gfs2/meta_io.c +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "log.h" -#include "lops.h" -#include "meta_io.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" -#include "ops_address.h" - -static int aspace_get_block(struct inode *inode, sector_t lblock, - struct buffer_head *bh_result, int create) -{ - gfs2_assert_warn(inode->i_sb->s_fs_info, 0); - return -EOPNOTSUPP; -} - -static int gfs2_aspace_writepage(struct page *page, - struct writeback_control *wbc) -{ - return block_write_full_page(page, aspace_get_block, wbc); -} - -static const struct address_space_operations aspace_aops = { - .writepage = gfs2_aspace_writepage, - .releasepage = gfs2_releasepage, -}; - -/** - * gfs2_aspace_get - Create and initialize a struct inode structure - * @sdp: the filesystem the aspace is in - * - * Right now a struct inode is just a struct inode. Maybe Linux - * will supply a more lightweight address space construct (that works) - * in the future. - * - * Make sure pages/buffers in this aspace aren't in high memory. - * - * Returns: the aspace - */ - -struct inode *gfs2_aspace_get(struct gfs2_sbd *sdp) -{ - struct inode *aspace; - - aspace = new_inode(sdp->sd_vfs); - if (aspace) { - mapping_set_gfp_mask(aspace->i_mapping, GFP_NOFS); - aspace->i_mapping->a_ops = &aspace_aops; - aspace->i_size = ~0ULL; - aspace->i_private = NULL; - insert_inode_hash(aspace); - } - return aspace; -} - -void gfs2_aspace_put(struct inode *aspace) -{ - remove_inode_hash(aspace); - iput(aspace); -} - -/** - * gfs2_meta_inval - Invalidate all buffers associated with a glock - * @gl: the glock - * - */ - -void gfs2_meta_inval(struct gfs2_glock *gl) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - struct inode *aspace = gl->gl_aspace; - struct address_space *mapping = gl->gl_aspace->i_mapping; - - gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); - - atomic_inc(&aspace->i_writecount); - truncate_inode_pages(mapping, 0); - atomic_dec(&aspace->i_writecount); - - gfs2_assert_withdraw(sdp, !mapping->nrpages); -} - -/** - * gfs2_meta_sync - Sync all buffers associated with a glock - * @gl: The glock - * - */ - -void gfs2_meta_sync(struct gfs2_glock *gl) -{ - struct address_space *mapping = gl->gl_aspace->i_mapping; - int error; - - filemap_fdatawrite(mapping); - error = filemap_fdatawait(mapping); - - if (error) - gfs2_io_error(gl->gl_sbd); -} - -/** - * getbuf - Get a buffer with a given address space - * @sdp: the filesystem - * @aspace: the address space - * @blkno: the block number (filesystem scope) - * @create: 1 if the buffer should be created - * - * Returns: the buffer - */ - -static struct buffer_head *getbuf(struct gfs2_sbd *sdp, struct inode *aspace, - u64 blkno, int create) -{ - struct page *page; - struct buffer_head *bh; - unsigned int shift; - unsigned long index; - unsigned int bufnum; - - shift = PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift; - index = blkno >> shift; /* convert block to page */ - bufnum = blkno - (index << shift); /* block buf index within page */ - - if (create) { - for (;;) { - page = grab_cache_page(aspace->i_mapping, index); - if (page) - break; - yield(); - } - } else { - page = find_lock_page(aspace->i_mapping, index); - if (!page) - return NULL; - } - - if (!page_has_buffers(page)) - create_empty_buffers(page, sdp->sd_sb.sb_bsize, 0); - - /* Locate header for our buffer within our page */ - for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page) - /* Do nothing */; - get_bh(bh); - - if (!buffer_mapped(bh)) - map_bh(bh, sdp->sd_vfs, blkno); - - unlock_page(page); - mark_page_accessed(page); - page_cache_release(page); - - return bh; -} - -static void meta_prep_new(struct buffer_head *bh) -{ - struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data; - - lock_buffer(bh); - clear_buffer_dirty(bh); - set_buffer_uptodate(bh); - unlock_buffer(bh); - - mh->mh_magic = cpu_to_be32(GFS2_MAGIC); -} - -/** - * gfs2_meta_new - Get a block - * @gl: The glock associated with this block - * @blkno: The block number - * - * Returns: The buffer - */ - -struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno) -{ - struct buffer_head *bh; - bh = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE); - meta_prep_new(bh); - return bh; -} - -/** - * gfs2_meta_read - Read a block from disk - * @gl: The glock covering the block - * @blkno: The block number - * @flags: flags - * @bhp: the place where the buffer is returned (NULL on failure) - * - * Returns: errno - */ - -int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, - struct buffer_head **bhp) -{ - *bhp = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE); - if (!buffer_uptodate(*bhp)) - ll_rw_block(READ_META, 1, bhp); - if (flags & DIO_WAIT) { - int error = gfs2_meta_wait(gl->gl_sbd, *bhp); - if (error) { - brelse(*bhp); - return error; - } - } - - return 0; -} - -/** - * gfs2_meta_wait - Reread a block from disk - * @sdp: the filesystem - * @bh: The block to wait for - * - * Returns: errno - */ - -int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh) -{ - if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - return -EIO; - - wait_on_buffer(bh); - - if (!buffer_uptodate(bh)) { - struct gfs2_trans *tr = current->journal_info; - if (tr && tr->tr_touched) - gfs2_io_error_bh(sdp, bh); - return -EIO; - } - if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - return -EIO; - - return 0; -} - -/** - * gfs2_attach_bufdata - attach a struct gfs2_bufdata structure to a buffer - * @gl: the glock the buffer belongs to - * @bh: The buffer to be attached to - * @meta: Flag to indicate whether its metadata or not - */ - -void gfs2_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh, - int meta) -{ - struct gfs2_bufdata *bd; - - if (meta) - lock_page(bh->b_page); - - if (bh->b_private) { - if (meta) - unlock_page(bh->b_page); - return; - } - - bd = kmem_cache_alloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL), - memset(bd, 0, sizeof(struct gfs2_bufdata)); - bd->bd_bh = bh; - bd->bd_gl = gl; - - INIT_LIST_HEAD(&bd->bd_list_tr); - if (meta) - lops_init_le(&bd->bd_le, &gfs2_buf_lops); - else - lops_init_le(&bd->bd_le, &gfs2_databuf_lops); - bh->b_private = bd; - - if (meta) - unlock_page(bh->b_page); -} - -/** - * gfs2_pin - Pin a buffer in memory - * @sdp: the filesystem the buffer belongs to - * @bh: The buffer to be pinned - * - */ - -void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) -{ - struct gfs2_bufdata *bd = bh->b_private; - - gfs2_assert_withdraw(sdp, test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)); - - if (test_set_buffer_pinned(bh)) - gfs2_assert_withdraw(sdp, 0); - - wait_on_buffer(bh); - - /* If this buffer is in the AIL and it has already been written - to in-place disk block, remove it from the AIL. */ - - gfs2_log_lock(sdp); - if (bd->bd_ail && !buffer_in_io(bh)) - list_move(&bd->bd_ail_st_list, &bd->bd_ail->ai_ail2_list); - gfs2_log_unlock(sdp); - - clear_buffer_dirty(bh); - wait_on_buffer(bh); - - if (!buffer_uptodate(bh)) - gfs2_io_error_bh(sdp, bh); - - get_bh(bh); -} - -/** - * gfs2_unpin - Unpin a buffer - * @sdp: the filesystem the buffer belongs to - * @bh: The buffer to unpin - * @ai: - * - */ - -void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, - struct gfs2_ail *ai) -{ - struct gfs2_bufdata *bd = bh->b_private; - - gfs2_assert_withdraw(sdp, buffer_uptodate(bh)); - - if (!buffer_pinned(bh)) - gfs2_assert_withdraw(sdp, 0); - - mark_buffer_dirty(bh); - clear_buffer_pinned(bh); - - gfs2_log_lock(sdp); - if (bd->bd_ail) { - list_del(&bd->bd_ail_st_list); - brelse(bh); - } else { - struct gfs2_glock *gl = bd->bd_gl; - list_add(&bd->bd_ail_gl_list, &gl->gl_ail_list); - atomic_inc(&gl->gl_ail_count); - } - bd->bd_ail = ai; - list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list); - gfs2_log_unlock(sdp); -} - -/** - * gfs2_meta_wipe - make inode's buffers so they aren't dirty/pinned anymore - * @ip: the inode who owns the buffers - * @bstart: the first buffer in the run - * @blen: the number of buffers in the run - * - */ - -void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct inode *aspace = ip->i_gl->gl_aspace; - struct buffer_head *bh; - - while (blen) { - bh = getbuf(sdp, aspace, bstart, NO_CREATE); - if (bh) { - struct gfs2_bufdata *bd = bh->b_private; - - if (test_clear_buffer_pinned(bh)) { - struct gfs2_trans *tr = current->journal_info; - gfs2_log_lock(sdp); - list_del_init(&bd->bd_le.le_list); - gfs2_assert_warn(sdp, sdp->sd_log_num_buf); - sdp->sd_log_num_buf--; - gfs2_log_unlock(sdp); - tr->tr_num_buf_rm++; - brelse(bh); - } - if (bd) { - gfs2_log_lock(sdp); - if (bd->bd_ail) { - u64 blkno = bh->b_blocknr; - bd->bd_ail = NULL; - list_del(&bd->bd_ail_st_list); - list_del(&bd->bd_ail_gl_list); - atomic_dec(&bd->bd_gl->gl_ail_count); - brelse(bh); - gfs2_log_unlock(sdp); - gfs2_trans_add_revoke(sdp, blkno); - } else - gfs2_log_unlock(sdp); - } - - lock_buffer(bh); - clear_buffer_dirty(bh); - clear_buffer_uptodate(bh); - unlock_buffer(bh); - - brelse(bh); - } - - bstart++; - blen--; - } -} - -/** - * gfs2_meta_cache_flush - get rid of any references on buffers for this inode - * @ip: The GFS2 inode - * - * This releases buffers that are in the most-recently-used array of - * blocks used for indirect block addressing for this inode. - */ - -void gfs2_meta_cache_flush(struct gfs2_inode *ip) -{ - struct buffer_head **bh_slot; - unsigned int x; - - spin_lock(&ip->i_spin); - - for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) { - bh_slot = &ip->i_cache[x]; - if (!*bh_slot) - break; - brelse(*bh_slot); - *bh_slot = NULL; - } - - spin_unlock(&ip->i_spin); -} - -/** - * gfs2_meta_indirect_buffer - Get a metadata buffer - * @ip: The GFS2 inode - * @height: The level of this buf in the metadata (indir addr) tree (if any) - * @num: The block number (device relative) of the buffer - * @new: Non-zero if we may create a new buffer - * @bhp: the buffer is returned here - * - * Try to use the gfs2_inode's MRU metadata tree cache. - * - * Returns: errno - */ - -int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num, - int new, struct buffer_head **bhp) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_glock *gl = ip->i_gl; - struct buffer_head *bh = NULL, **bh_slot = ip->i_cache + height; - int in_cache = 0; - - spin_lock(&ip->i_spin); - if (*bh_slot && (*bh_slot)->b_blocknr == num) { - bh = *bh_slot; - get_bh(bh); - in_cache = 1; - } - spin_unlock(&ip->i_spin); - - if (!bh) - bh = getbuf(gl->gl_sbd, gl->gl_aspace, num, CREATE); - - if (!bh) - return -ENOBUFS; - - if (new) { - if (gfs2_assert_warn(sdp, height)) - goto err; - meta_prep_new(bh); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_metatype_set(bh, GFS2_METATYPE_IN, GFS2_FORMAT_IN); - gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header)); - } else { - u32 mtype = height ? GFS2_METATYPE_IN : GFS2_METATYPE_DI; - if (!buffer_uptodate(bh)) { - ll_rw_block(READ_META, 1, &bh); - if (gfs2_meta_wait(sdp, bh)) - goto err; - } - if (gfs2_metatype_check(sdp, bh, mtype)) - goto err; - } - - if (!in_cache) { - spin_lock(&ip->i_spin); - if (*bh_slot) - brelse(*bh_slot); - *bh_slot = bh; - get_bh(bh); - spin_unlock(&ip->i_spin); - } - - *bhp = bh; - return 0; -err: - brelse(bh); - return -EIO; -} - -/** - * gfs2_meta_ra - start readahead on an extent of a file - * @gl: the glock the blocks belong to - * @dblock: the starting disk block - * @extlen: the number of blocks in the extent - * - * returns: the first buffer in the extent - */ - -struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - struct inode *aspace = gl->gl_aspace; - struct buffer_head *first_bh, *bh; - u32 max_ra = gfs2_tune_get(sdp, gt_max_readahead) >> - sdp->sd_sb.sb_bsize_shift; - - BUG_ON(!extlen); - - if (max_ra < 1) - max_ra = 1; - if (extlen > max_ra) - extlen = max_ra; - - first_bh = getbuf(sdp, aspace, dblock, CREATE); - - if (buffer_uptodate(first_bh)) - goto out; - if (!buffer_locked(first_bh)) - ll_rw_block(READ_META, 1, &first_bh); - - dblock++; - extlen--; - - while (extlen) { - bh = getbuf(sdp, aspace, dblock, CREATE); - - if (!buffer_uptodate(bh) && !buffer_locked(bh)) - ll_rw_block(READA, 1, &bh); - brelse(bh); - dblock++; - extlen--; - if (!buffer_locked(first_bh) && buffer_uptodate(first_bh)) - goto out; - } - - wait_on_buffer(first_bh); -out: - return first_bh; -} - -/** - * gfs2_meta_syncfs - sync all the buffers in a filesystem - * @sdp: the filesystem - * - */ - -void gfs2_meta_syncfs(struct gfs2_sbd *sdp) -{ - gfs2_log_flush(sdp, NULL); - for (;;) { - gfs2_ail1_start(sdp, DIO_ALL); - if (gfs2_ail1_empty(sdp, DIO_ALL)) - break; - msleep(10); - } -} - diff --git a/trunk/fs/gfs2/meta_io.h b/trunk/fs/gfs2/meta_io.h deleted file mode 100644 index 3ec939e20dff..000000000000 --- a/trunk/fs/gfs2/meta_io.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __DIO_DOT_H__ -#define __DIO_DOT_H__ - -#include -#include -#include "incore.h" - -static inline void gfs2_buffer_clear(struct buffer_head *bh) -{ - memset(bh->b_data, 0, bh->b_size); -} - -static inline void gfs2_buffer_clear_tail(struct buffer_head *bh, int head) -{ - BUG_ON(head > bh->b_size); - memset(bh->b_data + head, 0, bh->b_size - head); -} - -static inline void gfs2_buffer_copy_tail(struct buffer_head *to_bh, - int to_head, - struct buffer_head *from_bh, - int from_head) -{ - BUG_ON(from_head < to_head); - memcpy(to_bh->b_data + to_head, from_bh->b_data + from_head, - from_bh->b_size - from_head); - memset(to_bh->b_data + to_bh->b_size + to_head - from_head, - 0, from_head - to_head); -} - -struct inode *gfs2_aspace_get(struct gfs2_sbd *sdp); -void gfs2_aspace_put(struct inode *aspace); - -void gfs2_meta_inval(struct gfs2_glock *gl); -void gfs2_meta_sync(struct gfs2_glock *gl); - -struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno); -int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, - int flags, struct buffer_head **bhp); -int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh); - -void gfs2_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh, - int meta); -void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh); -void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, - struct gfs2_ail *ai); - -void gfs2_meta_wipe(struct gfs2_inode *ip, u64 bstart, u32 blen); - -void gfs2_meta_cache_flush(struct gfs2_inode *ip); -int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num, - int new, struct buffer_head **bhp); - -static inline int gfs2_meta_inode_buffer(struct gfs2_inode *ip, - struct buffer_head **bhp) -{ - return gfs2_meta_indirect_buffer(ip, 0, ip->i_num.no_addr, 0, bhp); -} - -struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen); -void gfs2_meta_syncfs(struct gfs2_sbd *sdp); - -#define buffer_busy(bh) \ -((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock) | (1ul << BH_Pinned))) -#define buffer_in_io(bh) \ -((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock))) - -#endif /* __DIO_DOT_H__ */ - diff --git a/trunk/fs/gfs2/mount.c b/trunk/fs/gfs2/mount.c deleted file mode 100644 index ef3092e29607..000000000000 --- a/trunk/fs/gfs2/mount.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "mount.h" -#include "sys.h" -#include "util.h" - -/** - * gfs2_mount_args - Parse mount options - * @sdp: - * @data: - * - * Return: errno - */ - -int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount) -{ - struct gfs2_args *args = &sdp->sd_args; - char *data = data_arg; - char *options, *o, *v; - int error = 0; - - if (!remount) { - /* If someone preloaded options, use those instead */ - spin_lock(&gfs2_sys_margs_lock); - if (gfs2_sys_margs) { - data = gfs2_sys_margs; - gfs2_sys_margs = NULL; - } - spin_unlock(&gfs2_sys_margs_lock); - - /* Set some defaults */ - args->ar_num_glockd = GFS2_GLOCKD_DEFAULT; - args->ar_quota = GFS2_QUOTA_DEFAULT; - args->ar_data = GFS2_DATA_DEFAULT; - } - - /* Split the options into tokens with the "," character and - process them */ - - for (options = data; (o = strsep(&options, ",")); ) { - if (!*o) - continue; - - v = strchr(o, '='); - if (v) - *v++ = 0; - - if (!strcmp(o, "lockproto")) { - if (!v) - goto need_value; - if (remount && strcmp(v, args->ar_lockproto)) - goto cant_remount; - strncpy(args->ar_lockproto, v, GFS2_LOCKNAME_LEN); - args->ar_lockproto[GFS2_LOCKNAME_LEN - 1] = 0; - } - - else if (!strcmp(o, "locktable")) { - if (!v) - goto need_value; - if (remount && strcmp(v, args->ar_locktable)) - goto cant_remount; - strncpy(args->ar_locktable, v, GFS2_LOCKNAME_LEN); - args->ar_locktable[GFS2_LOCKNAME_LEN - 1] = 0; - } - - else if (!strcmp(o, "hostdata")) { - if (!v) - goto need_value; - if (remount && strcmp(v, args->ar_hostdata)) - goto cant_remount; - strncpy(args->ar_hostdata, v, GFS2_LOCKNAME_LEN); - args->ar_hostdata[GFS2_LOCKNAME_LEN - 1] = 0; - } - - else if (!strcmp(o, "spectator")) { - if (remount && !args->ar_spectator) - goto cant_remount; - args->ar_spectator = 1; - sdp->sd_vfs->s_flags |= MS_RDONLY; - } - - else if (!strcmp(o, "ignore_local_fs")) { - if (remount && !args->ar_ignore_local_fs) - goto cant_remount; - args->ar_ignore_local_fs = 1; - } - - else if (!strcmp(o, "localflocks")) { - if (remount && !args->ar_localflocks) - goto cant_remount; - args->ar_localflocks = 1; - } - - else if (!strcmp(o, "localcaching")) { - if (remount && !args->ar_localcaching) - goto cant_remount; - args->ar_localcaching = 1; - } - - else if (!strcmp(o, "debug")) - args->ar_debug = 1; - - else if (!strcmp(o, "nodebug")) - args->ar_debug = 0; - - else if (!strcmp(o, "upgrade")) { - if (remount && !args->ar_upgrade) - goto cant_remount; - args->ar_upgrade = 1; - } - - else if (!strcmp(o, "num_glockd")) { - unsigned int x; - if (!v) - goto need_value; - sscanf(v, "%u", &x); - if (remount && x != args->ar_num_glockd) - goto cant_remount; - if (!x || x > GFS2_GLOCKD_MAX) { - fs_info(sdp, "0 < num_glockd <= %u (not %u)\n", - GFS2_GLOCKD_MAX, x); - error = -EINVAL; - break; - } - args->ar_num_glockd = x; - } - - else if (!strcmp(o, "acl")) { - args->ar_posix_acl = 1; - sdp->sd_vfs->s_flags |= MS_POSIXACL; - } - - else if (!strcmp(o, "noacl")) { - args->ar_posix_acl = 0; - sdp->sd_vfs->s_flags &= ~MS_POSIXACL; - } - - else if (!strcmp(o, "quota")) { - if (!v) - goto need_value; - if (!strcmp(v, "off")) - args->ar_quota = GFS2_QUOTA_OFF; - else if (!strcmp(v, "account")) - args->ar_quota = GFS2_QUOTA_ACCOUNT; - else if (!strcmp(v, "on")) - args->ar_quota = GFS2_QUOTA_ON; - else { - fs_info(sdp, "invalid value for quota\n"); - error = -EINVAL; - break; - } - } - - else if (!strcmp(o, "suiddir")) - args->ar_suiddir = 1; - - else if (!strcmp(o, "nosuiddir")) - args->ar_suiddir = 0; - - else if (!strcmp(o, "data")) { - if (!v) - goto need_value; - if (!strcmp(v, "writeback")) - args->ar_data = GFS2_DATA_WRITEBACK; - else if (!strcmp(v, "ordered")) - args->ar_data = GFS2_DATA_ORDERED; - else { - fs_info(sdp, "invalid value for data\n"); - error = -EINVAL; - break; - } - } - - else { - fs_info(sdp, "unknown option: %s\n", o); - error = -EINVAL; - break; - } - } - - if (error) - fs_info(sdp, "invalid mount option(s)\n"); - - if (data != data_arg) - kfree(data); - - return error; - -need_value: - fs_info(sdp, "need value for option %s\n", o); - return -EINVAL; - -cant_remount: - fs_info(sdp, "can't remount with option %s\n", o); - return -EINVAL; -} - diff --git a/trunk/fs/gfs2/mount.h b/trunk/fs/gfs2/mount.h deleted file mode 100644 index 401288acfdf3..000000000000 --- a/trunk/fs/gfs2/mount.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __MOUNT_DOT_H__ -#define __MOUNT_DOT_H__ - -struct gfs2_sbd; - -int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount); - -#endif /* __MOUNT_DOT_H__ */ diff --git a/trunk/fs/gfs2/ondisk.c b/trunk/fs/gfs2/ondisk.c deleted file mode 100644 index 1025960b0e6e..000000000000 --- a/trunk/fs/gfs2/ondisk.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include - -#define pv(struct, member, fmt) printk(KERN_INFO " "#member" = "fmt"\n", \ - struct->member); - -/* - * gfs2_xxx_in - read in an xxx struct - * first arg: the cpu-order structure - * buf: the disk-order buffer - * - * gfs2_xxx_out - write out an xxx struct - * first arg: the cpu-order structure - * buf: the disk-order buffer - * - * gfs2_xxx_print - print out an xxx struct - * first arg: the cpu-order structure - */ - -void gfs2_inum_in(struct gfs2_inum *no, const void *buf) -{ - const struct gfs2_inum *str = buf; - - no->no_formal_ino = be64_to_cpu(str->no_formal_ino); - no->no_addr = be64_to_cpu(str->no_addr); -} - -void gfs2_inum_out(const struct gfs2_inum *no, void *buf) -{ - struct gfs2_inum *str = buf; - - str->no_formal_ino = cpu_to_be64(no->no_formal_ino); - str->no_addr = cpu_to_be64(no->no_addr); -} - -static void gfs2_inum_print(const struct gfs2_inum *no) -{ - printk(KERN_INFO " no_formal_ino = %llu\n", (unsigned long long)no->no_formal_ino); - printk(KERN_INFO " no_addr = %llu\n", (unsigned long long)no->no_addr); -} - -static void gfs2_meta_header_in(struct gfs2_meta_header *mh, const void *buf) -{ - const struct gfs2_meta_header *str = buf; - - mh->mh_magic = be32_to_cpu(str->mh_magic); - mh->mh_type = be32_to_cpu(str->mh_type); - mh->mh_format = be32_to_cpu(str->mh_format); -} - -static void gfs2_meta_header_out(const struct gfs2_meta_header *mh, void *buf) -{ - struct gfs2_meta_header *str = buf; - - str->mh_magic = cpu_to_be32(mh->mh_magic); - str->mh_type = cpu_to_be32(mh->mh_type); - str->mh_format = cpu_to_be32(mh->mh_format); -} - -static void gfs2_meta_header_print(const struct gfs2_meta_header *mh) -{ - pv(mh, mh_magic, "0x%.8X"); - pv(mh, mh_type, "%u"); - pv(mh, mh_format, "%u"); -} - -void gfs2_sb_in(struct gfs2_sb *sb, const void *buf) -{ - const struct gfs2_sb *str = buf; - - gfs2_meta_header_in(&sb->sb_header, buf); - - sb->sb_fs_format = be32_to_cpu(str->sb_fs_format); - sb->sb_multihost_format = be32_to_cpu(str->sb_multihost_format); - sb->sb_bsize = be32_to_cpu(str->sb_bsize); - sb->sb_bsize_shift = be32_to_cpu(str->sb_bsize_shift); - - gfs2_inum_in(&sb->sb_master_dir, (char *)&str->sb_master_dir); - gfs2_inum_in(&sb->sb_root_dir, (char *)&str->sb_root_dir); - - memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN); - memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN); -} - -void gfs2_rindex_in(struct gfs2_rindex *ri, const void *buf) -{ - const struct gfs2_rindex *str = buf; - - ri->ri_addr = be64_to_cpu(str->ri_addr); - ri->ri_length = be32_to_cpu(str->ri_length); - ri->ri_data0 = be64_to_cpu(str->ri_data0); - ri->ri_data = be32_to_cpu(str->ri_data); - ri->ri_bitbytes = be32_to_cpu(str->ri_bitbytes); - -} - -void gfs2_rindex_print(const struct gfs2_rindex *ri) -{ - printk(KERN_INFO " ri_addr = %llu\n", (unsigned long long)ri->ri_addr); - pv(ri, ri_length, "%u"); - - printk(KERN_INFO " ri_data0 = %llu\n", (unsigned long long)ri->ri_data0); - pv(ri, ri_data, "%u"); - - pv(ri, ri_bitbytes, "%u"); -} - -void gfs2_rgrp_in(struct gfs2_rgrp *rg, const void *buf) -{ - const struct gfs2_rgrp *str = buf; - - gfs2_meta_header_in(&rg->rg_header, buf); - rg->rg_flags = be32_to_cpu(str->rg_flags); - rg->rg_free = be32_to_cpu(str->rg_free); - rg->rg_dinodes = be32_to_cpu(str->rg_dinodes); - rg->rg_igeneration = be64_to_cpu(str->rg_igeneration); -} - -void gfs2_rgrp_out(const struct gfs2_rgrp *rg, void *buf) -{ - struct gfs2_rgrp *str = buf; - - gfs2_meta_header_out(&rg->rg_header, buf); - str->rg_flags = cpu_to_be32(rg->rg_flags); - str->rg_free = cpu_to_be32(rg->rg_free); - str->rg_dinodes = cpu_to_be32(rg->rg_dinodes); - str->__pad = cpu_to_be32(0); - str->rg_igeneration = cpu_to_be64(rg->rg_igeneration); - memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); -} - -void gfs2_quota_in(struct gfs2_quota *qu, const void *buf) -{ - const struct gfs2_quota *str = buf; - - qu->qu_limit = be64_to_cpu(str->qu_limit); - qu->qu_warn = be64_to_cpu(str->qu_warn); - qu->qu_value = be64_to_cpu(str->qu_value); -} - -void gfs2_dinode_in(struct gfs2_dinode *di, const void *buf) -{ - const struct gfs2_dinode *str = buf; - - gfs2_meta_header_in(&di->di_header, buf); - gfs2_inum_in(&di->di_num, &str->di_num); - - di->di_mode = be32_to_cpu(str->di_mode); - di->di_uid = be32_to_cpu(str->di_uid); - di->di_gid = be32_to_cpu(str->di_gid); - di->di_nlink = be32_to_cpu(str->di_nlink); - di->di_size = be64_to_cpu(str->di_size); - di->di_blocks = be64_to_cpu(str->di_blocks); - di->di_atime = be64_to_cpu(str->di_atime); - di->di_mtime = be64_to_cpu(str->di_mtime); - di->di_ctime = be64_to_cpu(str->di_ctime); - di->di_major = be32_to_cpu(str->di_major); - di->di_minor = be32_to_cpu(str->di_minor); - - di->di_goal_meta = be64_to_cpu(str->di_goal_meta); - di->di_goal_data = be64_to_cpu(str->di_goal_data); - di->di_generation = be64_to_cpu(str->di_generation); - - di->di_flags = be32_to_cpu(str->di_flags); - di->di_payload_format = be32_to_cpu(str->di_payload_format); - di->di_height = be16_to_cpu(str->di_height); - - di->di_depth = be16_to_cpu(str->di_depth); - di->di_entries = be32_to_cpu(str->di_entries); - - di->di_eattr = be64_to_cpu(str->di_eattr); - -} - -void gfs2_dinode_out(const struct gfs2_dinode *di, void *buf) -{ - struct gfs2_dinode *str = buf; - - gfs2_meta_header_out(&di->di_header, buf); - gfs2_inum_out(&di->di_num, (char *)&str->di_num); - - str->di_mode = cpu_to_be32(di->di_mode); - str->di_uid = cpu_to_be32(di->di_uid); - str->di_gid = cpu_to_be32(di->di_gid); - str->di_nlink = cpu_to_be32(di->di_nlink); - str->di_size = cpu_to_be64(di->di_size); - str->di_blocks = cpu_to_be64(di->di_blocks); - str->di_atime = cpu_to_be64(di->di_atime); - str->di_mtime = cpu_to_be64(di->di_mtime); - str->di_ctime = cpu_to_be64(di->di_ctime); - str->di_major = cpu_to_be32(di->di_major); - str->di_minor = cpu_to_be32(di->di_minor); - - str->di_goal_meta = cpu_to_be64(di->di_goal_meta); - str->di_goal_data = cpu_to_be64(di->di_goal_data); - str->di_generation = cpu_to_be64(di->di_generation); - - str->di_flags = cpu_to_be32(di->di_flags); - str->di_payload_format = cpu_to_be32(di->di_payload_format); - str->di_height = cpu_to_be16(di->di_height); - - str->di_depth = cpu_to_be16(di->di_depth); - str->di_entries = cpu_to_be32(di->di_entries); - - str->di_eattr = cpu_to_be64(di->di_eattr); - -} - -void gfs2_dinode_print(const struct gfs2_dinode *di) -{ - gfs2_meta_header_print(&di->di_header); - gfs2_inum_print(&di->di_num); - - pv(di, di_mode, "0%o"); - pv(di, di_uid, "%u"); - pv(di, di_gid, "%u"); - pv(di, di_nlink, "%u"); - printk(KERN_INFO " di_size = %llu\n", (unsigned long long)di->di_size); - printk(KERN_INFO " di_blocks = %llu\n", (unsigned long long)di->di_blocks); - printk(KERN_INFO " di_atime = %lld\n", (long long)di->di_atime); - printk(KERN_INFO " di_mtime = %lld\n", (long long)di->di_mtime); - printk(KERN_INFO " di_ctime = %lld\n", (long long)di->di_ctime); - pv(di, di_major, "%u"); - pv(di, di_minor, "%u"); - - printk(KERN_INFO " di_goal_meta = %llu\n", (unsigned long long)di->di_goal_meta); - printk(KERN_INFO " di_goal_data = %llu\n", (unsigned long long)di->di_goal_data); - - pv(di, di_flags, "0x%.8X"); - pv(di, di_payload_format, "%u"); - pv(di, di_height, "%u"); - - pv(di, di_depth, "%u"); - pv(di, di_entries, "%u"); - - printk(KERN_INFO " di_eattr = %llu\n", (unsigned long long)di->di_eattr); -} - -void gfs2_log_header_in(struct gfs2_log_header *lh, const void *buf) -{ - const struct gfs2_log_header *str = buf; - - gfs2_meta_header_in(&lh->lh_header, buf); - lh->lh_sequence = be64_to_cpu(str->lh_sequence); - lh->lh_flags = be32_to_cpu(str->lh_flags); - lh->lh_tail = be32_to_cpu(str->lh_tail); - lh->lh_blkno = be32_to_cpu(str->lh_blkno); - lh->lh_hash = be32_to_cpu(str->lh_hash); -} - -void gfs2_inum_range_in(struct gfs2_inum_range *ir, const void *buf) -{ - const struct gfs2_inum_range *str = buf; - - ir->ir_start = be64_to_cpu(str->ir_start); - ir->ir_length = be64_to_cpu(str->ir_length); -} - -void gfs2_inum_range_out(const struct gfs2_inum_range *ir, void *buf) -{ - struct gfs2_inum_range *str = buf; - - str->ir_start = cpu_to_be64(ir->ir_start); - str->ir_length = cpu_to_be64(ir->ir_length); -} - -void gfs2_statfs_change_in(struct gfs2_statfs_change *sc, const void *buf) -{ - const struct gfs2_statfs_change *str = buf; - - sc->sc_total = be64_to_cpu(str->sc_total); - sc->sc_free = be64_to_cpu(str->sc_free); - sc->sc_dinodes = be64_to_cpu(str->sc_dinodes); -} - -void gfs2_statfs_change_out(const struct gfs2_statfs_change *sc, void *buf) -{ - struct gfs2_statfs_change *str = buf; - - str->sc_total = cpu_to_be64(sc->sc_total); - str->sc_free = cpu_to_be64(sc->sc_free); - str->sc_dinodes = cpu_to_be64(sc->sc_dinodes); -} - -void gfs2_quota_change_in(struct gfs2_quota_change *qc, const void *buf) -{ - const struct gfs2_quota_change *str = buf; - - qc->qc_change = be64_to_cpu(str->qc_change); - qc->qc_flags = be32_to_cpu(str->qc_flags); - qc->qc_id = be32_to_cpu(str->qc_id); -} - diff --git a/trunk/fs/gfs2/ops_address.c b/trunk/fs/gfs2/ops_address.c deleted file mode 100644 index 4fb743f4e4a4..000000000000 --- a/trunk/fs/gfs2/ops_address.c +++ /dev/null @@ -1,790 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "inode.h" -#include "log.h" -#include "meta_io.h" -#include "ops_address.h" -#include "quota.h" -#include "trans.h" -#include "rgrp.h" -#include "ops_file.h" -#include "util.h" -#include "glops.h" - - -static void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, - unsigned int from, unsigned int to) -{ - struct buffer_head *head = page_buffers(page); - unsigned int bsize = head->b_size; - struct buffer_head *bh; - unsigned int start, end; - - for (bh = head, start = 0; bh != head || !start; - bh = bh->b_this_page, start = end) { - end = start + bsize; - if (end <= from || start >= to) - continue; - gfs2_trans_add_bh(ip->i_gl, bh, 0); - } -} - -/** - * gfs2_get_block - Fills in a buffer head with details about a block - * @inode: The inode - * @lblock: The block number to look up - * @bh_result: The buffer head to return the result in - * @create: Non-zero if we may add block to the file - * - * Returns: errno - */ - -int gfs2_get_block(struct inode *inode, sector_t lblock, - struct buffer_head *bh_result, int create) -{ - return gfs2_block_map(inode, lblock, create, bh_result, 32); -} - -/** - * gfs2_get_block_noalloc - Fills in a buffer head with details about a block - * @inode: The inode - * @lblock: The block number to look up - * @bh_result: The buffer head to return the result in - * @create: Non-zero if we may add block to the file - * - * Returns: errno - */ - -static int gfs2_get_block_noalloc(struct inode *inode, sector_t lblock, - struct buffer_head *bh_result, int create) -{ - int error; - - error = gfs2_block_map(inode, lblock, 0, bh_result, 1); - if (error) - return error; - if (bh_result->b_blocknr == 0) - return -EIO; - return 0; -} - -static int gfs2_get_block_direct(struct inode *inode, sector_t lblock, - struct buffer_head *bh_result, int create) -{ - return gfs2_block_map(inode, lblock, 0, bh_result, 32); -} - -/** - * gfs2_writepage - Write complete page - * @page: Page to write - * - * Returns: errno - * - * Some of this is copied from block_write_full_page() although we still - * call it to do most of the work. - */ - -static int gfs2_writepage(struct page *page, struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - loff_t i_size = i_size_read(inode); - pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; - unsigned offset; - int error; - int done_trans = 0; - - if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl))) { - unlock_page(page); - return -EIO; - } - if (current->journal_info) - goto out_ignore; - - /* Is the page fully outside i_size? (truncate in progress) */ - offset = i_size & (PAGE_CACHE_SIZE-1); - if (page->index > end_index || (page->index == end_index && !offset)) { - page->mapping->a_ops->invalidatepage(page, 0); - unlock_page(page); - return 0; /* don't care */ - } - - if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) { - error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); - if (error) - goto out_ignore; - if (!page_has_buffers(page)) { - create_empty_buffers(page, inode->i_sb->s_blocksize, - (1 << BH_Dirty)|(1 << BH_Uptodate)); - } - gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize-1); - done_trans = 1; - } - error = block_write_full_page(page, gfs2_get_block_noalloc, wbc); - if (done_trans) - gfs2_trans_end(sdp); - gfs2_meta_cache_flush(ip); - return error; - -out_ignore: - redirty_page_for_writepage(wbc, page); - unlock_page(page); - return 0; -} - -static int zero_readpage(struct page *page) -{ - void *kaddr; - - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr, 0, PAGE_CACHE_SIZE); - kunmap_atomic(page, KM_USER0); - - SetPageUptodate(page); - - return 0; -} - -/** - * stuffed_readpage - Fill in a Linux page with stuffed file data - * @ip: the inode - * @page: the page - * - * Returns: errno - */ - -static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) -{ - struct buffer_head *dibh; - void *kaddr; - int error; - - /* Only the first page of a stuffed file might contain data */ - if (unlikely(page->index)) - return zero_readpage(page); - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - kaddr = kmap_atomic(page, KM_USER0); - memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), - ip->i_di.di_size); - memset(kaddr + ip->i_di.di_size, 0, PAGE_CACHE_SIZE - ip->i_di.di_size); - kunmap_atomic(page, KM_USER0); - - brelse(dibh); - - SetPageUptodate(page); - - return 0; -} - - -/** - * gfs2_readpage - readpage with locking - * @file: The file to read a page for. N.B. This may be NULL if we are - * reading an internal file. - * @page: The page to read - * - * Returns: errno - */ - -static int gfs2_readpage(struct file *file, struct page *page) -{ - struct gfs2_inode *ip = GFS2_I(page->mapping->host); - struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); - struct gfs2_file *gf = NULL; - struct gfs2_holder gh; - int error; - int do_unlock = 0; - - if (likely(file != &gfs2_internal_file_sentinel)) { - if (file) { - gf = file->private_data; - if (test_bit(GFF_EXLOCK, &gf->f_flags)) - /* gfs2_sharewrite_nopage has grabbed the ip->i_gl already */ - goto skip_lock; - } - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME|GL_AOP, &gh); - do_unlock = 1; - error = gfs2_glock_nq_m_atime(1, &gh); - if (unlikely(error)) - goto out_unlock; - } - -skip_lock: - if (gfs2_is_stuffed(ip)) { - error = stuffed_readpage(ip, page); - unlock_page(page); - } else - error = mpage_readpage(page, gfs2_get_block); - - if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - error = -EIO; - - if (do_unlock) { - gfs2_glock_dq_m(1, &gh); - gfs2_holder_uninit(&gh); - } -out: - return error; -out_unlock: - unlock_page(page); - if (do_unlock) - gfs2_holder_uninit(&gh); - goto out; -} - -/** - * gfs2_readpages - Read a bunch of pages at once - * - * Some notes: - * 1. This is only for readahead, so we can simply ignore any things - * which are slightly inconvenient (such as locking conflicts between - * the page lock and the glock) and return having done no I/O. Its - * obviously not something we'd want to do on too regular a basis. - * Any I/O we ignore at this time will be done via readpage later. - * 2. We have to handle stuffed files here too. - * 3. mpage_readpages() does most of the heavy lifting in the common case. - * 4. gfs2_get_block() is relied upon to set BH_Boundary in the right places. - * 5. We use LM_FLAG_TRY_1CB here, effectively we then have lock-ahead as - * well as read-ahead. - */ -static int gfs2_readpages(struct file *file, struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - struct inode *inode = mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct gfs2_holder gh; - unsigned page_idx; - int ret; - int do_unlock = 0; - - if (likely(file != &gfs2_internal_file_sentinel)) { - if (file) { - struct gfs2_file *gf = file->private_data; - if (test_bit(GFF_EXLOCK, &gf->f_flags)) - goto skip_lock; - } - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, - LM_FLAG_TRY_1CB|GL_ATIME|GL_AOP, &gh); - do_unlock = 1; - ret = gfs2_glock_nq_m_atime(1, &gh); - if (ret == GLR_TRYFAILED) - goto out_noerror; - if (unlikely(ret)) - goto out_unlock; - } -skip_lock: - if (gfs2_is_stuffed(ip)) { - struct pagevec lru_pvec; - pagevec_init(&lru_pvec, 0); - for (page_idx = 0; page_idx < nr_pages; page_idx++) { - struct page *page = list_entry(pages->prev, struct page, lru); - prefetchw(&page->flags); - list_del(&page->lru); - if (!add_to_page_cache(page, mapping, - page->index, GFP_KERNEL)) { - ret = stuffed_readpage(ip, page); - unlock_page(page); - if (!pagevec_add(&lru_pvec, page)) - __pagevec_lru_add(&lru_pvec); - } else { - page_cache_release(page); - } - } - pagevec_lru_add(&lru_pvec); - ret = 0; - } else { - /* What we really want to do .... */ - ret = mpage_readpages(mapping, pages, nr_pages, gfs2_get_block); - } - - if (do_unlock) { - gfs2_glock_dq_m(1, &gh); - gfs2_holder_uninit(&gh); - } -out: - if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) - ret = -EIO; - return ret; -out_noerror: - ret = 0; -out_unlock: - /* unlock all pages, we can't do any I/O right now */ - for (page_idx = 0; page_idx < nr_pages; page_idx++) { - struct page *page = list_entry(pages->prev, struct page, lru); - list_del(&page->lru); - unlock_page(page); - page_cache_release(page); - } - if (do_unlock) - gfs2_holder_uninit(&gh); - goto out; -} - -/** - * gfs2_prepare_write - Prepare to write a page to a file - * @file: The file to write to - * @page: The page which is to be prepared for writing - * @from: From (byte range within page) - * @to: To (byte range within page) - * - * Returns: errno - */ - -static int gfs2_prepare_write(struct file *file, struct page *page, - unsigned from, unsigned to) -{ - struct gfs2_inode *ip = GFS2_I(page->mapping->host); - struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); - unsigned int data_blocks, ind_blocks, rblocks; - int alloc_required; - int error = 0; - loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + from; - loff_t end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; - struct gfs2_alloc *al; - - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ATIME|GL_AOP, &ip->i_gh); - error = gfs2_glock_nq_m_atime(1, &ip->i_gh); - if (error) - goto out_uninit; - - gfs2_write_calc_reserv(ip, to - from, &data_blocks, &ind_blocks); - - error = gfs2_write_alloc_required(ip, pos, from - to, &alloc_required); - if (error) - goto out_unlock; - - - if (alloc_required) { - al = gfs2_alloc_get(ip); - - error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out_alloc_put; - - error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); - if (error) - goto out_qunlock; - - al->al_requested = data_blocks + ind_blocks; - error = gfs2_inplace_reserve(ip); - if (error) - goto out_qunlock; - } - - rblocks = RES_DINODE + ind_blocks; - if (gfs2_is_jdata(ip)) - rblocks += data_blocks ? data_blocks : 1; - if (ind_blocks || data_blocks) - rblocks += RES_STATFS + RES_QUOTA; - - error = gfs2_trans_begin(sdp, rblocks, 0); - if (error) - goto out; - - if (gfs2_is_stuffed(ip)) { - if (end > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { - error = gfs2_unstuff_dinode(ip, page); - if (error == 0) - goto prepare_write; - } else if (!PageUptodate(page)) - error = stuffed_readpage(ip, page); - goto out; - } - -prepare_write: - error = block_prepare_write(page, from, to, gfs2_get_block); - -out: - if (error) { - gfs2_trans_end(sdp); - if (alloc_required) { - gfs2_inplace_release(ip); -out_qunlock: - gfs2_quota_unlock(ip); -out_alloc_put: - gfs2_alloc_put(ip); - } -out_unlock: - gfs2_glock_dq_m(1, &ip->i_gh); -out_uninit: - gfs2_holder_uninit(&ip->i_gh); - } - - return error; -} - -/** - * gfs2_commit_write - Commit write to a file - * @file: The file to write to - * @page: The page containing the data - * @from: From (byte range within page) - * @to: To (byte range within page) - * - * Returns: errno - */ - -static int gfs2_commit_write(struct file *file, struct page *page, - unsigned from, unsigned to) -{ - struct inode *inode = page->mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - int error = -EOPNOTSUPP; - struct buffer_head *dibh; - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_dinode *di; - - if (gfs2_assert_withdraw(sdp, gfs2_glock_is_locked_by_me(ip->i_gl))) - goto fail_nounlock; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto fail_endtrans; - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - di = (struct gfs2_dinode *)dibh->b_data; - - if (gfs2_is_stuffed(ip)) { - u64 file_size; - void *kaddr; - - file_size = ((u64)page->index << PAGE_CACHE_SHIFT) + to; - - kaddr = kmap_atomic(page, KM_USER0); - memcpy(dibh->b_data + sizeof(struct gfs2_dinode) + from, - kaddr + from, to - from); - kunmap_atomic(page, KM_USER0); - - SetPageUptodate(page); - - if (inode->i_size < file_size) - i_size_write(inode, file_size); - } else { - if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || - gfs2_is_jdata(ip)) - gfs2_page_add_databufs(ip, page, from, to); - error = generic_commit_write(file, page, from, to); - if (error) - goto fail; - } - - if (ip->i_di.di_size < inode->i_size) { - ip->i_di.di_size = inode->i_size; - di->di_size = cpu_to_be64(inode->i_size); - } - - di->di_mode = cpu_to_be32(inode->i_mode); - di->di_atime = cpu_to_be64(inode->i_atime.tv_sec); - di->di_mtime = cpu_to_be64(inode->i_mtime.tv_sec); - di->di_ctime = cpu_to_be64(inode->i_ctime.tv_sec); - - brelse(dibh); - gfs2_trans_end(sdp); - if (al->al_requested) { - gfs2_inplace_release(ip); - gfs2_quota_unlock(ip); - gfs2_alloc_put(ip); - } - gfs2_glock_dq_m(1, &ip->i_gh); - gfs2_holder_uninit(&ip->i_gh); - return 0; - -fail: - brelse(dibh); -fail_endtrans: - gfs2_trans_end(sdp); - if (al->al_requested) { - gfs2_inplace_release(ip); - gfs2_quota_unlock(ip); - gfs2_alloc_put(ip); - } - gfs2_glock_dq_m(1, &ip->i_gh); - gfs2_holder_uninit(&ip->i_gh); -fail_nounlock: - ClearPageUptodate(page); - return error; -} - -/** - * gfs2_bmap - Block map function - * @mapping: Address space info - * @lblock: The block to map - * - * Returns: The disk address for the block or 0 on hole or error - */ - -static sector_t gfs2_bmap(struct address_space *mapping, sector_t lblock) -{ - struct gfs2_inode *ip = GFS2_I(mapping->host); - struct gfs2_holder i_gh; - sector_t dblock = 0; - int error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (error) - return 0; - - if (!gfs2_is_stuffed(ip)) - dblock = generic_block_bmap(mapping, lblock, gfs2_get_block); - - gfs2_glock_dq_uninit(&i_gh); - - return dblock; -} - -static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh) -{ - struct gfs2_bufdata *bd; - - gfs2_log_lock(sdp); - bd = bh->b_private; - if (bd) { - bd->bd_bh = NULL; - bh->b_private = NULL; - } - gfs2_log_unlock(sdp); - - lock_buffer(bh); - clear_buffer_dirty(bh); - bh->b_bdev = NULL; - clear_buffer_mapped(bh); - clear_buffer_req(bh); - clear_buffer_new(bh); - clear_buffer_delay(bh); - unlock_buffer(bh); -} - -static void gfs2_invalidatepage(struct page *page, unsigned long offset) -{ - struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); - struct buffer_head *head, *bh, *next; - unsigned int curr_off = 0; - - BUG_ON(!PageLocked(page)); - if (!page_has_buffers(page)) - return; - - bh = head = page_buffers(page); - do { - unsigned int next_off = curr_off + bh->b_size; - next = bh->b_this_page; - - if (offset <= curr_off) - discard_buffer(sdp, bh); - - curr_off = next_off; - bh = next; - } while (bh != head); - - if (!offset) - try_to_release_page(page, 0); - - return; -} - -static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) -{ - struct file *file = iocb->ki_filp; - struct inode *inode = file->f_mapping->host; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int rv; - - if (rw == READ) - mutex_lock(&inode->i_mutex); - /* - * Shared lock, even if its a write, since we do no allocation - * on this path. All we need change is atime. - */ - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh); - rv = gfs2_glock_nq_m_atime(1, &gh); - if (rv) - goto out; - - if (offset > i_size_read(inode)) - goto out; - - /* - * Should we return an error here? I can't see that O_DIRECT for - * a journaled file makes any sense. For now we'll silently fall - * back to buffered I/O, likewise we do the same for stuffed - * files since they are (a) small and (b) unaligned. - */ - if (gfs2_is_jdata(ip)) - goto out; - - if (gfs2_is_stuffed(ip)) - goto out; - - rv = blockdev_direct_IO_own_locking(rw, iocb, inode, - inode->i_sb->s_bdev, - iov, offset, nr_segs, - gfs2_get_block_direct, NULL); -out: - gfs2_glock_dq_m(1, &gh); - gfs2_holder_uninit(&gh); - if (rw == READ) - mutex_unlock(&inode->i_mutex); - - return rv; -} - -/** - * stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out. - * @bh: the buffer we're stuck on - * - */ - -static void stuck_releasepage(struct buffer_head *bh) -{ - struct inode *inode = bh->b_page->mapping->host; - struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; - struct gfs2_bufdata *bd = bh->b_private; - struct gfs2_glock *gl; -static unsigned limit = 0; - - if (limit > 3) - return; - limit++; - - fs_warn(sdp, "stuck in gfs2_releasepage() %p\n", inode); - fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", - (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); - fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); - fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); - - if (!bd) - return; - - gl = bd->bd_gl; - - fs_warn(sdp, "gl = (%u, %llu)\n", - gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); - - fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", - (list_empty(&bd->bd_list_tr)) ? "no" : "yes", - (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); - - if (gl->gl_ops == &gfs2_inode_glops) { - struct gfs2_inode *ip = gl->gl_object; - unsigned int x; - - if (!ip) - return; - - fs_warn(sdp, "ip = %llu %llu\n", - (unsigned long long)ip->i_num.no_formal_ino, - (unsigned long long)ip->i_num.no_addr); - - for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) - fs_warn(sdp, "ip->i_cache[%u] = %s\n", - x, (ip->i_cache[x]) ? "!NULL" : "NULL"); - } -} - -/** - * gfs2_releasepage - free the metadata associated with a page - * @page: the page that's being released - * @gfp_mask: passed from Linux VFS, ignored by us - * - * Call try_to_free_buffers() if the buffers in this page can be - * released. - * - * Returns: 0 - */ - -int gfs2_releasepage(struct page *page, gfp_t gfp_mask) -{ - struct inode *aspace = page->mapping->host; - struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; - struct buffer_head *bh, *head; - struct gfs2_bufdata *bd; - unsigned long t = jiffies + gfs2_tune_get(sdp, gt_stall_secs) * HZ; - - if (!page_has_buffers(page)) - goto out; - - head = bh = page_buffers(page); - do { - while (atomic_read(&bh->b_count)) { - if (!atomic_read(&aspace->i_writecount)) - return 0; - - if (time_after_eq(jiffies, t)) { - stuck_releasepage(bh); - /* should we withdraw here? */ - return 0; - } - - yield(); - } - - gfs2_assert_warn(sdp, !buffer_pinned(bh)); - gfs2_assert_warn(sdp, !buffer_dirty(bh)); - - gfs2_log_lock(sdp); - bd = bh->b_private; - if (bd) { - gfs2_assert_warn(sdp, bd->bd_bh == bh); - gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); - gfs2_assert_warn(sdp, !bd->bd_ail); - bd->bd_bh = NULL; - if (!list_empty(&bd->bd_le.le_list)) - bd = NULL; - bh->b_private = NULL; - } - gfs2_log_unlock(sdp); - if (bd) - kmem_cache_free(gfs2_bufdata_cachep, bd); - - bh = bh->b_this_page; - } while (bh != head); - -out: - return try_to_free_buffers(page); -} - -const struct address_space_operations gfs2_file_aops = { - .writepage = gfs2_writepage, - .readpage = gfs2_readpage, - .readpages = gfs2_readpages, - .sync_page = block_sync_page, - .prepare_write = gfs2_prepare_write, - .commit_write = gfs2_commit_write, - .bmap = gfs2_bmap, - .invalidatepage = gfs2_invalidatepage, - .releasepage = gfs2_releasepage, - .direct_IO = gfs2_direct_IO, -}; - diff --git a/trunk/fs/gfs2/ops_address.h b/trunk/fs/gfs2/ops_address.h deleted file mode 100644 index 35aaee4aa7e1..000000000000 --- a/trunk/fs/gfs2/ops_address.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_ADDRESS_DOT_H__ -#define __OPS_ADDRESS_DOT_H__ - -#include -#include -#include - -extern const struct address_space_operations gfs2_file_aops; -extern int gfs2_get_block(struct inode *inode, sector_t lblock, - struct buffer_head *bh_result, int create); -extern int gfs2_releasepage(struct page *page, gfp_t gfp_mask); - -#endif /* __OPS_ADDRESS_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_dentry.c b/trunk/fs/gfs2/ops_dentry.c deleted file mode 100644 index 00041b1b8025..000000000000 --- a/trunk/fs/gfs2/ops_dentry.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "dir.h" -#include "glock.h" -#include "ops_dentry.h" -#include "util.h" - -/** - * gfs2_drevalidate - Check directory lookup consistency - * @dentry: the mapping to check - * @nd: - * - * Check to make sure the lookup necessary to arrive at this inode from its - * parent is still good. - * - * Returns: 1 if the dentry is ok, 0 if it isn't - */ - -static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) -{ - struct dentry *parent = dget_parent(dentry); - struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); - struct gfs2_inode *dip = GFS2_I(parent->d_inode); - struct inode *inode = dentry->d_inode; - struct gfs2_holder d_gh; - struct gfs2_inode *ip; - struct gfs2_inum inum; - unsigned int type; - int error; - - if (inode && is_bad_inode(inode)) - goto invalid; - - if (sdp->sd_args.ar_localcaching) - goto valid; - - error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); - if (error) - goto fail; - - error = gfs2_dir_search(parent->d_inode, &dentry->d_name, &inum, &type); - switch (error) { - case 0: - if (!inode) - goto invalid_gunlock; - break; - case -ENOENT: - if (!inode) - goto valid_gunlock; - goto invalid_gunlock; - default: - goto fail_gunlock; - } - - ip = GFS2_I(inode); - - if (!gfs2_inum_equal(&ip->i_num, &inum)) - goto invalid_gunlock; - - if (IF2DT(ip->i_di.di_mode) != type) { - gfs2_consist_inode(dip); - goto fail_gunlock; - } - -valid_gunlock: - gfs2_glock_dq_uninit(&d_gh); -valid: - dput(parent); - return 1; - -invalid_gunlock: - gfs2_glock_dq_uninit(&d_gh); -invalid: - if (inode && S_ISDIR(inode->i_mode)) { - if (have_submounts(dentry)) - goto valid; - shrink_dcache_parent(dentry); - } - d_drop(dentry); - dput(parent); - return 0; - -fail_gunlock: - gfs2_glock_dq_uninit(&d_gh); -fail: - dput(parent); - return 0; -} - -static int gfs2_dhash(struct dentry *dentry, struct qstr *str) -{ - str->hash = gfs2_disk_hash(str->name, str->len); - return 0; -} - -struct dentry_operations gfs2_dops = { - .d_revalidate = gfs2_drevalidate, - .d_hash = gfs2_dhash, -}; - diff --git a/trunk/fs/gfs2/ops_dentry.h b/trunk/fs/gfs2/ops_dentry.h deleted file mode 100644 index 5caa3db4d3f5..000000000000 --- a/trunk/fs/gfs2/ops_dentry.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_DENTRY_DOT_H__ -#define __OPS_DENTRY_DOT_H__ - -#include - -extern struct dentry_operations gfs2_dops; - -#endif /* __OPS_DENTRY_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_export.c b/trunk/fs/gfs2/ops_export.c deleted file mode 100644 index 86127d93bd35..000000000000 --- a/trunk/fs/gfs2/ops_export.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "dir.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "ops_export.h" -#include "rgrp.h" -#include "util.h" - -static struct dentry *gfs2_decode_fh(struct super_block *sb, - __u32 *fh, - int fh_len, - int fh_type, - int (*acceptable)(void *context, - struct dentry *dentry), - void *context) -{ - struct gfs2_fh_obj fh_obj; - struct gfs2_inum *this, parent; - - if (fh_type != fh_len) - return NULL; - - this = &fh_obj.this; - fh_obj.imode = DT_UNKNOWN; - memset(&parent, 0, sizeof(struct gfs2_inum)); - - switch (fh_type) { - case GFS2_LARGE_FH_SIZE: - parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32; - parent.no_formal_ino |= be32_to_cpu(fh[5]); - parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32; - parent.no_addr |= be32_to_cpu(fh[7]); - fh_obj.imode = be32_to_cpu(fh[8]); - case GFS2_SMALL_FH_SIZE: - this->no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32; - this->no_formal_ino |= be32_to_cpu(fh[1]); - this->no_addr = ((u64)be32_to_cpu(fh[2])) << 32; - this->no_addr |= be32_to_cpu(fh[3]); - break; - default: - return NULL; - } - - return gfs2_export_ops.find_exported_dentry(sb, &fh_obj, &parent, - acceptable, context); -} - -static int gfs2_encode_fh(struct dentry *dentry, __u32 *fh, int *len, - int connectable) -{ - struct inode *inode = dentry->d_inode; - struct super_block *sb = inode->i_sb; - struct gfs2_inode *ip = GFS2_I(inode); - - if (*len < GFS2_SMALL_FH_SIZE || - (connectable && *len < GFS2_LARGE_FH_SIZE)) - return 255; - - fh[0] = ip->i_num.no_formal_ino >> 32; - fh[0] = cpu_to_be32(fh[0]); - fh[1] = ip->i_num.no_formal_ino & 0xFFFFFFFF; - fh[1] = cpu_to_be32(fh[1]); - fh[2] = ip->i_num.no_addr >> 32; - fh[2] = cpu_to_be32(fh[2]); - fh[3] = ip->i_num.no_addr & 0xFFFFFFFF; - fh[3] = cpu_to_be32(fh[3]); - *len = GFS2_SMALL_FH_SIZE; - - if (!connectable || inode == sb->s_root->d_inode) - return *len; - - spin_lock(&dentry->d_lock); - inode = dentry->d_parent->d_inode; - ip = GFS2_I(inode); - igrab(inode); - spin_unlock(&dentry->d_lock); - - fh[4] = ip->i_num.no_formal_ino >> 32; - fh[4] = cpu_to_be32(fh[4]); - fh[5] = ip->i_num.no_formal_ino & 0xFFFFFFFF; - fh[5] = cpu_to_be32(fh[5]); - fh[6] = ip->i_num.no_addr >> 32; - fh[6] = cpu_to_be32(fh[6]); - fh[7] = ip->i_num.no_addr & 0xFFFFFFFF; - fh[7] = cpu_to_be32(fh[7]); - - fh[8] = cpu_to_be32(inode->i_mode); - fh[9] = 0; /* pad to double word */ - *len = GFS2_LARGE_FH_SIZE; - - iput(inode); - - return *len; -} - -struct get_name_filldir { - struct gfs2_inum inum; - char *name; -}; - -static int get_name_filldir(void *opaque, const char *name, unsigned int length, - u64 offset, struct gfs2_inum *inum, - unsigned int type) -{ - struct get_name_filldir *gnfd = (struct get_name_filldir *)opaque; - - if (!gfs2_inum_equal(inum, &gnfd->inum)) - return 0; - - memcpy(gnfd->name, name, length); - gnfd->name[length] = 0; - - return 1; -} - -static int gfs2_get_name(struct dentry *parent, char *name, - struct dentry *child) -{ - struct inode *dir = parent->d_inode; - struct inode *inode = child->d_inode; - struct gfs2_inode *dip, *ip; - struct get_name_filldir gnfd; - struct gfs2_holder gh; - u64 offset = 0; - int error; - - if (!dir) - return -EINVAL; - - if (!S_ISDIR(dir->i_mode) || !inode) - return -EINVAL; - - dip = GFS2_I(dir); - ip = GFS2_I(inode); - - *name = 0; - gnfd.inum = ip->i_num; - gnfd.name = name; - - error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &gh); - if (error) - return error; - - error = gfs2_dir_read(dir, &offset, &gnfd, get_name_filldir); - - gfs2_glock_dq_uninit(&gh); - - if (!error && !*name) - error = -ENOENT; - - return error; -} - -static struct dentry *gfs2_get_parent(struct dentry *child) -{ - struct qstr dotdot; - struct inode *inode; - struct dentry *dentry; - - gfs2_str2qstr(&dotdot, ".."); - inode = gfs2_lookupi(child->d_inode, &dotdot, 1, NULL); - - if (!inode) - return ERR_PTR(-ENOENT); - /* - * In case of an error, @inode carries the error value, and we - * have to return that as a(n invalid) pointer to dentry. - */ - if (IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); - - dentry = d_alloc_anon(inode); - if (!dentry) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - - return dentry; -} - -static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - struct gfs2_fh_obj *fh_obj = (struct gfs2_fh_obj *)inum_obj; - struct gfs2_inum *inum = &fh_obj->this; - struct gfs2_holder i_gh, ri_gh, rgd_gh; - struct gfs2_rgrpd *rgd; - struct inode *inode; - struct dentry *dentry; - int error; - - /* System files? */ - - inode = gfs2_ilookup(sb, inum); - if (inode) { - if (GFS2_I(inode)->i_num.no_formal_ino != inum->no_formal_ino) { - iput(inode); - return ERR_PTR(-ESTALE); - } - goto out_inode; - } - - error = gfs2_glock_nq_num(sdp, inum->no_addr, &gfs2_inode_glops, - LM_ST_SHARED, LM_FLAG_ANY | GL_LOCAL_EXCL, - &i_gh); - if (error) - return ERR_PTR(error); - - error = gfs2_rindex_hold(sdp, &ri_gh); - if (error) - goto fail; - - error = -EINVAL; - rgd = gfs2_blk2rgrpd(sdp, inum->no_addr); - if (!rgd) - goto fail_rindex; - - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh); - if (error) - goto fail_rindex; - - error = -ESTALE; - if (gfs2_get_block_type(rgd, inum->no_addr) != GFS2_BLKST_DINODE) - goto fail_rgd; - - gfs2_glock_dq_uninit(&rgd_gh); - gfs2_glock_dq_uninit(&ri_gh); - - inode = gfs2_inode_lookup(sb, inum, fh_obj->imode); - if (!inode) - goto fail; - if (IS_ERR(inode)) { - error = PTR_ERR(inode); - goto fail; - } - - error = gfs2_inode_refresh(GFS2_I(inode)); - if (error) { - iput(inode); - goto fail; - } - - error = -EIO; - if (GFS2_I(inode)->i_di.di_flags & GFS2_DIF_SYSTEM) { - iput(inode); - goto fail; - } - - gfs2_glock_dq_uninit(&i_gh); - -out_inode: - dentry = d_alloc_anon(inode); - if (!dentry) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - - return dentry; - -fail_rgd: - gfs2_glock_dq_uninit(&rgd_gh); - -fail_rindex: - gfs2_glock_dq_uninit(&ri_gh); - -fail: - gfs2_glock_dq_uninit(&i_gh); - return ERR_PTR(error); -} - -struct export_operations gfs2_export_ops = { - .decode_fh = gfs2_decode_fh, - .encode_fh = gfs2_encode_fh, - .get_name = gfs2_get_name, - .get_parent = gfs2_get_parent, - .get_dentry = gfs2_get_dentry, -}; - diff --git a/trunk/fs/gfs2/ops_export.h b/trunk/fs/gfs2/ops_export.h deleted file mode 100644 index 09aca5046fb1..000000000000 --- a/trunk/fs/gfs2/ops_export.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_EXPORT_DOT_H__ -#define __OPS_EXPORT_DOT_H__ - -#define GFS2_SMALL_FH_SIZE 4 -#define GFS2_LARGE_FH_SIZE 10 - -extern struct export_operations gfs2_export_ops; -struct gfs2_fh_obj { - struct gfs2_inum this; - __u32 imode; -}; - -#endif /* __OPS_EXPORT_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_file.c b/trunk/fs/gfs2/ops_file.c deleted file mode 100644 index 3064f133bf3c..000000000000 --- a/trunk/fs/gfs2/ops_file.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "dir.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "lm.h" -#include "log.h" -#include "meta_io.h" -#include "ops_file.h" -#include "ops_vm.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" -#include "eaops.h" - -/* For regular, non-NFS */ -struct filldir_reg { - struct gfs2_sbd *fdr_sbd; - int fdr_prefetch; - - filldir_t fdr_filldir; - void *fdr_opaque; -}; - -/* - * Most fields left uninitialised to catch anybody who tries to - * use them. f_flags set to prevent file_accessed() from touching - * any other part of this. Its use is purely as a flag so that we - * know (in readpage()) whether or not do to locking. - */ -struct file gfs2_internal_file_sentinel = { - .f_flags = O_NOATIME|O_RDONLY, -}; - -static int gfs2_read_actor(read_descriptor_t *desc, struct page *page, - unsigned long offset, unsigned long size) -{ - char *kaddr; - unsigned long count = desc->count; - - if (size > count) - size = count; - - kaddr = kmap(page); - memcpy(desc->arg.buf, kaddr + offset, size); - kunmap(page); - - desc->count = count - size; - desc->written += size; - desc->arg.buf += size; - return size; -} - -int gfs2_internal_read(struct gfs2_inode *ip, struct file_ra_state *ra_state, - char *buf, loff_t *pos, unsigned size) -{ - struct inode *inode = &ip->i_inode; - read_descriptor_t desc; - desc.written = 0; - desc.arg.buf = buf; - desc.count = size; - desc.error = 0; - do_generic_mapping_read(inode->i_mapping, ra_state, - &gfs2_internal_file_sentinel, pos, &desc, - gfs2_read_actor); - return desc.written ? desc.written : desc.error; -} - -/** - * gfs2_llseek - seek to a location in a file - * @file: the file - * @offset: the offset - * @origin: Where to seek from (SEEK_SET, SEEK_CUR, or SEEK_END) - * - * SEEK_END requires the glock for the file because it references the - * file's size. - * - * Returns: The new offset, or errno - */ - -static loff_t gfs2_llseek(struct file *file, loff_t offset, int origin) -{ - struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); - struct gfs2_holder i_gh; - loff_t error; - - if (origin == 2) { - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, - &i_gh); - if (!error) { - error = remote_llseek(file, offset, origin); - gfs2_glock_dq_uninit(&i_gh); - } - } else - error = remote_llseek(file, offset, origin); - - return error; -} - -/** - * filldir_func - Report a directory entry to the caller of gfs2_dir_read() - * @opaque: opaque data used by the function - * @name: the name of the directory entry - * @length: the length of the name - * @offset: the entry's offset in the directory - * @inum: the inode number the entry points to - * @type: the type of inode the entry points to - * - * Returns: 0 on success, 1 if buffer full - */ - -static int filldir_func(void *opaque, const char *name, unsigned int length, - u64 offset, struct gfs2_inum *inum, - unsigned int type) -{ - struct filldir_reg *fdr = (struct filldir_reg *)opaque; - struct gfs2_sbd *sdp = fdr->fdr_sbd; - int error; - - error = fdr->fdr_filldir(fdr->fdr_opaque, name, length, offset, - inum->no_addr, type); - if (error) - return 1; - - if (fdr->fdr_prefetch && !(length == 1 && *name == '.')) { - gfs2_glock_prefetch_num(sdp, inum->no_addr, &gfs2_inode_glops, - LM_ST_SHARED, LM_FLAG_TRY | LM_FLAG_ANY); - gfs2_glock_prefetch_num(sdp, inum->no_addr, &gfs2_iopen_glops, - LM_ST_SHARED, LM_FLAG_TRY); - } - - return 0; -} - -/** - * gfs2_readdir - Read directory entries from a directory - * @file: The directory to read from - * @dirent: Buffer for dirents - * @filldir: Function used to do the copying - * - * Returns: errno - */ - -static int gfs2_readdir(struct file *file, void *dirent, filldir_t filldir) -{ - struct inode *dir = file->f_mapping->host; - struct gfs2_inode *dip = GFS2_I(dir); - struct filldir_reg fdr; - struct gfs2_holder d_gh; - u64 offset = file->f_pos; - int error; - - fdr.fdr_sbd = GFS2_SB(dir); - fdr.fdr_prefetch = 1; - fdr.fdr_filldir = filldir; - fdr.fdr_opaque = dirent; - - gfs2_holder_init(dip->i_gl, LM_ST_SHARED, GL_ATIME, &d_gh); - error = gfs2_glock_nq_atime(&d_gh); - if (error) { - gfs2_holder_uninit(&d_gh); - return error; - } - - error = gfs2_dir_read(dir, &offset, &fdr, filldir_func); - - gfs2_glock_dq_uninit(&d_gh); - - file->f_pos = offset; - - return error; -} - -/** - * fsflags_cvt - * @table: A table of 32 u32 flags - * @val: a 32 bit value to convert - * - * This function can be used to convert between fsflags values and - * GFS2's own flags values. - * - * Returns: the converted flags - */ -static u32 fsflags_cvt(const u32 *table, u32 val) -{ - u32 res = 0; - while(val) { - if (val & 1) - res |= *table; - table++; - val >>= 1; - } - return res; -} - -static const u32 fsflags_to_gfs2[32] = { - [3] = GFS2_DIF_SYNC, - [4] = GFS2_DIF_IMMUTABLE, - [5] = GFS2_DIF_APPENDONLY, - [7] = GFS2_DIF_NOATIME, - [12] = GFS2_DIF_EXHASH, - [14] = GFS2_DIF_JDATA, - [20] = GFS2_DIF_DIRECTIO, -}; - -static const u32 gfs2_to_fsflags[32] = { - [gfs2fl_Sync] = FS_SYNC_FL, - [gfs2fl_Immutable] = FS_IMMUTABLE_FL, - [gfs2fl_AppendOnly] = FS_APPEND_FL, - [gfs2fl_NoAtime] = FS_NOATIME_FL, - [gfs2fl_ExHash] = FS_INDEX_FL, - [gfs2fl_Jdata] = FS_JOURNAL_DATA_FL, - [gfs2fl_Directio] = FS_DIRECTIO_FL, - [gfs2fl_InheritDirectio] = FS_DIRECTIO_FL, - [gfs2fl_InheritJdata] = FS_JOURNAL_DATA_FL, -}; - -static int gfs2_get_flags(struct file *filp, u32 __user *ptr) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int error; - u32 fsflags; - - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh); - error = gfs2_glock_nq_m_atime(1, &gh); - if (error) - return error; - - fsflags = fsflags_cvt(gfs2_to_fsflags, ip->i_di.di_flags); - if (put_user(fsflags, ptr)) - error = -EFAULT; - - gfs2_glock_dq_m(1, &gh); - gfs2_holder_uninit(&gh); - return error; -} - -/* Flags that can be set by user space */ -#define GFS2_FLAGS_USER_SET (GFS2_DIF_JDATA| \ - GFS2_DIF_DIRECTIO| \ - GFS2_DIF_IMMUTABLE| \ - GFS2_DIF_APPENDONLY| \ - GFS2_DIF_NOATIME| \ - GFS2_DIF_SYNC| \ - GFS2_DIF_SYSTEM| \ - GFS2_DIF_INHERIT_DIRECTIO| \ - GFS2_DIF_INHERIT_JDATA) - -/** - * gfs2_set_flags - set flags on an inode - * @inode: The inode - * @flags: The flags to set - * @mask: Indicates which flags are valid - * - */ -static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct buffer_head *bh; - struct gfs2_holder gh; - int error; - u32 new_flags, flags; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); - if (error) - return error; - - flags = ip->i_di.di_flags; - new_flags = (flags & ~mask) | (reqflags & mask); - if ((new_flags ^ flags) == 0) - goto out; - - if (S_ISDIR(inode->i_mode)) { - if ((new_flags ^ flags) & GFS2_DIF_JDATA) - new_flags ^= (GFS2_DIF_JDATA|GFS2_DIF_INHERIT_JDATA); - if ((new_flags ^ flags) & GFS2_DIF_DIRECTIO) - new_flags ^= (GFS2_DIF_DIRECTIO|GFS2_DIF_INHERIT_DIRECTIO); - } - - error = -EINVAL; - if ((new_flags ^ flags) & ~GFS2_FLAGS_USER_SET) - goto out; - - error = -EPERM; - if (IS_IMMUTABLE(inode) && (new_flags & GFS2_DIF_IMMUTABLE)) - goto out; - if (IS_APPEND(inode) && (new_flags & GFS2_DIF_APPENDONLY)) - goto out; - if (((new_flags ^ flags) & GFS2_DIF_IMMUTABLE) && - !capable(CAP_LINUX_IMMUTABLE)) - goto out; - if (!IS_IMMUTABLE(inode)) { - error = permission(inode, MAY_WRITE, NULL); - if (error) - goto out; - } - - error = gfs2_trans_begin(sdp, RES_DINODE, 0); - if (error) - goto out; - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - goto out_trans_end; - gfs2_trans_add_bh(ip->i_gl, bh, 1); - ip->i_di.di_flags = new_flags; - gfs2_dinode_out(&ip->i_di, bh->b_data); - brelse(bh); -out_trans_end: - gfs2_trans_end(sdp); -out: - gfs2_glock_dq_uninit(&gh); - return error; -} - -static int gfs2_set_flags(struct file *filp, u32 __user *ptr) -{ - u32 fsflags, gfsflags; - if (get_user(fsflags, ptr)) - return -EFAULT; - gfsflags = fsflags_cvt(fsflags_to_gfs2, fsflags); - return do_gfs2_set_flags(filp, gfsflags, ~0); -} - -static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - switch(cmd) { - case FS_IOC_GETFLAGS: - return gfs2_get_flags(filp, (u32 __user *)arg); - case FS_IOC_SETFLAGS: - return gfs2_set_flags(filp, (u32 __user *)arg); - } - return -ENOTTY; -} - - -/** - * gfs2_mmap - - * @file: The file to map - * @vma: The VMA which described the mapping - * - * Returns: 0 or error code - */ - -static int gfs2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); - struct gfs2_holder i_gh; - int error; - - gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &i_gh); - error = gfs2_glock_nq_atime(&i_gh); - if (error) { - gfs2_holder_uninit(&i_gh); - return error; - } - - /* This is VM_MAYWRITE instead of VM_WRITE because a call - to mprotect() can turn on VM_WRITE later. */ - - if ((vma->vm_flags & (VM_MAYSHARE | VM_MAYWRITE)) == - (VM_MAYSHARE | VM_MAYWRITE)) - vma->vm_ops = &gfs2_vm_ops_sharewrite; - else - vma->vm_ops = &gfs2_vm_ops_private; - - gfs2_glock_dq_uninit(&i_gh); - - return error; -} - -/** - * gfs2_open - open a file - * @inode: the inode to open - * @file: the struct file for this opening - * - * Returns: errno - */ - -static int gfs2_open(struct inode *inode, struct file *file) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder i_gh; - struct gfs2_file *fp; - int error; - - fp = kzalloc(sizeof(struct gfs2_file), GFP_KERNEL); - if (!fp) - return -ENOMEM; - - mutex_init(&fp->f_fl_mutex); - - gfs2_assert_warn(GFS2_SB(inode), !file->private_data); - file->private_data = fp; - - if (S_ISREG(ip->i_di.di_mode)) { - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, - &i_gh); - if (error) - goto fail; - - if (!(file->f_flags & O_LARGEFILE) && - ip->i_di.di_size > MAX_NON_LFS) { - error = -EFBIG; - goto fail_gunlock; - } - - /* Listen to the Direct I/O flag */ - - if (ip->i_di.di_flags & GFS2_DIF_DIRECTIO) - file->f_flags |= O_DIRECT; - - gfs2_glock_dq_uninit(&i_gh); - } - - return 0; - -fail_gunlock: - gfs2_glock_dq_uninit(&i_gh); -fail: - file->private_data = NULL; - kfree(fp); - return error; -} - -/** - * gfs2_close - called to close a struct file - * @inode: the inode the struct file belongs to - * @file: the struct file being closed - * - * Returns: errno - */ - -static int gfs2_close(struct inode *inode, struct file *file) -{ - struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; - struct gfs2_file *fp; - - fp = file->private_data; - file->private_data = NULL; - - if (gfs2_assert_warn(sdp, fp)) - return -EIO; - - kfree(fp); - - return 0; -} - -/** - * gfs2_fsync - sync the dirty data for a file (across the cluster) - * @file: the file that points to the dentry (we ignore this) - * @dentry: the dentry that points to the inode to sync - * - * Returns: errno - */ - -static int gfs2_fsync(struct file *file, struct dentry *dentry, int datasync) -{ - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - - gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl); - - return 0; -} - -/** - * gfs2_lock - acquire/release a posix lock on a file - * @file: the file pointer - * @cmd: either modify or retrieve lock state, possibly wait - * @fl: type and range of lock - * - * Returns: errno - */ - -static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl) -{ - struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); - struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host); - struct lm_lockname name = - { .ln_number = ip->i_num.no_addr, - .ln_type = LM_TYPE_PLOCK }; - - if (!(fl->fl_flags & FL_POSIX)) - return -ENOLCK; - if ((ip->i_di.di_mode & (S_ISGID | S_IXGRP)) == S_ISGID) - return -ENOLCK; - - if (sdp->sd_args.ar_localflocks) { - if (IS_GETLK(cmd)) { - struct file_lock tmp; - int ret; - ret = posix_test_lock(file, fl, &tmp); - fl->fl_type = F_UNLCK; - if (ret) - memcpy(fl, &tmp, sizeof(struct file_lock)); - return 0; - } else { - return posix_lock_file_wait(file, fl); - } - } - - if (IS_GETLK(cmd)) - return gfs2_lm_plock_get(sdp, &name, file, fl); - else if (fl->fl_type == F_UNLCK) - return gfs2_lm_punlock(sdp, &name, file, fl); - else - return gfs2_lm_plock(sdp, &name, file, cmd, fl); -} - -static int do_flock(struct file *file, int cmd, struct file_lock *fl) -{ - struct gfs2_file *fp = file->private_data; - struct gfs2_holder *fl_gh = &fp->f_fl_gh; - struct gfs2_inode *ip = GFS2_I(file->f_dentry->d_inode); - struct gfs2_glock *gl; - unsigned int state; - int flags; - int error = 0; - - state = (fl->fl_type == F_WRLCK) ? LM_ST_EXCLUSIVE : LM_ST_SHARED; - flags = (IS_SETLKW(cmd) ? 0 : LM_FLAG_TRY) | GL_EXACT | GL_NOCACHE; - - mutex_lock(&fp->f_fl_mutex); - - gl = fl_gh->gh_gl; - if (gl) { - if (fl_gh->gh_state == state) - goto out; - gfs2_glock_hold(gl); - flock_lock_file_wait(file, - &(struct file_lock){.fl_type = F_UNLCK}); - gfs2_glock_dq_uninit(fl_gh); - } else { - error = gfs2_glock_get(GFS2_SB(&ip->i_inode), - ip->i_num.no_addr, &gfs2_flock_glops, - CREATE, &gl); - if (error) - goto out; - } - - gfs2_holder_init(gl, state, flags, fl_gh); - gfs2_glock_put(gl); - - error = gfs2_glock_nq(fl_gh); - if (error) { - gfs2_holder_uninit(fl_gh); - if (error == GLR_TRYFAILED) - error = -EAGAIN; - } else { - error = flock_lock_file_wait(file, fl); - gfs2_assert_warn(GFS2_SB(&ip->i_inode), !error); - } - -out: - mutex_unlock(&fp->f_fl_mutex); - return error; -} - -static void do_unflock(struct file *file, struct file_lock *fl) -{ - struct gfs2_file *fp = file->private_data; - struct gfs2_holder *fl_gh = &fp->f_fl_gh; - - mutex_lock(&fp->f_fl_mutex); - flock_lock_file_wait(file, fl); - if (fl_gh->gh_gl) - gfs2_glock_dq_uninit(fl_gh); - mutex_unlock(&fp->f_fl_mutex); -} - -/** - * gfs2_flock - acquire/release a flock lock on a file - * @file: the file pointer - * @cmd: either modify or retrieve lock state, possibly wait - * @fl: type and range of lock - * - * Returns: errno - */ - -static int gfs2_flock(struct file *file, int cmd, struct file_lock *fl) -{ - struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); - struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host); - - if (!(fl->fl_flags & FL_FLOCK)) - return -ENOLCK; - if ((ip->i_di.di_mode & (S_ISGID | S_IXGRP)) == S_ISGID) - return -ENOLCK; - - if (sdp->sd_args.ar_localflocks) - return flock_lock_file_wait(file, fl); - - if (fl->fl_type == F_UNLCK) { - do_unflock(file, fl); - return 0; - } else { - return do_flock(file, cmd, fl); - } -} - -const struct file_operations gfs2_file_fops = { - .llseek = gfs2_llseek, - .read = do_sync_read, - .aio_read = generic_file_aio_read, - .write = do_sync_write, - .aio_write = generic_file_aio_write, - .unlocked_ioctl = gfs2_ioctl, - .mmap = gfs2_mmap, - .open = gfs2_open, - .release = gfs2_close, - .fsync = gfs2_fsync, - .lock = gfs2_lock, - .sendfile = generic_file_sendfile, - .flock = gfs2_flock, - .splice_read = generic_file_splice_read, - .splice_write = generic_file_splice_write, -}; - -const struct file_operations gfs2_dir_fops = { - .readdir = gfs2_readdir, - .unlocked_ioctl = gfs2_ioctl, - .open = gfs2_open, - .release = gfs2_close, - .fsync = gfs2_fsync, - .lock = gfs2_lock, - .flock = gfs2_flock, -}; - diff --git a/trunk/fs/gfs2/ops_file.h b/trunk/fs/gfs2/ops_file.h deleted file mode 100644 index ce319f89ec8e..000000000000 --- a/trunk/fs/gfs2/ops_file.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_FILE_DOT_H__ -#define __OPS_FILE_DOT_H__ - -#include -struct gfs2_inode; - -extern struct file gfs2_internal_file_sentinel; -extern int gfs2_internal_read(struct gfs2_inode *ip, - struct file_ra_state *ra_state, - char *buf, loff_t *pos, unsigned size); - -extern const struct file_operations gfs2_file_fops; -extern const struct file_operations gfs2_dir_fops; - -#endif /* __OPS_FILE_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_fstype.c b/trunk/fs/gfs2/ops_fstype.c deleted file mode 100644 index 178b33911843..000000000000 --- a/trunk/fs/gfs2/ops_fstype.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "daemon.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "lm.h" -#include "mount.h" -#include "ops_export.h" -#include "ops_fstype.h" -#include "ops_super.h" -#include "recovery.h" -#include "rgrp.h" -#include "super.h" -#include "sys.h" -#include "util.h" - -#define DO 0 -#define UNDO 1 - -extern struct dentry_operations gfs2_dops; - -static struct gfs2_sbd *init_sbd(struct super_block *sb) -{ - struct gfs2_sbd *sdp; - - sdp = kzalloc(sizeof(struct gfs2_sbd), GFP_KERNEL); - if (!sdp) - return NULL; - - sb->s_fs_info = sdp; - sdp->sd_vfs = sb; - - gfs2_tune_init(&sdp->sd_tune); - - INIT_LIST_HEAD(&sdp->sd_reclaim_list); - spin_lock_init(&sdp->sd_reclaim_lock); - init_waitqueue_head(&sdp->sd_reclaim_wq); - - mutex_init(&sdp->sd_inum_mutex); - spin_lock_init(&sdp->sd_statfs_spin); - mutex_init(&sdp->sd_statfs_mutex); - - spin_lock_init(&sdp->sd_rindex_spin); - mutex_init(&sdp->sd_rindex_mutex); - INIT_LIST_HEAD(&sdp->sd_rindex_list); - INIT_LIST_HEAD(&sdp->sd_rindex_mru_list); - INIT_LIST_HEAD(&sdp->sd_rindex_recent_list); - - INIT_LIST_HEAD(&sdp->sd_jindex_list); - spin_lock_init(&sdp->sd_jindex_spin); - mutex_init(&sdp->sd_jindex_mutex); - - INIT_LIST_HEAD(&sdp->sd_quota_list); - spin_lock_init(&sdp->sd_quota_spin); - mutex_init(&sdp->sd_quota_mutex); - - spin_lock_init(&sdp->sd_log_lock); - - INIT_LIST_HEAD(&sdp->sd_log_le_gl); - INIT_LIST_HEAD(&sdp->sd_log_le_buf); - INIT_LIST_HEAD(&sdp->sd_log_le_revoke); - INIT_LIST_HEAD(&sdp->sd_log_le_rg); - INIT_LIST_HEAD(&sdp->sd_log_le_databuf); - - mutex_init(&sdp->sd_log_reserve_mutex); - INIT_LIST_HEAD(&sdp->sd_ail1_list); - INIT_LIST_HEAD(&sdp->sd_ail2_list); - - init_rwsem(&sdp->sd_log_flush_lock); - INIT_LIST_HEAD(&sdp->sd_log_flush_list); - - INIT_LIST_HEAD(&sdp->sd_revoke_list); - - mutex_init(&sdp->sd_freeze_lock); - - return sdp; -} - -static void init_vfs(struct super_block *sb, unsigned noatime) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - - sb->s_magic = GFS2_MAGIC; - sb->s_op = &gfs2_super_ops; - sb->s_export_op = &gfs2_export_ops; - sb->s_maxbytes = MAX_LFS_FILESIZE; - - if (sb->s_flags & (MS_NOATIME | MS_NODIRATIME)) - set_bit(noatime, &sdp->sd_flags); - - /* Don't let the VFS update atimes. GFS2 handles this itself. */ - sb->s_flags |= MS_NOATIME | MS_NODIRATIME; -} - -static int init_names(struct gfs2_sbd *sdp, int silent) -{ - struct page *page; - char *proto, *table; - int error = 0; - - proto = sdp->sd_args.ar_lockproto; - table = sdp->sd_args.ar_locktable; - - /* Try to autodetect */ - - if (!proto[0] || !table[0]) { - struct gfs2_sb *sb; - page = gfs2_read_super(sdp->sd_vfs, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); - if (!page) - return -ENOBUFS; - sb = kmap(page); - gfs2_sb_in(&sdp->sd_sb, sb); - kunmap(page); - __free_page(page); - - error = gfs2_check_sb(sdp, &sdp->sd_sb, silent); - if (error) - goto out; - - if (!proto[0]) - proto = sdp->sd_sb.sb_lockproto; - if (!table[0]) - table = sdp->sd_sb.sb_locktable; - } - - if (!table[0]) - table = sdp->sd_vfs->s_id; - - snprintf(sdp->sd_proto_name, GFS2_FSNAME_LEN, "%s", proto); - snprintf(sdp->sd_table_name, GFS2_FSNAME_LEN, "%s", table); - -out: - return error; -} - -static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh, - int undo) -{ - struct task_struct *p; - int error = 0; - - if (undo) - goto fail_trans; - - p = kthread_run(gfs2_scand, sdp, "gfs2_scand"); - error = IS_ERR(p); - if (error) { - fs_err(sdp, "can't start scand thread: %d\n", error); - return error; - } - sdp->sd_scand_process = p; - - for (sdp->sd_glockd_num = 0; - sdp->sd_glockd_num < sdp->sd_args.ar_num_glockd; - sdp->sd_glockd_num++) { - p = kthread_run(gfs2_glockd, sdp, "gfs2_glockd"); - error = IS_ERR(p); - if (error) { - fs_err(sdp, "can't start glockd thread: %d\n", error); - goto fail; - } - sdp->sd_glockd_process[sdp->sd_glockd_num] = p; - } - - error = gfs2_glock_nq_num(sdp, - GFS2_MOUNT_LOCK, &gfs2_nondisk_glops, - LM_ST_EXCLUSIVE, LM_FLAG_NOEXP | GL_NOCACHE, - mount_gh); - if (error) { - fs_err(sdp, "can't acquire mount glock: %d\n", error); - goto fail; - } - - error = gfs2_glock_nq_num(sdp, - GFS2_LIVE_LOCK, &gfs2_nondisk_glops, - LM_ST_SHARED, - LM_FLAG_NOEXP | GL_EXACT, - &sdp->sd_live_gh); - if (error) { - fs_err(sdp, "can't acquire live glock: %d\n", error); - goto fail_mount; - } - - error = gfs2_glock_get(sdp, GFS2_RENAME_LOCK, &gfs2_nondisk_glops, - CREATE, &sdp->sd_rename_gl); - if (error) { - fs_err(sdp, "can't create rename glock: %d\n", error); - goto fail_live; - } - - error = gfs2_glock_get(sdp, GFS2_TRANS_LOCK, &gfs2_trans_glops, - CREATE, &sdp->sd_trans_gl); - if (error) { - fs_err(sdp, "can't create transaction glock: %d\n", error); - goto fail_rename; - } - set_bit(GLF_STICKY, &sdp->sd_trans_gl->gl_flags); - - return 0; - -fail_trans: - gfs2_glock_put(sdp->sd_trans_gl); -fail_rename: - gfs2_glock_put(sdp->sd_rename_gl); -fail_live: - gfs2_glock_dq_uninit(&sdp->sd_live_gh); -fail_mount: - gfs2_glock_dq_uninit(mount_gh); -fail: - while (sdp->sd_glockd_num--) - kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]); - - kthread_stop(sdp->sd_scand_process); - return error; -} - -static struct inode *gfs2_lookup_root(struct super_block *sb, - struct gfs2_inum *inum) -{ - return gfs2_inode_lookup(sb, inum, DT_DIR); -} - -static int init_sb(struct gfs2_sbd *sdp, int silent, int undo) -{ - struct super_block *sb = sdp->sd_vfs; - struct gfs2_holder sb_gh; - struct gfs2_inum *inum; - struct inode *inode; - int error = 0; - - if (undo) { - if (sb->s_root) { - dput(sb->s_root); - sb->s_root = NULL; - } - return 0; - } - - error = gfs2_glock_nq_num(sdp, GFS2_SB_LOCK, &gfs2_meta_glops, - LM_ST_SHARED, 0, &sb_gh); - if (error) { - fs_err(sdp, "can't acquire superblock glock: %d\n", error); - return error; - } - - error = gfs2_read_sb(sdp, sb_gh.gh_gl, silent); - if (error) { - fs_err(sdp, "can't read superblock: %d\n", error); - goto out; - } - - /* Set up the buffer cache and SB for real */ - if (sdp->sd_sb.sb_bsize < bdev_hardsect_size(sb->s_bdev)) { - error = -EINVAL; - fs_err(sdp, "FS block size (%u) is too small for device " - "block size (%u)\n", - sdp->sd_sb.sb_bsize, bdev_hardsect_size(sb->s_bdev)); - goto out; - } - if (sdp->sd_sb.sb_bsize > PAGE_SIZE) { - error = -EINVAL; - fs_err(sdp, "FS block size (%u) is too big for machine " - "page size (%u)\n", - sdp->sd_sb.sb_bsize, (unsigned int)PAGE_SIZE); - goto out; - } - sb_set_blocksize(sb, sdp->sd_sb.sb_bsize); - - /* Get the root inode */ - inum = &sdp->sd_sb.sb_root_dir; - if (sb->s_type == &gfs2meta_fs_type) - inum = &sdp->sd_sb.sb_master_dir; - inode = gfs2_lookup_root(sb, inum); - if (IS_ERR(inode)) { - error = PTR_ERR(inode); - fs_err(sdp, "can't read in root inode: %d\n", error); - goto out; - } - - sb->s_root = d_alloc_root(inode); - if (!sb->s_root) { - fs_err(sdp, "can't get root dentry\n"); - error = -ENOMEM; - iput(inode); - } - sb->s_root->d_op = &gfs2_dops; -out: - gfs2_glock_dq_uninit(&sb_gh); - return error; -} - -static int init_journal(struct gfs2_sbd *sdp, int undo) -{ - struct gfs2_holder ji_gh; - struct task_struct *p; - struct gfs2_inode *ip; - int jindex = 1; - int error = 0; - - if (undo) { - jindex = 0; - goto fail_recoverd; - } - - sdp->sd_jindex = gfs2_lookup_simple(sdp->sd_master_dir, "jindex"); - if (IS_ERR(sdp->sd_jindex)) { - fs_err(sdp, "can't lookup journal index: %d\n", error); - return PTR_ERR(sdp->sd_jindex); - } - ip = GFS2_I(sdp->sd_jindex); - set_bit(GLF_STICKY, &ip->i_gl->gl_flags); - - /* Load in the journal index special file */ - - error = gfs2_jindex_hold(sdp, &ji_gh); - if (error) { - fs_err(sdp, "can't read journal index: %d\n", error); - goto fail; - } - - error = -EINVAL; - if (!gfs2_jindex_size(sdp)) { - fs_err(sdp, "no journals!\n"); - goto fail_jindex; - } - - if (sdp->sd_args.ar_spectator) { - sdp->sd_jdesc = gfs2_jdesc_find(sdp, 0); - sdp->sd_log_blks_free = sdp->sd_jdesc->jd_blocks; - } else { - if (sdp->sd_lockstruct.ls_jid >= gfs2_jindex_size(sdp)) { - fs_err(sdp, "can't mount journal #%u\n", - sdp->sd_lockstruct.ls_jid); - fs_err(sdp, "there are only %u journals (0 - %u)\n", - gfs2_jindex_size(sdp), - gfs2_jindex_size(sdp) - 1); - goto fail_jindex; - } - sdp->sd_jdesc = gfs2_jdesc_find(sdp, sdp->sd_lockstruct.ls_jid); - - error = gfs2_glock_nq_num(sdp, sdp->sd_lockstruct.ls_jid, - &gfs2_journal_glops, - LM_ST_EXCLUSIVE, LM_FLAG_NOEXP, - &sdp->sd_journal_gh); - if (error) { - fs_err(sdp, "can't acquire journal glock: %d\n", error); - goto fail_jindex; - } - - ip = GFS2_I(sdp->sd_jdesc->jd_inode); - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, - LM_FLAG_NOEXP | GL_EXACT, - &sdp->sd_jinode_gh); - if (error) { - fs_err(sdp, "can't acquire journal inode glock: %d\n", - error); - goto fail_journal_gh; - } - - error = gfs2_jdesc_check(sdp->sd_jdesc); - if (error) { - fs_err(sdp, "my journal (%u) is bad: %d\n", - sdp->sd_jdesc->jd_jid, error); - goto fail_jinode_gh; - } - sdp->sd_log_blks_free = sdp->sd_jdesc->jd_blocks; - } - - if (sdp->sd_lockstruct.ls_first) { - unsigned int x; - for (x = 0; x < sdp->sd_journals; x++) { - error = gfs2_recover_journal(gfs2_jdesc_find(sdp, x)); - if (error) { - fs_err(sdp, "error recovering journal %u: %d\n", - x, error); - goto fail_jinode_gh; - } - } - - gfs2_lm_others_may_mount(sdp); - } else if (!sdp->sd_args.ar_spectator) { - error = gfs2_recover_journal(sdp->sd_jdesc); - if (error) { - fs_err(sdp, "error recovering my journal: %d\n", error); - goto fail_jinode_gh; - } - } - - set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags); - gfs2_glock_dq_uninit(&ji_gh); - jindex = 0; - - p = kthread_run(gfs2_recoverd, sdp, "gfs2_recoverd"); - error = IS_ERR(p); - if (error) { - fs_err(sdp, "can't start recoverd thread: %d\n", error); - goto fail_jinode_gh; - } - sdp->sd_recoverd_process = p; - - return 0; - -fail_recoverd: - kthread_stop(sdp->sd_recoverd_process); -fail_jinode_gh: - if (!sdp->sd_args.ar_spectator) - gfs2_glock_dq_uninit(&sdp->sd_jinode_gh); -fail_journal_gh: - if (!sdp->sd_args.ar_spectator) - gfs2_glock_dq_uninit(&sdp->sd_journal_gh); -fail_jindex: - gfs2_jindex_free(sdp); - if (jindex) - gfs2_glock_dq_uninit(&ji_gh); -fail: - iput(sdp->sd_jindex); - return error; -} - - -static int init_inodes(struct gfs2_sbd *sdp, int undo) -{ - int error = 0; - struct gfs2_inode *ip; - struct inode *inode; - - if (undo) - goto fail_qinode; - - inode = gfs2_lookup_root(sdp->sd_vfs, &sdp->sd_sb.sb_master_dir); - if (IS_ERR(inode)) { - error = PTR_ERR(inode); - fs_err(sdp, "can't read in master directory: %d\n", error); - goto fail; - } - sdp->sd_master_dir = inode; - - error = init_journal(sdp, undo); - if (error) - goto fail_master; - - /* Read in the master inode number inode */ - sdp->sd_inum_inode = gfs2_lookup_simple(sdp->sd_master_dir, "inum"); - if (IS_ERR(sdp->sd_inum_inode)) { - error = PTR_ERR(sdp->sd_inum_inode); - fs_err(sdp, "can't read in inum inode: %d\n", error); - goto fail_journal; - } - - - /* Read in the master statfs inode */ - sdp->sd_statfs_inode = gfs2_lookup_simple(sdp->sd_master_dir, "statfs"); - if (IS_ERR(sdp->sd_statfs_inode)) { - error = PTR_ERR(sdp->sd_statfs_inode); - fs_err(sdp, "can't read in statfs inode: %d\n", error); - goto fail_inum; - } - - /* Read in the resource index inode */ - sdp->sd_rindex = gfs2_lookup_simple(sdp->sd_master_dir, "rindex"); - if (IS_ERR(sdp->sd_rindex)) { - error = PTR_ERR(sdp->sd_rindex); - fs_err(sdp, "can't get resource index inode: %d\n", error); - goto fail_statfs; - } - ip = GFS2_I(sdp->sd_rindex); - set_bit(GLF_STICKY, &ip->i_gl->gl_flags); - sdp->sd_rindex_vn = ip->i_gl->gl_vn - 1; - - /* Read in the quota inode */ - sdp->sd_quota_inode = gfs2_lookup_simple(sdp->sd_master_dir, "quota"); - if (IS_ERR(sdp->sd_quota_inode)) { - error = PTR_ERR(sdp->sd_quota_inode); - fs_err(sdp, "can't get quota file inode: %d\n", error); - goto fail_rindex; - } - return 0; - -fail_qinode: - iput(sdp->sd_quota_inode); -fail_rindex: - gfs2_clear_rgrpd(sdp); - iput(sdp->sd_rindex); -fail_statfs: - iput(sdp->sd_statfs_inode); -fail_inum: - iput(sdp->sd_inum_inode); -fail_journal: - init_journal(sdp, UNDO); -fail_master: - iput(sdp->sd_master_dir); -fail: - return error; -} - -static int init_per_node(struct gfs2_sbd *sdp, int undo) -{ - struct inode *pn = NULL; - char buf[30]; - int error = 0; - struct gfs2_inode *ip; - - if (sdp->sd_args.ar_spectator) - return 0; - - if (undo) - goto fail_qc_gh; - - pn = gfs2_lookup_simple(sdp->sd_master_dir, "per_node"); - if (IS_ERR(pn)) { - error = PTR_ERR(pn); - fs_err(sdp, "can't find per_node directory: %d\n", error); - return error; - } - - sprintf(buf, "inum_range%u", sdp->sd_jdesc->jd_jid); - sdp->sd_ir_inode = gfs2_lookup_simple(pn, buf); - if (IS_ERR(sdp->sd_ir_inode)) { - error = PTR_ERR(sdp->sd_ir_inode); - fs_err(sdp, "can't find local \"ir\" file: %d\n", error); - goto fail; - } - - sprintf(buf, "statfs_change%u", sdp->sd_jdesc->jd_jid); - sdp->sd_sc_inode = gfs2_lookup_simple(pn, buf); - if (IS_ERR(sdp->sd_sc_inode)) { - error = PTR_ERR(sdp->sd_sc_inode); - fs_err(sdp, "can't find local \"sc\" file: %d\n", error); - goto fail_ir_i; - } - - sprintf(buf, "quota_change%u", sdp->sd_jdesc->jd_jid); - sdp->sd_qc_inode = gfs2_lookup_simple(pn, buf); - if (IS_ERR(sdp->sd_qc_inode)) { - error = PTR_ERR(sdp->sd_qc_inode); - fs_err(sdp, "can't find local \"qc\" file: %d\n", error); - goto fail_ut_i; - } - - iput(pn); - pn = NULL; - - ip = GFS2_I(sdp->sd_ir_inode); - error = gfs2_glock_nq_init(ip->i_gl, - LM_ST_EXCLUSIVE, 0, - &sdp->sd_ir_gh); - if (error) { - fs_err(sdp, "can't lock local \"ir\" file: %d\n", error); - goto fail_qc_i; - } - - ip = GFS2_I(sdp->sd_sc_inode); - error = gfs2_glock_nq_init(ip->i_gl, - LM_ST_EXCLUSIVE, 0, - &sdp->sd_sc_gh); - if (error) { - fs_err(sdp, "can't lock local \"sc\" file: %d\n", error); - goto fail_ir_gh; - } - - ip = GFS2_I(sdp->sd_qc_inode); - error = gfs2_glock_nq_init(ip->i_gl, - LM_ST_EXCLUSIVE, 0, - &sdp->sd_qc_gh); - if (error) { - fs_err(sdp, "can't lock local \"qc\" file: %d\n", error); - goto fail_ut_gh; - } - - return 0; - -fail_qc_gh: - gfs2_glock_dq_uninit(&sdp->sd_qc_gh); -fail_ut_gh: - gfs2_glock_dq_uninit(&sdp->sd_sc_gh); -fail_ir_gh: - gfs2_glock_dq_uninit(&sdp->sd_ir_gh); -fail_qc_i: - iput(sdp->sd_qc_inode); -fail_ut_i: - iput(sdp->sd_sc_inode); -fail_ir_i: - iput(sdp->sd_ir_inode); -fail: - if (pn) - iput(pn); - return error; -} - -static int init_threads(struct gfs2_sbd *sdp, int undo) -{ - struct task_struct *p; - int error = 0; - - if (undo) - goto fail_quotad; - - sdp->sd_log_flush_time = jiffies; - sdp->sd_jindex_refresh_time = jiffies; - - p = kthread_run(gfs2_logd, sdp, "gfs2_logd"); - error = IS_ERR(p); - if (error) { - fs_err(sdp, "can't start logd thread: %d\n", error); - return error; - } - sdp->sd_logd_process = p; - - sdp->sd_statfs_sync_time = jiffies; - sdp->sd_quota_sync_time = jiffies; - - p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad"); - error = IS_ERR(p); - if (error) { - fs_err(sdp, "can't start quotad thread: %d\n", error); - goto fail; - } - sdp->sd_quotad_process = p; - - return 0; - - -fail_quotad: - kthread_stop(sdp->sd_quotad_process); -fail: - kthread_stop(sdp->sd_logd_process); - return error; -} - -/** - * fill_super - Read in superblock - * @sb: The VFS superblock - * @data: Mount options - * @silent: Don't complain if it's not a GFS2 filesystem - * - * Returns: errno - */ - -static int fill_super(struct super_block *sb, void *data, int silent) -{ - struct gfs2_sbd *sdp; - struct gfs2_holder mount_gh; - int error; - - sdp = init_sbd(sb); - if (!sdp) { - printk(KERN_WARNING "GFS2: can't alloc struct gfs2_sbd\n"); - return -ENOMEM; - } - - error = gfs2_mount_args(sdp, (char *)data, 0); - if (error) { - printk(KERN_WARNING "GFS2: can't parse mount arguments\n"); - goto fail; - } - - init_vfs(sb, SDF_NOATIME); - - /* Set up the buffer cache and fill in some fake block size values - to allow us to read-in the on-disk superblock. */ - sdp->sd_sb.sb_bsize = sb_min_blocksize(sb, GFS2_BASIC_BLOCK); - sdp->sd_sb.sb_bsize_shift = sb->s_blocksize_bits; - sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift - - GFS2_BASIC_BLOCK_SHIFT; - sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift; - - error = init_names(sdp, silent); - if (error) - goto fail; - - error = gfs2_sys_fs_add(sdp); - if (error) - goto fail; - - error = gfs2_lm_mount(sdp, silent); - if (error) - goto fail_sys; - - error = init_locking(sdp, &mount_gh, DO); - if (error) - goto fail_lm; - - error = init_sb(sdp, silent, DO); - if (error) - goto fail_locking; - - error = init_inodes(sdp, DO); - if (error) - goto fail_sb; - - error = init_per_node(sdp, DO); - if (error) - goto fail_inodes; - - error = gfs2_statfs_init(sdp); - if (error) { - fs_err(sdp, "can't initialize statfs subsystem: %d\n", error); - goto fail_per_node; - } - - error = init_threads(sdp, DO); - if (error) - goto fail_per_node; - - if (!(sb->s_flags & MS_RDONLY)) { - error = gfs2_make_fs_rw(sdp); - if (error) { - fs_err(sdp, "can't make FS RW: %d\n", error); - goto fail_threads; - } - } - - gfs2_glock_dq_uninit(&mount_gh); - - return 0; - -fail_threads: - init_threads(sdp, UNDO); -fail_per_node: - init_per_node(sdp, UNDO); -fail_inodes: - init_inodes(sdp, UNDO); -fail_sb: - init_sb(sdp, 0, UNDO); -fail_locking: - init_locking(sdp, &mount_gh, UNDO); -fail_lm: - gfs2_gl_hash_clear(sdp, WAIT); - gfs2_lm_unmount(sdp); - while (invalidate_inodes(sb)) - yield(); -fail_sys: - gfs2_sys_fs_del(sdp); -fail: - kfree(sdp); - sb->s_fs_info = NULL; - return error; -} - -static int gfs2_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, struct vfsmount *mnt) -{ - struct super_block *sb; - struct gfs2_sbd *sdp; - int error = get_sb_bdev(fs_type, flags, dev_name, data, fill_super, mnt); - if (error) - goto out; - sb = mnt->mnt_sb; - sdp = sb->s_fs_info; - sdp->sd_gfs2mnt = mnt; -out: - return error; -} - -static int fill_super_meta(struct super_block *sb, struct super_block *new, - void *data, int silent) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - struct inode *inode; - int error = 0; - - new->s_fs_info = sdp; - sdp->sd_vfs_meta = sb; - - init_vfs(new, SDF_NOATIME); - - /* Get the master inode */ - inode = igrab(sdp->sd_master_dir); - - new->s_root = d_alloc_root(inode); - if (!new->s_root) { - fs_err(sdp, "can't get root dentry\n"); - error = -ENOMEM; - iput(inode); - } - new->s_root->d_op = &gfs2_dops; - - return error; -} - -static int set_bdev_super(struct super_block *s, void *data) -{ - s->s_bdev = data; - s->s_dev = s->s_bdev->bd_dev; - return 0; -} - -static int test_bdev_super(struct super_block *s, void *data) -{ - return s->s_bdev == data; -} - -static struct super_block* get_gfs2_sb(const char *dev_name) -{ - struct kstat stat; - struct nameidata nd; - struct file_system_type *fstype; - struct super_block *sb = NULL, *s; - struct list_head *l; - int error; - - error = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); - if (error) { - printk(KERN_WARNING "GFS2: path_lookup on %s returned error\n", - dev_name); - goto out; - } - error = vfs_getattr(nd.mnt, nd.dentry, &stat); - - fstype = get_fs_type("gfs2"); - list_for_each(l, &fstype->fs_supers) { - s = list_entry(l, struct super_block, s_instances); - if ((S_ISBLK(stat.mode) && s->s_dev == stat.rdev) || - (S_ISDIR(stat.mode) && s == nd.dentry->d_inode->i_sb)) { - sb = s; - goto free_nd; - } - } - - printk(KERN_WARNING "GFS2: Unrecognized block device or " - "mount point %s", dev_name); - -free_nd: - path_release(&nd); -out: - return sb; -} - -static int gfs2_get_sb_meta(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, struct vfsmount *mnt) -{ - int error = 0; - struct super_block *sb = NULL, *new; - struct gfs2_sbd *sdp; - char *gfs2mnt = NULL; - - sb = get_gfs2_sb(dev_name); - if (!sb) { - printk(KERN_WARNING "GFS2: gfs2 mount does not exist\n"); - error = -ENOENT; - goto error; - } - sdp = (struct gfs2_sbd*) sb->s_fs_info; - if (sdp->sd_vfs_meta) { - printk(KERN_WARNING "GFS2: gfs2meta mount already exists\n"); - error = -EBUSY; - goto error; - } - mutex_lock(&sb->s_bdev->bd_mount_mutex); - new = sget(fs_type, test_bdev_super, set_bdev_super, sb->s_bdev); - mutex_unlock(&sb->s_bdev->bd_mount_mutex); - if (IS_ERR(new)) { - error = PTR_ERR(new); - goto error; - } - module_put(fs_type->owner); - new->s_flags = flags; - strlcpy(new->s_id, sb->s_id, sizeof(new->s_id)); - sb_set_blocksize(new, sb->s_blocksize); - error = fill_super_meta(sb, new, data, flags & MS_SILENT ? 1 : 0); - if (error) { - up_write(&new->s_umount); - deactivate_super(new); - goto error; - } - - new->s_flags |= MS_ACTIVE; - - /* Grab a reference to the gfs2 mount point */ - atomic_inc(&sdp->sd_gfs2mnt->mnt_count); - return simple_set_mnt(mnt, new); -error: - if (gfs2mnt) - kfree(gfs2mnt); - return error; -} - -static void gfs2_kill_sb(struct super_block *sb) -{ - kill_block_super(sb); -} - -static void gfs2_kill_sb_meta(struct super_block *sb) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - generic_shutdown_super(sb); - sdp->sd_vfs_meta = NULL; - atomic_dec(&sdp->sd_gfs2mnt->mnt_count); -} - -struct file_system_type gfs2_fs_type = { - .name = "gfs2", - .fs_flags = FS_REQUIRES_DEV, - .get_sb = gfs2_get_sb, - .kill_sb = gfs2_kill_sb, - .owner = THIS_MODULE, -}; - -struct file_system_type gfs2meta_fs_type = { - .name = "gfs2meta", - .fs_flags = FS_REQUIRES_DEV, - .get_sb = gfs2_get_sb_meta, - .kill_sb = gfs2_kill_sb_meta, - .owner = THIS_MODULE, -}; - diff --git a/trunk/fs/gfs2/ops_fstype.h b/trunk/fs/gfs2/ops_fstype.h deleted file mode 100644 index 7cc2c296271b..000000000000 --- a/trunk/fs/gfs2/ops_fstype.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_FSTYPE_DOT_H__ -#define __OPS_FSTYPE_DOT_H__ - -#include - -extern struct file_system_type gfs2_fs_type; -extern struct file_system_type gfs2meta_fs_type; - -#endif /* __OPS_FSTYPE_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_inode.c b/trunk/fs/gfs2/ops_inode.c deleted file mode 100644 index ef6e5ed70e94..000000000000 --- a/trunk/fs/gfs2/ops_inode.c +++ /dev/null @@ -1,1151 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "acl.h" -#include "bmap.h" -#include "dir.h" -#include "eaops.h" -#include "eattr.h" -#include "glock.h" -#include "inode.h" -#include "meta_io.h" -#include "ops_dentry.h" -#include "ops_inode.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" - -/** - * gfs2_create - Create a file - * @dir: The directory in which to create the file - * @dentry: The dentry of the new file - * @mode: The mode of the new file - * - * Returns: errno - */ - -static int gfs2_create(struct inode *dir, struct dentry *dentry, - int mode, struct nameidata *nd) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - for (;;) { - inode = gfs2_createi(ghs, &dentry->d_name, S_IFREG | mode); - if (!IS_ERR(inode)) { - gfs2_trans_end(sdp); - if (dip->i_alloc.al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - gfs2_glock_dq_uninit_m(2, ghs); - mark_inode_dirty(inode); - break; - } else if (PTR_ERR(inode) != -EEXIST || - (nd->intent.open.flags & O_EXCL)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd); - if (inode) { - if (!IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - break; - } else { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - } - } - - d_instantiate(dentry, inode); - - return 0; -} - -/** - * gfs2_lookup - Look up a filename in a directory and return its inode - * @dir: The directory inode - * @dentry: The dentry of the new inode - * @nd: passed from Linux VFS, ignored by us - * - * Called by the VFS layer. Lock dir and call gfs2_lookupi() - * - * Returns: errno - */ - -static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - struct inode *inode = NULL; - - dentry->d_op = &gfs2_dops; - - inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd); - if (inode && IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); - - if (inode) - return d_splice_alias(inode, dentry); - d_add(dentry, inode); - - return NULL; -} - -/** - * gfs2_link - Link to a file - * @old_dentry: The inode to link - * @dir: Add link to this directory - * @dentry: The name of the link - * - * Link the inode in "old_dentry" into the directory "dir" with the - * name in "dentry". - * - * Returns: errno - */ - -static int gfs2_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct inode *inode = old_dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder ghs[2]; - int alloc_required; - int error; - - if (S_ISDIR(ip->i_di.di_mode)) - return -EPERM; - - gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - - error = gfs2_glock_nq_m(2, ghs); - if (error) - goto out; - - error = permission(dir, MAY_WRITE | MAY_EXEC, NULL); - if (error) - goto out_gunlock; - - error = gfs2_dir_search(dir, &dentry->d_name, NULL, NULL); - switch (error) { - case -ENOENT: - break; - case 0: - error = -EEXIST; - default: - goto out_gunlock; - } - - error = -EINVAL; - if (!dip->i_di.di_nlink) - goto out_gunlock; - error = -EFBIG; - if (dip->i_di.di_entries == (u32)-1) - goto out_gunlock; - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto out_gunlock; - error = -EINVAL; - if (!ip->i_di.di_nlink) - goto out_gunlock; - error = -EMLINK; - if (ip->i_di.di_nlink == (u32)-1) - goto out_gunlock; - - alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name); - if (error < 0) - goto out_gunlock; - error = 0; - - if (alloc_required) { - struct gfs2_alloc *al = gfs2_alloc_get(dip); - - error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out_alloc; - - error = gfs2_quota_check(dip, dip->i_di.di_uid, - dip->i_di.di_gid); - if (error) - goto out_gunlock_q; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve(dip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_ri.ri_length + - 2 * RES_DINODE + RES_STATFS + - RES_QUOTA, 0); - if (error) - goto out_ipres; - } else { - error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0); - if (error) - goto out_ipres; - } - - error = gfs2_dir_add(dir, &dentry->d_name, &ip->i_num, - IF2DT(ip->i_di.di_mode)); - if (error) - goto out_end_trans; - - error = gfs2_change_nlink(ip, +1); - -out_end_trans: - gfs2_trans_end(sdp); -out_ipres: - if (alloc_required) - gfs2_inplace_release(dip); -out_gunlock_q: - if (alloc_required) - gfs2_quota_unlock(dip); -out_alloc: - if (alloc_required) - gfs2_alloc_put(dip); -out_gunlock: - gfs2_glock_dq_m(2, ghs); -out: - gfs2_holder_uninit(ghs); - gfs2_holder_uninit(ghs + 1); - if (!error) { - atomic_inc(&inode->i_count); - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - } - return error; -} - -/** - * gfs2_unlink - Unlink a file - * @dir: The inode of the directory containing the file to unlink - * @dentry: The file itself - * - * Unlink a file. Call gfs2_unlinki() - * - * Returns: errno - */ - -static int gfs2_unlink(struct inode *dir, struct dentry *dentry) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - struct gfs2_holder ghs[2]; - int error; - - gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - - error = gfs2_glock_nq_m(2, ghs); - if (error) - goto out; - - error = gfs2_unlink_ok(dip, &dentry->d_name, ip); - if (error) - goto out_gunlock; - - error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0); - if (error) - goto out_gunlock; - - error = gfs2_dir_del(dip, &dentry->d_name); - if (error) - goto out_end_trans; - - error = gfs2_change_nlink(ip, -1); - -out_end_trans: - gfs2_trans_end(sdp); -out_gunlock: - gfs2_glock_dq_m(2, ghs); -out: - gfs2_holder_uninit(ghs); - gfs2_holder_uninit(ghs + 1); - return error; -} - -/** - * gfs2_symlink - Create a symlink - * @dir: The directory to create the symlink in - * @dentry: The dentry to put the symlink in - * @symname: The thing which the link points to - * - * Returns: errno - */ - -static int gfs2_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - struct gfs2_inode *dip = GFS2_I(dir), *ip; - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - struct buffer_head *dibh; - int size; - int error; - - /* Must be stuffed with a null terminator for gfs2_follow_link() */ - size = strlen(symname); - if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1) - return -ENAMETOOLONG; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - inode = gfs2_createi(ghs, &dentry->d_name, S_IFLNK | S_IRWXUGO); - if (IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - ip = ghs[1].gh_gl->gl_object; - - ip->i_di.di_size = size; - - error = gfs2_meta_inode_buffer(ip, &dibh); - - if (!gfs2_assert_withdraw(sdp, !error)) { - gfs2_dinode_out(&ip->i_di, dibh->b_data); - memcpy(dibh->b_data + sizeof(struct gfs2_dinode), symname, - size); - brelse(dibh); - } - - gfs2_trans_end(sdp); - if (dip->i_alloc.al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - - gfs2_glock_dq_uninit_m(2, ghs); - - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - - return 0; -} - -/** - * gfs2_mkdir - Make a directory - * @dir: The parent directory of the new one - * @dentry: The dentry of the new directory - * @mode: The mode of the new directory - * - * Returns: errno - */ - -static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct gfs2_inode *dip = GFS2_I(dir), *ip; - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - struct buffer_head *dibh; - int error; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - inode = gfs2_createi(ghs, &dentry->d_name, S_IFDIR | mode); - if (IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - ip = ghs[1].gh_gl->gl_object; - - ip->i_di.di_nlink = 2; - ip->i_di.di_size = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode); - ip->i_di.di_flags |= GFS2_DIF_JDATA; - ip->i_di.di_payload_format = GFS2_FORMAT_DE; - ip->i_di.di_entries = 2; - - error = gfs2_meta_inode_buffer(ip, &dibh); - - if (!gfs2_assert_withdraw(sdp, !error)) { - struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data; - struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1); - struct qstr str; - - gfs2_str2qstr(&str, "."); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_qstr2dirent(&str, GFS2_DIRENT_SIZE(str.len), dent); - dent->de_inum = di->di_num; /* already GFS2 endian */ - dent->de_type = cpu_to_be16(DT_DIR); - di->di_entries = cpu_to_be32(1); - - gfs2_str2qstr(&str, ".."); - dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1)); - gfs2_qstr2dirent(&str, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent); - - gfs2_inum_out(&dip->i_num, &dent->de_inum); - dent->de_type = cpu_to_be16(DT_DIR); - - gfs2_dinode_out(&ip->i_di, di); - - brelse(dibh); - } - - error = gfs2_change_nlink(dip, +1); - gfs2_assert_withdraw(sdp, !error); /* dip already pinned */ - - gfs2_trans_end(sdp); - if (dip->i_alloc.al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - - gfs2_glock_dq_uninit_m(2, ghs); - - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - - return 0; -} - -/** - * gfs2_rmdir - Remove a directory - * @dir: The parent directory of the directory to be removed - * @dentry: The dentry of the directory to remove - * - * Remove a directory. Call gfs2_rmdiri() - * - * Returns: errno - */ - -static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct gfs2_inode *dip = GFS2_I(dir); - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - struct gfs2_holder ghs[2]; - int error; - - gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); - - error = gfs2_glock_nq_m(2, ghs); - if (error) - goto out; - - error = gfs2_unlink_ok(dip, &dentry->d_name, ip); - if (error) - goto out_gunlock; - - if (ip->i_di.di_entries < 2) { - if (gfs2_consist_inode(ip)) - gfs2_dinode_print(&ip->i_di); - error = -EIO; - goto out_gunlock; - } - if (ip->i_di.di_entries > 2) { - error = -ENOTEMPTY; - goto out_gunlock; - } - - error = gfs2_trans_begin(sdp, 2 * RES_DINODE + 3 * RES_LEAF + RES_RG_BIT, 0); - if (error) - goto out_gunlock; - - error = gfs2_rmdiri(dip, &dentry->d_name, ip); - - gfs2_trans_end(sdp); - -out_gunlock: - gfs2_glock_dq_m(2, ghs); -out: - gfs2_holder_uninit(ghs); - gfs2_holder_uninit(ghs + 1); - return error; -} - -/** - * gfs2_mknod - Make a special file - * @dir: The directory in which the special file will reside - * @dentry: The dentry of the special file - * @mode: The mode of the special file - * @rdev: The device specification of the special file - * - */ - -static int gfs2_mknod(struct inode *dir, struct dentry *dentry, int mode, - dev_t dev) -{ - struct gfs2_inode *dip = GFS2_I(dir), *ip; - struct gfs2_sbd *sdp = GFS2_SB(dir); - struct gfs2_holder ghs[2]; - struct inode *inode; - struct buffer_head *dibh; - u32 major = 0, minor = 0; - int error; - - switch (mode & S_IFMT) { - case S_IFBLK: - case S_IFCHR: - major = MAJOR(dev); - minor = MINOR(dev); - break; - case S_IFIFO: - case S_IFSOCK: - break; - default: - return -EOPNOTSUPP; - }; - - gfs2_holder_init(dip->i_gl, 0, 0, ghs); - - inode = gfs2_createi(ghs, &dentry->d_name, mode); - if (IS_ERR(inode)) { - gfs2_holder_uninit(ghs); - return PTR_ERR(inode); - } - - ip = ghs[1].gh_gl->gl_object; - - ip->i_di.di_major = major; - ip->i_di.di_minor = minor; - - error = gfs2_meta_inode_buffer(ip, &dibh); - - if (!gfs2_assert_withdraw(sdp, !error)) { - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - gfs2_trans_end(sdp); - if (dip->i_alloc.al_rgd) - gfs2_inplace_release(dip); - gfs2_quota_unlock(dip); - gfs2_alloc_put(dip); - - gfs2_glock_dq_uninit_m(2, ghs); - - d_instantiate(dentry, inode); - mark_inode_dirty(inode); - - return 0; -} - -/** - * gfs2_rename - Rename a file - * @odir: Parent directory of old file name - * @odentry: The old dentry of the file - * @ndir: Parent directory of new file name - * @ndentry: The new dentry of the file - * - * Returns: errno - */ - -static int gfs2_rename(struct inode *odir, struct dentry *odentry, - struct inode *ndir, struct dentry *ndentry) -{ - struct gfs2_inode *odip = GFS2_I(odir); - struct gfs2_inode *ndip = GFS2_I(ndir); - struct gfs2_inode *ip = GFS2_I(odentry->d_inode); - struct gfs2_inode *nip = NULL; - struct gfs2_sbd *sdp = GFS2_SB(odir); - struct gfs2_holder ghs[4], r_gh; - unsigned int num_gh; - int dir_rename = 0; - int alloc_required; - unsigned int x; - int error; - - if (ndentry->d_inode) { - nip = GFS2_I(ndentry->d_inode); - if (ip == nip) - return 0; - } - - /* Make sure we aren't trying to move a dirctory into it's subdir */ - - if (S_ISDIR(ip->i_di.di_mode) && odip != ndip) { - dir_rename = 1; - - error = gfs2_glock_nq_init(sdp->sd_rename_gl, - LM_ST_EXCLUSIVE, 0, - &r_gh); - if (error) - goto out; - - error = gfs2_ok_to_move(ip, ndip); - if (error) - goto out_gunlock_r; - } - - num_gh = 1; - gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); - if (odip != ndip) { - gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); - num_gh++; - } - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); - num_gh++; - - if (nip) { - gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); - num_gh++; - } - - error = gfs2_glock_nq_m(num_gh, ghs); - if (error) - goto out_uninit; - - /* Check out the old directory */ - - error = gfs2_unlink_ok(odip, &odentry->d_name, ip); - if (error) - goto out_gunlock; - - /* Check out the new directory */ - - if (nip) { - error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip); - if (error) - goto out_gunlock; - - if (S_ISDIR(nip->i_di.di_mode)) { - if (nip->i_di.di_entries < 2) { - if (gfs2_consist_inode(nip)) - gfs2_dinode_print(&nip->i_di); - error = -EIO; - goto out_gunlock; - } - if (nip->i_di.di_entries > 2) { - error = -ENOTEMPTY; - goto out_gunlock; - } - } - } else { - error = permission(ndir, MAY_WRITE | MAY_EXEC, NULL); - if (error) - goto out_gunlock; - - error = gfs2_dir_search(ndir, &ndentry->d_name, NULL, NULL); - switch (error) { - case -ENOENT: - error = 0; - break; - case 0: - error = -EEXIST; - default: - goto out_gunlock; - }; - - if (odip != ndip) { - if (!ndip->i_di.di_nlink) { - error = -EINVAL; - goto out_gunlock; - } - if (ndip->i_di.di_entries == (u32)-1) { - error = -EFBIG; - goto out_gunlock; - } - if (S_ISDIR(ip->i_di.di_mode) && - ndip->i_di.di_nlink == (u32)-1) { - error = -EMLINK; - goto out_gunlock; - } - } - } - - /* Check out the dir to be renamed */ - - if (dir_rename) { - error = permission(odentry->d_inode, MAY_WRITE, NULL); - if (error) - goto out_gunlock; - } - - alloc_required = error = gfs2_diradd_alloc_required(ndir, &ndentry->d_name); - if (error < 0) - goto out_gunlock; - error = 0; - - if (alloc_required) { - struct gfs2_alloc *al = gfs2_alloc_get(ndip); - - error = gfs2_quota_lock(ndip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out_alloc; - - error = gfs2_quota_check(ndip, ndip->i_di.di_uid, - ndip->i_di.di_gid); - if (error) - goto out_gunlock_q; - - al->al_requested = sdp->sd_max_dirres; - - error = gfs2_inplace_reserve(ndip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_ri.ri_length + - 4 * RES_DINODE + 4 * RES_LEAF + - RES_STATFS + RES_QUOTA, 0); - if (error) - goto out_ipreserv; - } else { - error = gfs2_trans_begin(sdp, 4 * RES_DINODE + - 5 * RES_LEAF, 0); - if (error) - goto out_gunlock; - } - - /* Remove the target file, if it exists */ - - if (nip) { - if (S_ISDIR(nip->i_di.di_mode)) - error = gfs2_rmdiri(ndip, &ndentry->d_name, nip); - else { - error = gfs2_dir_del(ndip, &ndentry->d_name); - if (error) - goto out_end_trans; - error = gfs2_change_nlink(nip, -1); - } - if (error) - goto out_end_trans; - } - - if (dir_rename) { - struct qstr name; - gfs2_str2qstr(&name, ".."); - - error = gfs2_change_nlink(ndip, +1); - if (error) - goto out_end_trans; - error = gfs2_change_nlink(odip, -1); - if (error) - goto out_end_trans; - - error = gfs2_dir_mvino(ip, &name, &ndip->i_num, DT_DIR); - if (error) - goto out_end_trans; - } else { - struct buffer_head *dibh; - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - ip->i_di.di_ctime = get_seconds(); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - error = gfs2_dir_del(odip, &odentry->d_name); - if (error) - goto out_end_trans; - - error = gfs2_dir_add(ndir, &ndentry->d_name, &ip->i_num, - IF2DT(ip->i_di.di_mode)); - if (error) - goto out_end_trans; - -out_end_trans: - gfs2_trans_end(sdp); -out_ipreserv: - if (alloc_required) - gfs2_inplace_release(ndip); -out_gunlock_q: - if (alloc_required) - gfs2_quota_unlock(ndip); -out_alloc: - if (alloc_required) - gfs2_alloc_put(ndip); -out_gunlock: - gfs2_glock_dq_m(num_gh, ghs); -out_uninit: - for (x = 0; x < num_gh; x++) - gfs2_holder_uninit(ghs + x); -out_gunlock_r: - if (dir_rename) - gfs2_glock_dq_uninit(&r_gh); -out: - return error; -} - -/** - * gfs2_readlink - Read the value of a symlink - * @dentry: the symlink - * @buf: the buffer to read the symlink data into - * @size: the size of the buffer - * - * Returns: errno - */ - -static int gfs2_readlink(struct dentry *dentry, char __user *user_buf, - int user_size) -{ - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - char array[GFS2_FAST_NAME_SIZE], *buf = array; - unsigned int len = GFS2_FAST_NAME_SIZE; - int error; - - error = gfs2_readlinki(ip, &buf, &len); - if (error) - return error; - - if (user_size > len - 1) - user_size = len - 1; - - if (copy_to_user(user_buf, buf, user_size)) - error = -EFAULT; - else - error = user_size; - - if (buf != array) - kfree(buf); - - return error; -} - -/** - * gfs2_follow_link - Follow a symbolic link - * @dentry: The dentry of the link - * @nd: Data that we pass to vfs_follow_link() - * - * This can handle symlinks of any size. It is optimised for symlinks - * under GFS2_FAST_NAME_SIZE. - * - * Returns: 0 on success or error code - */ - -static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct gfs2_inode *ip = GFS2_I(dentry->d_inode); - char array[GFS2_FAST_NAME_SIZE], *buf = array; - unsigned int len = GFS2_FAST_NAME_SIZE; - int error; - - error = gfs2_readlinki(ip, &buf, &len); - if (!error) { - error = vfs_follow_link(nd, buf); - if (buf != array) - kfree(buf); - } - - return ERR_PTR(error); -} - -/** - * gfs2_permission - - * @inode: - * @mask: - * @nd: passed from Linux VFS, ignored by us - * - * Returns: errno - */ - -static int gfs2_permission(struct inode *inode, int mask, struct nameidata *nd) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder i_gh; - int error; - - if (ip->i_vn == ip->i_gl->gl_vn) - return generic_permission(inode, mask, gfs2_check_acl); - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); - if (!error) { - error = generic_permission(inode, mask, gfs2_check_acl_locked); - gfs2_glock_dq_uninit(&i_gh); - } - - return error; -} - -static int setattr_size(struct inode *inode, struct iattr *attr) -{ - struct gfs2_inode *ip = GFS2_I(inode); - int error; - - if (attr->ia_size != ip->i_di.di_size) { - error = vmtruncate(inode, attr->ia_size); - if (error) - return error; - } - - error = gfs2_truncatei(ip, attr->ia_size); - if (error) - return error; - - return error; -} - -static int setattr_chown(struct inode *inode, struct iattr *attr) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct buffer_head *dibh; - u32 ouid, ogid, nuid, ngid; - int error; - - ouid = ip->i_di.di_uid; - ogid = ip->i_di.di_gid; - nuid = attr->ia_uid; - ngid = attr->ia_gid; - - if (!(attr->ia_valid & ATTR_UID) || ouid == nuid) - ouid = nuid = NO_QUOTA_CHANGE; - if (!(attr->ia_valid & ATTR_GID) || ogid == ngid) - ogid = ngid = NO_QUOTA_CHANGE; - - gfs2_alloc_get(ip); - - error = gfs2_quota_lock(ip, nuid, ngid); - if (error) - goto out_alloc; - - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { - error = gfs2_quota_check(ip, nuid, ngid); - if (error) - goto out_gunlock_q; - } - - error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_QUOTA, 0); - if (error) - goto out_gunlock_q; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - - error = inode_setattr(inode, attr); - gfs2_assert_warn(sdp, !error); - gfs2_inode_attr_out(ip); - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { - gfs2_quota_change(ip, -ip->i_di.di_blocks, ouid, ogid); - gfs2_quota_change(ip, ip->i_di.di_blocks, nuid, ngid); - } - -out_end_trans: - gfs2_trans_end(sdp); -out_gunlock_q: - gfs2_quota_unlock(ip); -out_alloc: - gfs2_alloc_put(ip); - return error; -} - -/** - * gfs2_setattr - Change attributes on an inode - * @dentry: The dentry which is changing - * @attr: The structure describing the change - * - * The VFS layer wants to change one or more of an inodes attributes. Write - * that change out to disk. - * - * Returns: errno - */ - -static int gfs2_setattr(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder i_gh; - int error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); - if (error) - return error; - - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto out; - - error = inode_change_ok(inode, attr); - if (error) - goto out; - - if (attr->ia_valid & ATTR_SIZE) - error = setattr_size(inode, attr); - else if (attr->ia_valid & (ATTR_UID | ATTR_GID)) - error = setattr_chown(inode, attr); - else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) - error = gfs2_acl_chmod(ip, attr); - else - error = gfs2_setattr_simple(ip, attr); - -out: - gfs2_glock_dq_uninit(&i_gh); - if (!error) - mark_inode_dirty(inode); - return error; -} - -/** - * gfs2_getattr - Read out an inode's attributes - * @mnt: The vfsmount the inode is being accessed from - * @dentry: The dentry to stat - * @stat: The inode's stats - * - * Returns: errno - */ - -static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); - if (!error) { - generic_fillattr(inode, stat); - gfs2_glock_dq_uninit(&gh); - } - - return error; -} - -static int gfs2_setxattr(struct dentry *dentry, const char *name, - const void *data, size_t size, int flags) -{ - struct inode *inode = dentry->d_inode; - struct gfs2_ea_request er; - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - er.er_type = gfs2_ea_name2type(name, &er.er_name); - if (er.er_type == GFS2_EATYPE_UNUSED) - return -EOPNOTSUPP; - er.er_data = (char *)data; - er.er_name_len = strlen(er.er_name); - er.er_data_len = size; - er.er_flags = flags; - - gfs2_assert_warn(GFS2_SB(inode), !(er.er_flags & GFS2_ERF_MODE)); - - return gfs2_ea_set(GFS2_I(inode), &er); -} - -static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name, - void *data, size_t size) -{ - struct gfs2_ea_request er; - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - er.er_type = gfs2_ea_name2type(name, &er.er_name); - if (er.er_type == GFS2_EATYPE_UNUSED) - return -EOPNOTSUPP; - er.er_data = data; - er.er_name_len = strlen(er.er_name); - er.er_data_len = size; - - return gfs2_ea_get(GFS2_I(dentry->d_inode), &er); -} - -static ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size) -{ - struct gfs2_ea_request er; - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - er.er_data = (size) ? buffer : NULL; - er.er_data_len = size; - - return gfs2_ea_list(GFS2_I(dentry->d_inode), &er); -} - -static int gfs2_removexattr(struct dentry *dentry, const char *name) -{ - struct gfs2_ea_request er; - - memset(&er, 0, sizeof(struct gfs2_ea_request)); - er.er_type = gfs2_ea_name2type(name, &er.er_name); - if (er.er_type == GFS2_EATYPE_UNUSED) - return -EOPNOTSUPP; - er.er_name_len = strlen(er.er_name); - - return gfs2_ea_remove(GFS2_I(dentry->d_inode), &er); -} - -struct inode_operations gfs2_file_iops = { - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, -}; - -struct inode_operations gfs2_dev_iops = { - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, -}; - -struct inode_operations gfs2_dir_iops = { - .create = gfs2_create, - .lookup = gfs2_lookup, - .link = gfs2_link, - .unlink = gfs2_unlink, - .symlink = gfs2_symlink, - .mkdir = gfs2_mkdir, - .rmdir = gfs2_rmdir, - .mknod = gfs2_mknod, - .rename = gfs2_rename, - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, -}; - -struct inode_operations gfs2_symlink_iops = { - .readlink = gfs2_readlink, - .follow_link = gfs2_follow_link, - .permission = gfs2_permission, - .setattr = gfs2_setattr, - .getattr = gfs2_getattr, - .setxattr = gfs2_setxattr, - .getxattr = gfs2_getxattr, - .listxattr = gfs2_listxattr, - .removexattr = gfs2_removexattr, -}; - diff --git a/trunk/fs/gfs2/ops_inode.h b/trunk/fs/gfs2/ops_inode.h deleted file mode 100644 index b15acb4fd34c..000000000000 --- a/trunk/fs/gfs2/ops_inode.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_INODE_DOT_H__ -#define __OPS_INODE_DOT_H__ - -#include - -extern struct inode_operations gfs2_file_iops; -extern struct inode_operations gfs2_dir_iops; -extern struct inode_operations gfs2_symlink_iops; -extern struct inode_operations gfs2_dev_iops; - -#endif /* __OPS_INODE_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_super.c b/trunk/fs/gfs2/ops_super.c deleted file mode 100644 index 06f06f7773d0..000000000000 --- a/trunk/fs/gfs2/ops_super.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "inode.h" -#include "lm.h" -#include "log.h" -#include "mount.h" -#include "ops_super.h" -#include "quota.h" -#include "recovery.h" -#include "rgrp.h" -#include "super.h" -#include "sys.h" -#include "util.h" -#include "trans.h" -#include "dir.h" -#include "eattr.h" -#include "bmap.h" - -/** - * gfs2_write_inode - Make sure the inode is stable on the disk - * @inode: The inode - * @sync: synchronous write flag - * - * Returns: errno - */ - -static int gfs2_write_inode(struct inode *inode, int sync) -{ - struct gfs2_inode *ip = GFS2_I(inode); - - /* Check this is a "normal" inode */ - if (inode->i_private) { - if (current->flags & PF_MEMALLOC) - return 0; - if (sync) - gfs2_log_flush(GFS2_SB(inode), ip->i_gl); - } - - return 0; -} - -/** - * gfs2_put_super - Unmount the filesystem - * @sb: The VFS superblock - * - */ - -static void gfs2_put_super(struct super_block *sb) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - int error; - - if (!sdp) - return; - - if (!strncmp(sb->s_type->name, "gfs2meta", 8)) - return; /* Nothing to do */ - - /* Unfreeze the filesystem, if we need to */ - - mutex_lock(&sdp->sd_freeze_lock); - if (sdp->sd_freeze_count) - gfs2_glock_dq_uninit(&sdp->sd_freeze_gh); - mutex_unlock(&sdp->sd_freeze_lock); - - kthread_stop(sdp->sd_quotad_process); - kthread_stop(sdp->sd_logd_process); - kthread_stop(sdp->sd_recoverd_process); - while (sdp->sd_glockd_num--) - kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]); - kthread_stop(sdp->sd_scand_process); - - if (!(sb->s_flags & MS_RDONLY)) { - error = gfs2_make_fs_ro(sdp); - if (error) - gfs2_io_error(sdp); - } - /* At this point, we're through modifying the disk */ - - /* Release stuff */ - - iput(sdp->sd_master_dir); - iput(sdp->sd_jindex); - iput(sdp->sd_inum_inode); - iput(sdp->sd_statfs_inode); - iput(sdp->sd_rindex); - iput(sdp->sd_quota_inode); - - gfs2_glock_put(sdp->sd_rename_gl); - gfs2_glock_put(sdp->sd_trans_gl); - - if (!sdp->sd_args.ar_spectator) { - gfs2_glock_dq_uninit(&sdp->sd_journal_gh); - gfs2_glock_dq_uninit(&sdp->sd_jinode_gh); - gfs2_glock_dq_uninit(&sdp->sd_ir_gh); - gfs2_glock_dq_uninit(&sdp->sd_sc_gh); - gfs2_glock_dq_uninit(&sdp->sd_qc_gh); - iput(sdp->sd_ir_inode); - iput(sdp->sd_sc_inode); - iput(sdp->sd_qc_inode); - } - - gfs2_glock_dq_uninit(&sdp->sd_live_gh); - gfs2_clear_rgrpd(sdp); - gfs2_jindex_free(sdp); - /* Take apart glock structures and buffer lists */ - gfs2_gl_hash_clear(sdp, WAIT); - /* Unmount the locking protocol */ - gfs2_lm_unmount(sdp); - - /* At this point, we're through participating in the lockspace */ - gfs2_sys_fs_del(sdp); - kfree(sdp); -} - -/** - * gfs2_write_super - disk commit all incore transactions - * @sb: the filesystem - * - * This function is called every time sync(2) is called. - * After this exits, all dirty buffers are synced. - */ - -static void gfs2_write_super(struct super_block *sb) -{ - gfs2_log_flush(sb->s_fs_info, NULL); -} - -/** - * gfs2_write_super_lockfs - prevent further writes to the filesystem - * @sb: the VFS structure for the filesystem - * - */ - -static void gfs2_write_super_lockfs(struct super_block *sb) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - int error; - - for (;;) { - error = gfs2_freeze_fs(sdp); - if (!error) - break; - - switch (error) { - case -EBUSY: - fs_err(sdp, "waiting for recovery before freeze\n"); - break; - - default: - fs_err(sdp, "error freezing FS: %d\n", error); - break; - } - - fs_err(sdp, "retrying...\n"); - msleep(1000); - } -} - -/** - * gfs2_unlockfs - reallow writes to the filesystem - * @sb: the VFS structure for the filesystem - * - */ - -static void gfs2_unlockfs(struct super_block *sb) -{ - gfs2_unfreeze_fs(sb->s_fs_info); -} - -/** - * gfs2_statfs - Gather and return stats about the filesystem - * @sb: The superblock - * @statfsbuf: The buffer - * - * Returns: 0 on success or error code - */ - -static int gfs2_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_inode->i_sb; - struct gfs2_sbd *sdp = sb->s_fs_info; - struct gfs2_statfs_change sc; - int error; - - if (gfs2_tune_get(sdp, gt_statfs_slow)) - error = gfs2_statfs_slow(sdp, &sc); - else - error = gfs2_statfs_i(sdp, &sc); - - if (error) - return error; - - buf->f_type = GFS2_MAGIC; - buf->f_bsize = sdp->sd_sb.sb_bsize; - buf->f_blocks = sc.sc_total; - buf->f_bfree = sc.sc_free; - buf->f_bavail = sc.sc_free; - buf->f_files = sc.sc_dinodes + sc.sc_free; - buf->f_ffree = sc.sc_free; - buf->f_namelen = GFS2_FNAMESIZE; - - return 0; -} - -/** - * gfs2_remount_fs - called when the FS is remounted - * @sb: the filesystem - * @flags: the remount flags - * @data: extra data passed in (not used right now) - * - * Returns: errno - */ - -static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - int error; - - error = gfs2_mount_args(sdp, data, 1); - if (error) - return error; - - if (sdp->sd_args.ar_spectator) - *flags |= MS_RDONLY; - else { - if (*flags & MS_RDONLY) { - if (!(sb->s_flags & MS_RDONLY)) - error = gfs2_make_fs_ro(sdp); - } else if (!(*flags & MS_RDONLY) && - (sb->s_flags & MS_RDONLY)) { - error = gfs2_make_fs_rw(sdp); - } - } - - if (*flags & (MS_NOATIME | MS_NODIRATIME)) - set_bit(SDF_NOATIME, &sdp->sd_flags); - else - clear_bit(SDF_NOATIME, &sdp->sd_flags); - - /* Don't let the VFS update atimes. GFS2 handles this itself. */ - *flags |= MS_NOATIME | MS_NODIRATIME; - - return error; -} - -/** - * gfs2_clear_inode - Deallocate an inode when VFS is done with it - * @inode: The VFS inode - * - */ - -static void gfs2_clear_inode(struct inode *inode) -{ - /* This tells us its a "real" inode and not one which only - * serves to contain an address space (see rgrp.c, meta_io.c) - * which therefore doesn't have its own glocks. - */ - if (inode->i_private) { - struct gfs2_inode *ip = GFS2_I(inode); - gfs2_glock_inode_squish(inode); - gfs2_assert(inode->i_sb->s_fs_info, ip->i_gl->gl_state == LM_ST_UNLOCKED); - ip->i_gl->gl_object = NULL; - gfs2_glock_schedule_for_reclaim(ip->i_gl); - gfs2_glock_put(ip->i_gl); - ip->i_gl = NULL; - if (ip->i_iopen_gh.gh_gl) - gfs2_glock_dq_uninit(&ip->i_iopen_gh); - } -} - -/** - * gfs2_show_options - Show mount options for /proc/mounts - * @s: seq_file structure - * @mnt: vfsmount - * - * Returns: 0 on success or error code - */ - -static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) -{ - struct gfs2_sbd *sdp = mnt->mnt_sb->s_fs_info; - struct gfs2_args *args = &sdp->sd_args; - - if (args->ar_lockproto[0]) - seq_printf(s, ",lockproto=%s", args->ar_lockproto); - if (args->ar_locktable[0]) - seq_printf(s, ",locktable=%s", args->ar_locktable); - if (args->ar_hostdata[0]) - seq_printf(s, ",hostdata=%s", args->ar_hostdata); - if (args->ar_spectator) - seq_printf(s, ",spectator"); - if (args->ar_ignore_local_fs) - seq_printf(s, ",ignore_local_fs"); - if (args->ar_localflocks) - seq_printf(s, ",localflocks"); - if (args->ar_localcaching) - seq_printf(s, ",localcaching"); - if (args->ar_debug) - seq_printf(s, ",debug"); - if (args->ar_upgrade) - seq_printf(s, ",upgrade"); - if (args->ar_num_glockd != GFS2_GLOCKD_DEFAULT) - seq_printf(s, ",num_glockd=%u", args->ar_num_glockd); - if (args->ar_posix_acl) - seq_printf(s, ",acl"); - if (args->ar_quota != GFS2_QUOTA_DEFAULT) { - char *state; - switch (args->ar_quota) { - case GFS2_QUOTA_OFF: - state = "off"; - break; - case GFS2_QUOTA_ACCOUNT: - state = "account"; - break; - case GFS2_QUOTA_ON: - state = "on"; - break; - default: - state = "unknown"; - break; - } - seq_printf(s, ",quota=%s", state); - } - if (args->ar_suiddir) - seq_printf(s, ",suiddir"); - if (args->ar_data != GFS2_DATA_DEFAULT) { - char *state; - switch (args->ar_data) { - case GFS2_DATA_WRITEBACK: - state = "writeback"; - break; - case GFS2_DATA_ORDERED: - state = "ordered"; - break; - default: - state = "unknown"; - break; - } - seq_printf(s, ",data=%s", state); - } - - return 0; -} - -/* - * We have to (at the moment) hold the inodes main lock to cover - * the gap between unlocking the shared lock on the iopen lock and - * taking the exclusive lock. I'd rather do a shared -> exclusive - * conversion on the iopen lock, but we can change that later. This - * is safe, just less efficient. - */ -static void gfs2_delete_inode(struct inode *inode) -{ - struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_holder gh; - int error; - - if (!inode->i_private) - goto out; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &gh); - if (unlikely(error)) { - gfs2_glock_dq_uninit(&ip->i_iopen_gh); - goto out; - } - - gfs2_glock_dq(&ip->i_iopen_gh); - gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh); - error = gfs2_glock_nq(&ip->i_iopen_gh); - if (error) - goto out_uninit; - - if (S_ISDIR(ip->i_di.di_mode) && - (ip->i_di.di_flags & GFS2_DIF_EXHASH)) { - error = gfs2_dir_exhash_dealloc(ip); - if (error) - goto out_unlock; - } - - if (ip->i_di.di_eattr) { - error = gfs2_ea_dealloc(ip); - if (error) - goto out_unlock; - } - - if (!gfs2_is_stuffed(ip)) { - error = gfs2_file_dealloc(ip); - if (error) - goto out_unlock; - } - - error = gfs2_dinode_dealloc(ip); - -out_unlock: - gfs2_glock_dq(&ip->i_iopen_gh); -out_uninit: - gfs2_holder_uninit(&ip->i_iopen_gh); - gfs2_glock_dq_uninit(&gh); - if (error) - fs_warn(sdp, "gfs2_delete_inode: %d\n", error); -out: - truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); -} - - - -static struct inode *gfs2_alloc_inode(struct super_block *sb) -{ - struct gfs2_sbd *sdp = sb->s_fs_info; - struct gfs2_inode *ip; - - ip = kmem_cache_alloc(gfs2_inode_cachep, GFP_KERNEL); - if (ip) { - ip->i_flags = 0; - ip->i_gl = NULL; - ip->i_greedy = gfs2_tune_get(sdp, gt_greedy_default); - ip->i_last_pfault = jiffies; - } - return &ip->i_inode; -} - -static void gfs2_destroy_inode(struct inode *inode) -{ - kmem_cache_free(gfs2_inode_cachep, inode); -} - -struct super_operations gfs2_super_ops = { - .alloc_inode = gfs2_alloc_inode, - .destroy_inode = gfs2_destroy_inode, - .write_inode = gfs2_write_inode, - .delete_inode = gfs2_delete_inode, - .put_super = gfs2_put_super, - .write_super = gfs2_write_super, - .write_super_lockfs = gfs2_write_super_lockfs, - .unlockfs = gfs2_unlockfs, - .statfs = gfs2_statfs, - .remount_fs = gfs2_remount_fs, - .clear_inode = gfs2_clear_inode, - .show_options = gfs2_show_options, -}; - diff --git a/trunk/fs/gfs2/ops_super.h b/trunk/fs/gfs2/ops_super.h deleted file mode 100644 index 9de73f042f78..000000000000 --- a/trunk/fs/gfs2/ops_super.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_SUPER_DOT_H__ -#define __OPS_SUPER_DOT_H__ - -#include - -extern struct super_operations gfs2_super_ops; - -#endif /* __OPS_SUPER_DOT_H__ */ diff --git a/trunk/fs/gfs2/ops_vm.c b/trunk/fs/gfs2/ops_vm.c deleted file mode 100644 index 5453d2947ab3..000000000000 --- a/trunk/fs/gfs2/ops_vm.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "inode.h" -#include "ops_vm.h" -#include "quota.h" -#include "rgrp.h" -#include "trans.h" -#include "util.h" - -static void pfault_be_greedy(struct gfs2_inode *ip) -{ - unsigned int time; - - spin_lock(&ip->i_spin); - time = ip->i_greedy; - ip->i_last_pfault = jiffies; - spin_unlock(&ip->i_spin); - - igrab(&ip->i_inode); - if (gfs2_glock_be_greedy(ip->i_gl, time)) - iput(&ip->i_inode); -} - -static struct page *gfs2_private_nopage(struct vm_area_struct *area, - unsigned long address, int *type) -{ - struct gfs2_inode *ip = GFS2_I(area->vm_file->f_mapping->host); - struct page *result; - - set_bit(GIF_PAGED, &ip->i_flags); - - result = filemap_nopage(area, address, type); - - if (result && result != NOPAGE_OOM) - pfault_be_greedy(ip); - - return result; -} - -static int alloc_page_backing(struct gfs2_inode *ip, struct page *page) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - unsigned long index = page->index; - u64 lblock = index << (PAGE_CACHE_SHIFT - - sdp->sd_sb.sb_bsize_shift); - unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift; - struct gfs2_alloc *al; - unsigned int data_blocks, ind_blocks; - unsigned int x; - int error; - - al = gfs2_alloc_get(ip); - - error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); - if (error) - goto out; - - error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); - if (error) - goto out_gunlock_q; - - gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); - - al->al_requested = data_blocks + ind_blocks; - - error = gfs2_inplace_reserve(ip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, al->al_rgd->rd_ri.ri_length + - ind_blocks + RES_DINODE + - RES_STATFS + RES_QUOTA, 0); - if (error) - goto out_ipres; - - if (gfs2_is_stuffed(ip)) { - error = gfs2_unstuff_dinode(ip, NULL); - if (error) - goto out_trans; - } - - for (x = 0; x < blocks; ) { - u64 dblock; - unsigned int extlen; - int new = 1; - - error = gfs2_extent_map(&ip->i_inode, lblock, &new, &dblock, &extlen); - if (error) - goto out_trans; - - lblock += extlen; - x += extlen; - } - - gfs2_assert_warn(sdp, al->al_alloced); - -out_trans: - gfs2_trans_end(sdp); -out_ipres: - gfs2_inplace_release(ip); -out_gunlock_q: - gfs2_quota_unlock(ip); -out: - gfs2_alloc_put(ip); - return error; -} - -static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area, - unsigned long address, int *type) -{ - struct file *file = area->vm_file; - struct gfs2_file *gf = file->private_data; - struct gfs2_inode *ip = GFS2_I(file->f_mapping->host); - struct gfs2_holder i_gh; - struct page *result = NULL; - unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + - area->vm_pgoff; - int alloc_required; - int error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); - if (error) - return NULL; - - set_bit(GIF_PAGED, &ip->i_flags); - set_bit(GIF_SW_PAGED, &ip->i_flags); - - error = gfs2_write_alloc_required(ip, (u64)index << PAGE_CACHE_SHIFT, - PAGE_CACHE_SIZE, &alloc_required); - if (error) - goto out; - - set_bit(GFF_EXLOCK, &gf->f_flags); - result = filemap_nopage(area, address, type); - clear_bit(GFF_EXLOCK, &gf->f_flags); - if (!result || result == NOPAGE_OOM) - goto out; - - if (alloc_required) { - error = alloc_page_backing(ip, result); - if (error) { - page_cache_release(result); - result = NULL; - goto out; - } - set_page_dirty(result); - } - - pfault_be_greedy(ip); -out: - gfs2_glock_dq_uninit(&i_gh); - - return result; -} - -struct vm_operations_struct gfs2_vm_ops_private = { - .nopage = gfs2_private_nopage, -}; - -struct vm_operations_struct gfs2_vm_ops_sharewrite = { - .nopage = gfs2_sharewrite_nopage, -}; - diff --git a/trunk/fs/gfs2/ops_vm.h b/trunk/fs/gfs2/ops_vm.h deleted file mode 100644 index 4ae8f43ed5e3..000000000000 --- a/trunk/fs/gfs2/ops_vm.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __OPS_VM_DOT_H__ -#define __OPS_VM_DOT_H__ - -#include - -extern struct vm_operations_struct gfs2_vm_ops_private; -extern struct vm_operations_struct gfs2_vm_ops_sharewrite; - -#endif /* __OPS_VM_DOT_H__ */ diff --git a/trunk/fs/gfs2/quota.c b/trunk/fs/gfs2/quota.c deleted file mode 100644 index c69b94a55588..000000000000 --- a/trunk/fs/gfs2/quota.c +++ /dev/null @@ -1,1227 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -/* - * Quota change tags are associated with each transaction that allocates or - * deallocates space. Those changes are accumulated locally to each node (in a - * per-node file) and then are periodically synced to the quota file. This - * avoids the bottleneck of constantly touching the quota file, but introduces - * fuzziness in the current usage value of IDs that are being used on different - * nodes in the cluster simultaneously. So, it is possible for a user on - * multiple nodes to overrun their quota, but that overrun is controlable. - * Since quota tags are part of transactions, there is no need to a quota check - * program to be run on node crashes or anything like that. - * - * There are couple of knobs that let the administrator manage the quota - * fuzziness. "quota_quantum" sets the maximum time a quota change can be - * sitting on one node before being synced to the quota file. (The default is - * 60 seconds.) Another knob, "quota_scale" controls how quickly the frequency - * of quota file syncs increases as the user moves closer to their limit. The - * more frequent the syncs, the more accurate the quota enforcement, but that - * means that there is more contention between the nodes for the quota file. - * The default value is one. This sets the maximum theoretical quota overrun - * (with infinite node with infinite bandwidth) to twice the user's limit. (In - * practice, the maximum overrun you see should be much less.) A "quota_scale" - * number greater than one makes quota syncs more frequent and reduces the - * maximum overrun. Numbers less than one (but greater than zero) make quota - * syncs less frequent. - * - * GFS quotas also use per-ID Lock Value Blocks (LVBs) to cache the contents of - * the quota file, so it is not being constantly read. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "glops.h" -#include "log.h" -#include "meta_io.h" -#include "quota.h" -#include "rgrp.h" -#include "super.h" -#include "trans.h" -#include "inode.h" -#include "ops_file.h" -#include "ops_address.h" -#include "util.h" - -#define QUOTA_USER 1 -#define QUOTA_GROUP 0 - -static u64 qd2offset(struct gfs2_quota_data *qd) -{ - u64 offset; - - offset = 2 * (u64)qd->qd_id + !test_bit(QDF_USER, &qd->qd_flags); - offset *= sizeof(struct gfs2_quota); - - return offset; -} - -static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, - struct gfs2_quota_data **qdp) -{ - struct gfs2_quota_data *qd; - int error; - - qd = kzalloc(sizeof(struct gfs2_quota_data), GFP_KERNEL); - if (!qd) - return -ENOMEM; - - qd->qd_count = 1; - qd->qd_id = id; - if (user) - set_bit(QDF_USER, &qd->qd_flags); - qd->qd_slot = -1; - - error = gfs2_glock_get(sdp, 2 * (u64)id + !user, - &gfs2_quota_glops, CREATE, &qd->qd_gl); - if (error) - goto fail; - - error = gfs2_lvb_hold(qd->qd_gl); - gfs2_glock_put(qd->qd_gl); - if (error) - goto fail; - - *qdp = qd; - - return 0; - -fail: - kfree(qd); - return error; -} - -static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, int create, - struct gfs2_quota_data **qdp) -{ - struct gfs2_quota_data *qd = NULL, *new_qd = NULL; - int error, found; - - *qdp = NULL; - - for (;;) { - found = 0; - spin_lock(&sdp->sd_quota_spin); - list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { - if (qd->qd_id == id && - !test_bit(QDF_USER, &qd->qd_flags) == !user) { - qd->qd_count++; - found = 1; - break; - } - } - - if (!found) - qd = NULL; - - if (!qd && new_qd) { - qd = new_qd; - list_add(&qd->qd_list, &sdp->sd_quota_list); - atomic_inc(&sdp->sd_quota_count); - new_qd = NULL; - } - - spin_unlock(&sdp->sd_quota_spin); - - if (qd || !create) { - if (new_qd) { - gfs2_lvb_unhold(new_qd->qd_gl); - kfree(new_qd); - } - *qdp = qd; - return 0; - } - - error = qd_alloc(sdp, user, id, &new_qd); - if (error) - return error; - } -} - -static void qd_hold(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - - spin_lock(&sdp->sd_quota_spin); - gfs2_assert(sdp, qd->qd_count); - qd->qd_count++; - spin_unlock(&sdp->sd_quota_spin); -} - -static void qd_put(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - spin_lock(&sdp->sd_quota_spin); - gfs2_assert(sdp, qd->qd_count); - if (!--qd->qd_count) - qd->qd_last_touched = jiffies; - spin_unlock(&sdp->sd_quota_spin); -} - -static int slot_get(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - unsigned int c, o = 0, b; - unsigned char byte = 0; - - spin_lock(&sdp->sd_quota_spin); - - if (qd->qd_slot_count++) { - spin_unlock(&sdp->sd_quota_spin); - return 0; - } - - for (c = 0; c < sdp->sd_quota_chunks; c++) - for (o = 0; o < PAGE_SIZE; o++) { - byte = sdp->sd_quota_bitmap[c][o]; - if (byte != 0xFF) - goto found; - } - - goto fail; - -found: - for (b = 0; b < 8; b++) - if (!(byte & (1 << b))) - break; - qd->qd_slot = c * (8 * PAGE_SIZE) + o * 8 + b; - - if (qd->qd_slot >= sdp->sd_quota_slots) - goto fail; - - sdp->sd_quota_bitmap[c][o] |= 1 << b; - - spin_unlock(&sdp->sd_quota_spin); - - return 0; - -fail: - qd->qd_slot_count--; - spin_unlock(&sdp->sd_quota_spin); - return -ENOSPC; -} - -static void slot_hold(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - - spin_lock(&sdp->sd_quota_spin); - gfs2_assert(sdp, qd->qd_slot_count); - qd->qd_slot_count++; - spin_unlock(&sdp->sd_quota_spin); -} - -static void slot_put(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - - spin_lock(&sdp->sd_quota_spin); - gfs2_assert(sdp, qd->qd_slot_count); - if (!--qd->qd_slot_count) { - gfs2_icbit_munge(sdp, sdp->sd_quota_bitmap, qd->qd_slot, 0); - qd->qd_slot = -1; - } - spin_unlock(&sdp->sd_quota_spin); -} - -static int bh_get(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); - unsigned int block, offset; - struct buffer_head *bh; - int error; - struct buffer_head bh_map; - - mutex_lock(&sdp->sd_quota_mutex); - - if (qd->qd_bh_count++) { - mutex_unlock(&sdp->sd_quota_mutex); - return 0; - } - - block = qd->qd_slot / sdp->sd_qc_per_block; - offset = qd->qd_slot % sdp->sd_qc_per_block;; - - error = gfs2_block_map(&ip->i_inode, block, 0, &bh_map, 1); - if (error) - goto fail; - error = gfs2_meta_read(ip->i_gl, bh_map.b_blocknr, DIO_WAIT, &bh); - if (error) - goto fail; - error = -EIO; - if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_QC)) - goto fail_brelse; - - qd->qd_bh = bh; - qd->qd_bh_qc = (struct gfs2_quota_change *) - (bh->b_data + sizeof(struct gfs2_meta_header) + - offset * sizeof(struct gfs2_quota_change)); - - mutex_lock(&sdp->sd_quota_mutex); - - return 0; - -fail_brelse: - brelse(bh); -fail: - qd->qd_bh_count--; - mutex_unlock(&sdp->sd_quota_mutex); - return error; -} - -static void bh_put(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - - mutex_lock(&sdp->sd_quota_mutex); - gfs2_assert(sdp, qd->qd_bh_count); - if (!--qd->qd_bh_count) { - brelse(qd->qd_bh); - qd->qd_bh = NULL; - qd->qd_bh_qc = NULL; - } - mutex_unlock(&sdp->sd_quota_mutex); -} - -static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) -{ - struct gfs2_quota_data *qd = NULL; - int error; - int found = 0; - - *qdp = NULL; - - if (sdp->sd_vfs->s_flags & MS_RDONLY) - return 0; - - spin_lock(&sdp->sd_quota_spin); - - list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { - if (test_bit(QDF_LOCKED, &qd->qd_flags) || - !test_bit(QDF_CHANGE, &qd->qd_flags) || - qd->qd_sync_gen >= sdp->sd_quota_sync_gen) - continue; - - list_move_tail(&qd->qd_list, &sdp->sd_quota_list); - - set_bit(QDF_LOCKED, &qd->qd_flags); - gfs2_assert_warn(sdp, qd->qd_count); - qd->qd_count++; - qd->qd_change_sync = qd->qd_change; - gfs2_assert_warn(sdp, qd->qd_slot_count); - qd->qd_slot_count++; - found = 1; - - break; - } - - if (!found) - qd = NULL; - - spin_unlock(&sdp->sd_quota_spin); - - if (qd) { - gfs2_assert_warn(sdp, qd->qd_change_sync); - error = bh_get(qd); - if (error) { - clear_bit(QDF_LOCKED, &qd->qd_flags); - slot_put(qd); - qd_put(qd); - return error; - } - } - - *qdp = qd; - - return 0; -} - -static int qd_trylock(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - - if (sdp->sd_vfs->s_flags & MS_RDONLY) - return 0; - - spin_lock(&sdp->sd_quota_spin); - - if (test_bit(QDF_LOCKED, &qd->qd_flags) || - !test_bit(QDF_CHANGE, &qd->qd_flags)) { - spin_unlock(&sdp->sd_quota_spin); - return 0; - } - - list_move_tail(&qd->qd_list, &sdp->sd_quota_list); - - set_bit(QDF_LOCKED, &qd->qd_flags); - gfs2_assert_warn(sdp, qd->qd_count); - qd->qd_count++; - qd->qd_change_sync = qd->qd_change; - gfs2_assert_warn(sdp, qd->qd_slot_count); - qd->qd_slot_count++; - - spin_unlock(&sdp->sd_quota_spin); - - gfs2_assert_warn(sdp, qd->qd_change_sync); - if (bh_get(qd)) { - clear_bit(QDF_LOCKED, &qd->qd_flags); - slot_put(qd); - qd_put(qd); - return 0; - } - - return 1; -} - -static void qd_unlock(struct gfs2_quota_data *qd) -{ - gfs2_assert_warn(qd->qd_gl->gl_sbd, - test_bit(QDF_LOCKED, &qd->qd_flags)); - clear_bit(QDF_LOCKED, &qd->qd_flags); - bh_put(qd); - slot_put(qd); - qd_put(qd); -} - -static int qdsb_get(struct gfs2_sbd *sdp, int user, u32 id, int create, - struct gfs2_quota_data **qdp) -{ - int error; - - error = qd_get(sdp, user, id, create, qdp); - if (error) - return error; - - error = slot_get(*qdp); - if (error) - goto fail; - - error = bh_get(*qdp); - if (error) - goto fail_slot; - - return 0; - -fail_slot: - slot_put(*qdp); -fail: - qd_put(*qdp); - return error; -} - -static void qdsb_put(struct gfs2_quota_data *qd) -{ - bh_put(qd); - slot_put(qd); - qd_put(qd); -} - -int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_quota_data **qd = al->al_qd; - int error; - - if (gfs2_assert_warn(sdp, !al->al_qd_num) || - gfs2_assert_warn(sdp, !test_bit(GIF_QD_LOCKED, &ip->i_flags))) - return -EIO; - - if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) - return 0; - - error = qdsb_get(sdp, QUOTA_USER, ip->i_di.di_uid, CREATE, qd); - if (error) - goto out; - al->al_qd_num++; - qd++; - - error = qdsb_get(sdp, QUOTA_GROUP, ip->i_di.di_gid, CREATE, qd); - if (error) - goto out; - al->al_qd_num++; - qd++; - - if (uid != NO_QUOTA_CHANGE && uid != ip->i_di.di_uid) { - error = qdsb_get(sdp, QUOTA_USER, uid, CREATE, qd); - if (error) - goto out; - al->al_qd_num++; - qd++; - } - - if (gid != NO_QUOTA_CHANGE && gid != ip->i_di.di_gid) { - error = qdsb_get(sdp, QUOTA_GROUP, gid, CREATE, qd); - if (error) - goto out; - al->al_qd_num++; - qd++; - } - -out: - if (error) - gfs2_quota_unhold(ip); - return error; -} - -void gfs2_quota_unhold(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - unsigned int x; - - gfs2_assert_warn(sdp, !test_bit(GIF_QD_LOCKED, &ip->i_flags)); - - for (x = 0; x < al->al_qd_num; x++) { - qdsb_put(al->al_qd[x]); - al->al_qd[x] = NULL; - } - al->al_qd_num = 0; -} - -static int sort_qd(const void *a, const void *b) -{ - const struct gfs2_quota_data *qd_a = *(const struct gfs2_quota_data **)a; - const struct gfs2_quota_data *qd_b = *(const struct gfs2_quota_data **)b; - - if (!test_bit(QDF_USER, &qd_a->qd_flags) != - !test_bit(QDF_USER, &qd_b->qd_flags)) { - if (test_bit(QDF_USER, &qd_a->qd_flags)) - return -1; - else - return 1; - } - if (qd_a->qd_id < qd_b->qd_id) - return -1; - if (qd_a->qd_id > qd_b->qd_id) - return 1; - - return 0; -} - -static void do_qc(struct gfs2_quota_data *qd, s64 change) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); - struct gfs2_quota_change *qc = qd->qd_bh_qc; - s64 x; - - mutex_lock(&sdp->sd_quota_mutex); - gfs2_trans_add_bh(ip->i_gl, qd->qd_bh, 1); - - if (!test_bit(QDF_CHANGE, &qd->qd_flags)) { - qc->qc_change = 0; - qc->qc_flags = 0; - if (test_bit(QDF_USER, &qd->qd_flags)) - qc->qc_flags = cpu_to_be32(GFS2_QCF_USER); - qc->qc_id = cpu_to_be32(qd->qd_id); - } - - x = qc->qc_change; - x = be64_to_cpu(x) + change; - qc->qc_change = cpu_to_be64(x); - - spin_lock(&sdp->sd_quota_spin); - qd->qd_change = x; - spin_unlock(&sdp->sd_quota_spin); - - if (!x) { - gfs2_assert_warn(sdp, test_bit(QDF_CHANGE, &qd->qd_flags)); - clear_bit(QDF_CHANGE, &qd->qd_flags); - qc->qc_flags = 0; - qc->qc_id = 0; - slot_put(qd); - qd_put(qd); - } else if (!test_and_set_bit(QDF_CHANGE, &qd->qd_flags)) { - qd_hold(qd); - slot_hold(qd); - } - - mutex_unlock(&sdp->sd_quota_mutex); -} - -/** - * gfs2_adjust_quota - * - * This function was mostly borrowed from gfs2_block_truncate_page which was - * in turn mostly borrowed from ext3 - */ -static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, - s64 change, struct gfs2_quota_data *qd) -{ - struct inode *inode = &ip->i_inode; - struct address_space *mapping = inode->i_mapping; - unsigned long index = loc >> PAGE_CACHE_SHIFT; - unsigned offset = loc & (PAGE_CACHE_SHIFT - 1); - unsigned blocksize, iblock, pos; - struct buffer_head *bh; - struct page *page; - void *kaddr; - __be64 *ptr; - s64 value; - int err = -EIO; - - page = grab_cache_page(mapping, index); - if (!page) - return -ENOMEM; - - blocksize = inode->i_sb->s_blocksize; - iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - - if (!page_has_buffers(page)) - create_empty_buffers(page, blocksize, 0); - - bh = page_buffers(page); - pos = blocksize; - while (offset >= pos) { - bh = bh->b_this_page; - iblock++; - pos += blocksize; - } - - if (!buffer_mapped(bh)) { - gfs2_get_block(inode, iblock, bh, 1); - if (!buffer_mapped(bh)) - goto unlock; - } - - if (PageUptodate(page)) - set_buffer_uptodate(bh); - - if (!buffer_uptodate(bh)) { - ll_rw_block(READ_META, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - goto unlock; - } - - gfs2_trans_add_bh(ip->i_gl, bh, 0); - - kaddr = kmap_atomic(page, KM_USER0); - ptr = kaddr + offset; - value = (s64)be64_to_cpu(*ptr) + change; - *ptr = cpu_to_be64(value); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); - err = 0; - qd->qd_qb.qb_magic = cpu_to_be32(GFS2_MAGIC); - qd->qd_qb.qb_value = cpu_to_be64(value); -unlock: - unlock_page(page); - page_cache_release(page); - return err; -} - -static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) -{ - struct gfs2_sbd *sdp = (*qda)->qd_gl->gl_sbd; - struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); - unsigned int data_blocks, ind_blocks; - struct gfs2_holder *ghs, i_gh; - unsigned int qx, x; - struct gfs2_quota_data *qd; - loff_t offset; - unsigned int nalloc = 0; - struct gfs2_alloc *al = NULL; - int error; - - gfs2_write_calc_reserv(ip, sizeof(struct gfs2_quota), - &data_blocks, &ind_blocks); - - ghs = kcalloc(num_qd, sizeof(struct gfs2_holder), GFP_KERNEL); - if (!ghs) - return -ENOMEM; - - sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL); - for (qx = 0; qx < num_qd; qx++) { - error = gfs2_glock_nq_init(qda[qx]->qd_gl, - LM_ST_EXCLUSIVE, - GL_NOCACHE, &ghs[qx]); - if (error) - goto out; - } - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); - if (error) - goto out; - - for (x = 0; x < num_qd; x++) { - int alloc_required; - - offset = qd2offset(qda[x]); - error = gfs2_write_alloc_required(ip, offset, - sizeof(struct gfs2_quota), - &alloc_required); - if (error) - goto out_gunlock; - if (alloc_required) - nalloc++; - } - - if (nalloc) { - al = gfs2_alloc_get(ip); - - al->al_requested = nalloc * (data_blocks + ind_blocks); - - error = gfs2_inplace_reserve(ip); - if (error) - goto out_alloc; - - error = gfs2_trans_begin(sdp, - al->al_rgd->rd_ri.ri_length + - num_qd * data_blocks + - nalloc * ind_blocks + - RES_DINODE + num_qd + - RES_STATFS, 0); - if (error) - goto out_ipres; - } else { - error = gfs2_trans_begin(sdp, - num_qd * data_blocks + - RES_DINODE + num_qd, 0); - if (error) - goto out_gunlock; - } - - for (x = 0; x < num_qd; x++) { - qd = qda[x]; - offset = qd2offset(qd); - error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync, - (struct gfs2_quota_data *) - qd->qd_gl->gl_lvb); - if (error) - goto out_end_trans; - - do_qc(qd, -qd->qd_change_sync); - } - - error = 0; - -out_end_trans: - gfs2_trans_end(sdp); -out_ipres: - if (nalloc) - gfs2_inplace_release(ip); -out_alloc: - if (nalloc) - gfs2_alloc_put(ip); -out_gunlock: - gfs2_glock_dq_uninit(&i_gh); -out: - while (qx--) - gfs2_glock_dq_uninit(&ghs[qx]); - kfree(ghs); - gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl); - return error; -} - -static int do_glock(struct gfs2_quota_data *qd, int force_refresh, - struct gfs2_holder *q_gh) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); - struct gfs2_holder i_gh; - struct gfs2_quota q; - char buf[sizeof(struct gfs2_quota)]; - struct file_ra_state ra_state; - int error; - struct gfs2_quota_lvb *qlvb; - - file_ra_state_init(&ra_state, sdp->sd_quota_inode->i_mapping); -restart: - error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh); - if (error) - return error; - - qd->qd_qb = *(struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb; - - if (force_refresh || qd->qd_qb.qb_magic != cpu_to_be32(GFS2_MAGIC)) { - loff_t pos; - gfs2_glock_dq_uninit(q_gh); - error = gfs2_glock_nq_init(qd->qd_gl, - LM_ST_EXCLUSIVE, GL_NOCACHE, - q_gh); - if (error) - return error; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh); - if (error) - goto fail; - - memset(buf, 0, sizeof(struct gfs2_quota)); - pos = qd2offset(qd); - error = gfs2_internal_read(ip, &ra_state, buf, - &pos, sizeof(struct gfs2_quota)); - if (error < 0) - goto fail_gunlock; - - gfs2_glock_dq_uninit(&i_gh); - - - gfs2_quota_in(&q, buf); - qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb; - qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC); - qlvb->__pad = 0; - qlvb->qb_limit = cpu_to_be64(q.qu_limit); - qlvb->qb_warn = cpu_to_be64(q.qu_warn); - qlvb->qb_value = cpu_to_be64(q.qu_value); - qd->qd_qb = *qlvb; - - if (gfs2_glock_is_blocking(qd->qd_gl)) { - gfs2_glock_dq_uninit(q_gh); - force_refresh = 0; - goto restart; - } - } - - return 0; - -fail_gunlock: - gfs2_glock_dq_uninit(&i_gh); -fail: - gfs2_glock_dq_uninit(q_gh); - return error; -} - -int gfs2_quota_lock(struct gfs2_inode *ip, u32 uid, u32 gid) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - unsigned int x; - int error = 0; - - gfs2_quota_hold(ip, uid, gid); - - if (capable(CAP_SYS_RESOURCE) || - sdp->sd_args.ar_quota != GFS2_QUOTA_ON) - return 0; - - sort(al->al_qd, al->al_qd_num, sizeof(struct gfs2_quota_data *), - sort_qd, NULL); - - for (x = 0; x < al->al_qd_num; x++) { - error = do_glock(al->al_qd[x], NO_FORCE, &al->al_qd_ghs[x]); - if (error) - break; - } - - if (!error) - set_bit(GIF_QD_LOCKED, &ip->i_flags); - else { - while (x--) - gfs2_glock_dq_uninit(&al->al_qd_ghs[x]); - gfs2_quota_unhold(ip); - } - - return error; -} - -static int need_sync(struct gfs2_quota_data *qd) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - struct gfs2_tune *gt = &sdp->sd_tune; - s64 value; - unsigned int num, den; - int do_sync = 1; - - if (!qd->qd_qb.qb_limit) - return 0; - - spin_lock(&sdp->sd_quota_spin); - value = qd->qd_change; - spin_unlock(&sdp->sd_quota_spin); - - spin_lock(>->gt_spin); - num = gt->gt_quota_scale_num; - den = gt->gt_quota_scale_den; - spin_unlock(>->gt_spin); - - if (value < 0) - do_sync = 0; - else if ((s64)be64_to_cpu(qd->qd_qb.qb_value) >= - (s64)be64_to_cpu(qd->qd_qb.qb_limit)) - do_sync = 0; - else { - value *= gfs2_jindex_size(sdp) * num; - do_div(value, den); - value += (s64)be64_to_cpu(qd->qd_qb.qb_value); - if (value < (s64)be64_to_cpu(qd->qd_qb.qb_limit)) - do_sync = 0; - } - - return do_sync; -} - -void gfs2_quota_unlock(struct gfs2_inode *ip) -{ - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_quota_data *qda[4]; - unsigned int count = 0; - unsigned int x; - - if (!test_and_clear_bit(GIF_QD_LOCKED, &ip->i_flags)) - goto out; - - for (x = 0; x < al->al_qd_num; x++) { - struct gfs2_quota_data *qd; - int sync; - - qd = al->al_qd[x]; - sync = need_sync(qd); - - gfs2_glock_dq_uninit(&al->al_qd_ghs[x]); - - if (sync && qd_trylock(qd)) - qda[count++] = qd; - } - - if (count) { - do_sync(count, qda); - for (x = 0; x < count; x++) - qd_unlock(qda[x]); - } - -out: - gfs2_quota_unhold(ip); -} - -#define MAX_LINE 256 - -static int print_message(struct gfs2_quota_data *qd, char *type) -{ - struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd; - - printk(KERN_INFO "GFS2: fsid=%s: quota %s for %s %u\r\n", - sdp->sd_fsname, type, - (test_bit(QDF_USER, &qd->qd_flags)) ? "user" : "group", - qd->qd_id); - - return 0; -} - -int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_quota_data *qd; - s64 value; - unsigned int x; - int error = 0; - - if (!test_bit(GIF_QD_LOCKED, &ip->i_flags)) - return 0; - - if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) - return 0; - - for (x = 0; x < al->al_qd_num; x++) { - qd = al->al_qd[x]; - - if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) || - (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags)))) - continue; - - value = (s64)be64_to_cpu(qd->qd_qb.qb_value); - spin_lock(&sdp->sd_quota_spin); - value += qd->qd_change; - spin_unlock(&sdp->sd_quota_spin); - - if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) { - print_message(qd, "exceeded"); - error = -EDQUOT; - break; - } else if (be64_to_cpu(qd->qd_qb.qb_warn) && - (s64)be64_to_cpu(qd->qd_qb.qb_warn) < value && - time_after_eq(jiffies, qd->qd_last_warn + - gfs2_tune_get(sdp, - gt_quota_warn_period) * HZ)) { - error = print_message(qd, "warning"); - qd->qd_last_warn = jiffies; - } - } - - return error; -} - -void gfs2_quota_change(struct gfs2_inode *ip, s64 change, - u32 uid, u32 gid) -{ - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_quota_data *qd; - unsigned int x; - unsigned int found = 0; - - if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), change)) - return; - if (ip->i_di.di_flags & GFS2_DIF_SYSTEM) - return; - - for (x = 0; x < al->al_qd_num; x++) { - qd = al->al_qd[x]; - - if ((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) || - (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags))) { - do_qc(qd, change); - found++; - } - } -} - -int gfs2_quota_sync(struct gfs2_sbd *sdp) -{ - struct gfs2_quota_data **qda; - unsigned int max_qd = gfs2_tune_get(sdp, gt_quota_simul_sync); - unsigned int num_qd; - unsigned int x; - int error = 0; - - sdp->sd_quota_sync_gen++; - - qda = kcalloc(max_qd, sizeof(struct gfs2_quota_data *), GFP_KERNEL); - if (!qda) - return -ENOMEM; - - do { - num_qd = 0; - - for (;;) { - error = qd_fish(sdp, qda + num_qd); - if (error || !qda[num_qd]) - break; - if (++num_qd == max_qd) - break; - } - - if (num_qd) { - if (!error) - error = do_sync(num_qd, qda); - if (!error) - for (x = 0; x < num_qd; x++) - qda[x]->qd_sync_gen = - sdp->sd_quota_sync_gen; - - for (x = 0; x < num_qd; x++) - qd_unlock(qda[x]); - } - } while (!error && num_qd == max_qd); - - kfree(qda); - - return error; -} - -int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id) -{ - struct gfs2_quota_data *qd; - struct gfs2_holder q_gh; - int error; - - error = qd_get(sdp, user, id, CREATE, &qd); - if (error) - return error; - - error = do_glock(qd, FORCE, &q_gh); - if (!error) - gfs2_glock_dq_uninit(&q_gh); - - qd_put(qd); - - return error; -} - -int gfs2_quota_init(struct gfs2_sbd *sdp) -{ - struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); - unsigned int blocks = ip->i_di.di_size >> sdp->sd_sb.sb_bsize_shift; - unsigned int x, slot = 0; - unsigned int found = 0; - u64 dblock; - u32 extlen = 0; - int error; - - if (!ip->i_di.di_size || ip->i_di.di_size > (64 << 20) || - ip->i_di.di_size & (sdp->sd_sb.sb_bsize - 1)) { - gfs2_consist_inode(ip); - return -EIO; - } - sdp->sd_quota_slots = blocks * sdp->sd_qc_per_block; - sdp->sd_quota_chunks = DIV_ROUND_UP(sdp->sd_quota_slots, 8 * PAGE_SIZE); - - error = -ENOMEM; - - sdp->sd_quota_bitmap = kcalloc(sdp->sd_quota_chunks, - sizeof(unsigned char *), GFP_KERNEL); - if (!sdp->sd_quota_bitmap) - return error; - - for (x = 0; x < sdp->sd_quota_chunks; x++) { - sdp->sd_quota_bitmap[x] = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!sdp->sd_quota_bitmap[x]) - goto fail; - } - - for (x = 0; x < blocks; x++) { - struct buffer_head *bh; - unsigned int y; - - if (!extlen) { - int new = 0; - error = gfs2_extent_map(&ip->i_inode, x, &new, &dblock, &extlen); - if (error) - goto fail; - } - error = -EIO; - bh = gfs2_meta_ra(ip->i_gl, dblock, extlen); - if (!bh) - goto fail; - if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_QC)) { - brelse(bh); - goto fail; - } - - for (y = 0; y < sdp->sd_qc_per_block && slot < sdp->sd_quota_slots; - y++, slot++) { - struct gfs2_quota_change qc; - struct gfs2_quota_data *qd; - - gfs2_quota_change_in(&qc, bh->b_data + - sizeof(struct gfs2_meta_header) + - y * sizeof(struct gfs2_quota_change)); - if (!qc.qc_change) - continue; - - error = qd_alloc(sdp, (qc.qc_flags & GFS2_QCF_USER), - qc.qc_id, &qd); - if (error) { - brelse(bh); - goto fail; - } - - set_bit(QDF_CHANGE, &qd->qd_flags); - qd->qd_change = qc.qc_change; - qd->qd_slot = slot; - qd->qd_slot_count = 1; - qd->qd_last_touched = jiffies; - - spin_lock(&sdp->sd_quota_spin); - gfs2_icbit_munge(sdp, sdp->sd_quota_bitmap, slot, 1); - list_add(&qd->qd_list, &sdp->sd_quota_list); - atomic_inc(&sdp->sd_quota_count); - spin_unlock(&sdp->sd_quota_spin); - - found++; - } - - brelse(bh); - dblock++; - extlen--; - } - - if (found) - fs_info(sdp, "found %u quota changes\n", found); - - return 0; - -fail: - gfs2_quota_cleanup(sdp); - return error; -} - -void gfs2_quota_scan(struct gfs2_sbd *sdp) -{ - struct gfs2_quota_data *qd, *safe; - LIST_HEAD(dead); - - spin_lock(&sdp->sd_quota_spin); - list_for_each_entry_safe(qd, safe, &sdp->sd_quota_list, qd_list) { - if (!qd->qd_count && - time_after_eq(jiffies, qd->qd_last_touched + - gfs2_tune_get(sdp, gt_quota_cache_secs) * HZ)) { - list_move(&qd->qd_list, &dead); - gfs2_assert_warn(sdp, - atomic_read(&sdp->sd_quota_count) > 0); - atomic_dec(&sdp->sd_quota_count); - } - } - spin_unlock(&sdp->sd_quota_spin); - - while (!list_empty(&dead)) { - qd = list_entry(dead.next, struct gfs2_quota_data, qd_list); - list_del(&qd->qd_list); - - gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); - gfs2_assert_warn(sdp, !qd->qd_bh_count); - - gfs2_lvb_unhold(qd->qd_gl); - kfree(qd); - } -} - -void gfs2_quota_cleanup(struct gfs2_sbd *sdp) -{ - struct list_head *head = &sdp->sd_quota_list; - struct gfs2_quota_data *qd; - unsigned int x; - - spin_lock(&sdp->sd_quota_spin); - while (!list_empty(head)) { - qd = list_entry(head->prev, struct gfs2_quota_data, qd_list); - - if (qd->qd_count > 1 || - (qd->qd_count && !test_bit(QDF_CHANGE, &qd->qd_flags))) { - list_move(&qd->qd_list, head); - spin_unlock(&sdp->sd_quota_spin); - schedule(); - spin_lock(&sdp->sd_quota_spin); - continue; - } - - list_del(&qd->qd_list); - atomic_dec(&sdp->sd_quota_count); - spin_unlock(&sdp->sd_quota_spin); - - if (!qd->qd_count) { - gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); - } else - gfs2_assert_warn(sdp, qd->qd_slot_count == 1); - gfs2_assert_warn(sdp, !qd->qd_bh_count); - - gfs2_lvb_unhold(qd->qd_gl); - kfree(qd); - - spin_lock(&sdp->sd_quota_spin); - } - spin_unlock(&sdp->sd_quota_spin); - - gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_quota_count)); - - if (sdp->sd_quota_bitmap) { - for (x = 0; x < sdp->sd_quota_chunks; x++) - kfree(sdp->sd_quota_bitmap[x]); - kfree(sdp->sd_quota_bitmap); - } -} - diff --git a/trunk/fs/gfs2/quota.h b/trunk/fs/gfs2/quota.h deleted file mode 100644 index a8be1417051f..000000000000 --- a/trunk/fs/gfs2/quota.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __QUOTA_DOT_H__ -#define __QUOTA_DOT_H__ - -struct gfs2_inode; -struct gfs2_sbd; - -#define NO_QUOTA_CHANGE ((u32)-1) - -int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid); -void gfs2_quota_unhold(struct gfs2_inode *ip); - -int gfs2_quota_lock(struct gfs2_inode *ip, u32 uid, u32 gid); -void gfs2_quota_unlock(struct gfs2_inode *ip); - -int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid); -void gfs2_quota_change(struct gfs2_inode *ip, s64 change, - u32 uid, u32 gid); - -int gfs2_quota_sync(struct gfs2_sbd *sdp); -int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id); - -int gfs2_quota_init(struct gfs2_sbd *sdp); -void gfs2_quota_scan(struct gfs2_sbd *sdp); -void gfs2_quota_cleanup(struct gfs2_sbd *sdp); - -#endif /* __QUOTA_DOT_H__ */ diff --git a/trunk/fs/gfs2/recovery.c b/trunk/fs/gfs2/recovery.c deleted file mode 100644 index 0a8a4b87dcc6..000000000000 --- a/trunk/fs/gfs2/recovery.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "glock.h" -#include "glops.h" -#include "lm.h" -#include "lops.h" -#include "meta_io.h" -#include "recovery.h" -#include "super.h" -#include "util.h" -#include "dir.h" - -int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk, - struct buffer_head **bh) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_glock *gl = ip->i_gl; - int new = 0; - u64 dblock; - u32 extlen; - int error; - - error = gfs2_extent_map(&ip->i_inode, blk, &new, &dblock, &extlen); - if (error) - return error; - if (!dblock) { - gfs2_consist_inode(ip); - return -EIO; - } - - *bh = gfs2_meta_ra(gl, dblock, extlen); - - return error; -} - -int gfs2_revoke_add(struct gfs2_sbd *sdp, u64 blkno, unsigned int where) -{ - struct list_head *head = &sdp->sd_revoke_list; - struct gfs2_revoke_replay *rr; - int found = 0; - - list_for_each_entry(rr, head, rr_list) { - if (rr->rr_blkno == blkno) { - found = 1; - break; - } - } - - if (found) { - rr->rr_where = where; - return 0; - } - - rr = kmalloc(sizeof(struct gfs2_revoke_replay), GFP_KERNEL); - if (!rr) - return -ENOMEM; - - rr->rr_blkno = blkno; - rr->rr_where = where; - list_add(&rr->rr_list, head); - - return 1; -} - -int gfs2_revoke_check(struct gfs2_sbd *sdp, u64 blkno, unsigned int where) -{ - struct gfs2_revoke_replay *rr; - int wrap, a, b, revoke; - int found = 0; - - list_for_each_entry(rr, &sdp->sd_revoke_list, rr_list) { - if (rr->rr_blkno == blkno) { - found = 1; - break; - } - } - - if (!found) - return 0; - - wrap = (rr->rr_where < sdp->sd_replay_tail); - a = (sdp->sd_replay_tail < where); - b = (where < rr->rr_where); - revoke = (wrap) ? (a || b) : (a && b); - - return revoke; -} - -void gfs2_revoke_clean(struct gfs2_sbd *sdp) -{ - struct list_head *head = &sdp->sd_revoke_list; - struct gfs2_revoke_replay *rr; - - while (!list_empty(head)) { - rr = list_entry(head->next, struct gfs2_revoke_replay, rr_list); - list_del(&rr->rr_list); - kfree(rr); - } -} - -/** - * get_log_header - read the log header for a given segment - * @jd: the journal - * @blk: the block to look at - * @lh: the log header to return - * - * Read the log header for a given segement in a given journal. Do a few - * sanity checks on it. - * - * Returns: 0 on success, - * 1 if the header was invalid or incomplete, - * errno on error - */ - -static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk, - struct gfs2_log_header *head) -{ - struct buffer_head *bh; - struct gfs2_log_header lh; - u32 hash; - int error; - - error = gfs2_replay_read_block(jd, blk, &bh); - if (error) - return error; - - memcpy(&lh, bh->b_data, sizeof(struct gfs2_log_header)); - lh.lh_hash = 0; - hash = gfs2_disk_hash((char *)&lh, sizeof(struct gfs2_log_header)); - gfs2_log_header_in(&lh, bh->b_data); - - brelse(bh); - - if (lh.lh_header.mh_magic != GFS2_MAGIC || - lh.lh_header.mh_type != GFS2_METATYPE_LH || - lh.lh_blkno != blk || lh.lh_hash != hash) - return 1; - - *head = lh; - - return 0; -} - -/** - * find_good_lh - find a good log header - * @jd: the journal - * @blk: the segment to start searching from - * @lh: the log header to fill in - * @forward: if true search forward in the log, else search backward - * - * Call get_log_header() to get a log header for a segment, but if the - * segment is bad, either scan forward or backward until we find a good one. - * - * Returns: errno - */ - -static int find_good_lh(struct gfs2_jdesc *jd, unsigned int *blk, - struct gfs2_log_header *head) -{ - unsigned int orig_blk = *blk; - int error; - - for (;;) { - error = get_log_header(jd, *blk, head); - if (error <= 0) - return error; - - if (++*blk == jd->jd_blocks) - *blk = 0; - - if (*blk == orig_blk) { - gfs2_consist_inode(GFS2_I(jd->jd_inode)); - return -EIO; - } - } -} - -/** - * jhead_scan - make sure we've found the head of the log - * @jd: the journal - * @head: this is filled in with the log descriptor of the head - * - * At this point, seg and lh should be either the head of the log or just - * before. Scan forward until we find the head. - * - * Returns: errno - */ - -static int jhead_scan(struct gfs2_jdesc *jd, struct gfs2_log_header *head) -{ - unsigned int blk = head->lh_blkno; - struct gfs2_log_header lh; - int error; - - for (;;) { - if (++blk == jd->jd_blocks) - blk = 0; - - error = get_log_header(jd, blk, &lh); - if (error < 0) - return error; - if (error == 1) - continue; - - if (lh.lh_sequence == head->lh_sequence) { - gfs2_consist_inode(GFS2_I(jd->jd_inode)); - return -EIO; - } - if (lh.lh_sequence < head->lh_sequence) - break; - - *head = lh; - } - - return 0; -} - -/** - * gfs2_find_jhead - find the head of a log - * @jd: the journal - * @head: the log descriptor for the head of the log is returned here - * - * Do a binary search of a journal and find the valid log entry with the - * highest sequence number. (i.e. the log head) - * - * Returns: errno - */ - -int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header *head) -{ - struct gfs2_log_header lh_1, lh_m; - u32 blk_1, blk_2, blk_m; - int error; - - blk_1 = 0; - blk_2 = jd->jd_blocks - 1; - - for (;;) { - blk_m = (blk_1 + blk_2) / 2; - - error = find_good_lh(jd, &blk_1, &lh_1); - if (error) - return error; - - error = find_good_lh(jd, &blk_m, &lh_m); - if (error) - return error; - - if (blk_1 == blk_m || blk_m == blk_2) - break; - - if (lh_1.lh_sequence <= lh_m.lh_sequence) - blk_1 = blk_m; - else - blk_2 = blk_m; - } - - error = jhead_scan(jd, &lh_1); - if (error) - return error; - - *head = lh_1; - - return error; -} - -/** - * foreach_descriptor - go through the active part of the log - * @jd: the journal - * @start: the first log header in the active region - * @end: the last log header (don't process the contents of this entry)) - * - * Call a given function once for every log descriptor in the active - * portion of the log. - * - * Returns: errno - */ - -static int foreach_descriptor(struct gfs2_jdesc *jd, unsigned int start, - unsigned int end, int pass) -{ - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - struct buffer_head *bh; - struct gfs2_log_descriptor *ld; - int error = 0; - u32 length; - __be64 *ptr; - unsigned int offset = sizeof(struct gfs2_log_descriptor); - offset += sizeof(__be64) - 1; - offset &= ~(sizeof(__be64) - 1); - - while (start != end) { - error = gfs2_replay_read_block(jd, start, &bh); - if (error) - return error; - if (gfs2_meta_check(sdp, bh)) { - brelse(bh); - return -EIO; - } - ld = (struct gfs2_log_descriptor *)bh->b_data; - length = be32_to_cpu(ld->ld_length); - - if (be32_to_cpu(ld->ld_header.mh_type) == GFS2_METATYPE_LH) { - struct gfs2_log_header lh; - error = get_log_header(jd, start, &lh); - if (!error) { - gfs2_replay_incr_blk(sdp, &start); - brelse(bh); - continue; - } - if (error == 1) { - gfs2_consist_inode(GFS2_I(jd->jd_inode)); - error = -EIO; - } - brelse(bh); - return error; - } else if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_LD)) { - brelse(bh); - return -EIO; - } - ptr = (__be64 *)(bh->b_data + offset); - error = lops_scan_elements(jd, start, ld, ptr, pass); - if (error) { - brelse(bh); - return error; - } - - while (length--) - gfs2_replay_incr_blk(sdp, &start); - - brelse(bh); - } - - return 0; -} - -/** - * clean_journal - mark a dirty journal as being clean - * @sdp: the filesystem - * @jd: the journal - * @gl: the journal's glock - * @head: the head journal to start from - * - * Returns: errno - */ - -static int clean_journal(struct gfs2_jdesc *jd, struct gfs2_log_header *head) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - unsigned int lblock; - struct gfs2_log_header *lh; - u32 hash; - struct buffer_head *bh; - int error; - struct buffer_head bh_map; - - lblock = head->lh_blkno; - gfs2_replay_incr_blk(sdp, &lblock); - error = gfs2_block_map(&ip->i_inode, lblock, 0, &bh_map, 1); - if (error) - return error; - if (!bh_map.b_blocknr) { - gfs2_consist_inode(ip); - return -EIO; - } - - bh = sb_getblk(sdp->sd_vfs, bh_map.b_blocknr); - lock_buffer(bh); - memset(bh->b_data, 0, bh->b_size); - set_buffer_uptodate(bh); - clear_buffer_dirty(bh); - unlock_buffer(bh); - - lh = (struct gfs2_log_header *)bh->b_data; - memset(lh, 0, sizeof(struct gfs2_log_header)); - lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC); - lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH); - lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH); - lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1); - lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT); - lh->lh_blkno = cpu_to_be32(lblock); - hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header)); - lh->lh_hash = cpu_to_be32(hash); - - set_buffer_dirty(bh); - if (sync_dirty_buffer(bh)) - gfs2_io_error_bh(sdp, bh); - brelse(bh); - - return error; -} - -/** - * gfs2_recover_journal - recovery a given journal - * @jd: the struct gfs2_jdesc describing the journal - * - * Acquire the journal's lock, check to see if the journal is clean, and - * do recovery if necessary. - * - * Returns: errno - */ - -int gfs2_recover_journal(struct gfs2_jdesc *jd) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - struct gfs2_log_header head; - struct gfs2_holder j_gh, ji_gh, t_gh; - unsigned long t; - int ro = 0; - unsigned int pass; - int error; - - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) { - fs_info(sdp, "jid=%u: Trying to acquire journal lock...\n", - jd->jd_jid); - - /* Aquire the journal lock so we can do recovery */ - - error = gfs2_glock_nq_num(sdp, jd->jd_jid, &gfs2_journal_glops, - LM_ST_EXCLUSIVE, - LM_FLAG_NOEXP | LM_FLAG_TRY | GL_NOCACHE, - &j_gh); - switch (error) { - case 0: - break; - - case GLR_TRYFAILED: - fs_info(sdp, "jid=%u: Busy\n", jd->jd_jid); - error = 0; - - default: - goto fail; - }; - - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, - LM_FLAG_NOEXP, &ji_gh); - if (error) - goto fail_gunlock_j; - } else { - fs_info(sdp, "jid=%u, already locked for use\n", jd->jd_jid); - } - - fs_info(sdp, "jid=%u: Looking at journal...\n", jd->jd_jid); - - error = gfs2_jdesc_check(jd); - if (error) - goto fail_gunlock_ji; - - error = gfs2_find_jhead(jd, &head); - if (error) - goto fail_gunlock_ji; - - if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { - fs_info(sdp, "jid=%u: Acquiring the transaction lock...\n", - jd->jd_jid); - - t = jiffies; - - /* Acquire a shared hold on the transaction lock */ - - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, - LM_FLAG_NOEXP | LM_FLAG_PRIORITY | - GL_NOCANCEL | GL_NOCACHE, &t_gh); - if (error) - goto fail_gunlock_ji; - - if (test_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags)) { - if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) - ro = 1; - } else { - if (sdp->sd_vfs->s_flags & MS_RDONLY) - ro = 1; - } - - if (ro) { - fs_warn(sdp, "jid=%u: Can't replay: read-only FS\n", - jd->jd_jid); - error = -EROFS; - goto fail_gunlock_tr; - } - - fs_info(sdp, "jid=%u: Replaying journal...\n", jd->jd_jid); - - for (pass = 0; pass < 2; pass++) { - lops_before_scan(jd, &head, pass); - error = foreach_descriptor(jd, head.lh_tail, - head.lh_blkno, pass); - lops_after_scan(jd, error, pass); - if (error) - goto fail_gunlock_tr; - } - - error = clean_journal(jd, &head); - if (error) - goto fail_gunlock_tr; - - gfs2_glock_dq_uninit(&t_gh); - t = DIV_ROUND_UP(jiffies - t, HZ); - fs_info(sdp, "jid=%u: Journal replayed in %lus\n", - jd->jd_jid, t); - } - - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) - gfs2_glock_dq_uninit(&ji_gh); - - gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS); - - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) - gfs2_glock_dq_uninit(&j_gh); - - fs_info(sdp, "jid=%u: Done\n", jd->jd_jid); - return 0; - -fail_gunlock_tr: - gfs2_glock_dq_uninit(&t_gh); -fail_gunlock_ji: - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) { - gfs2_glock_dq_uninit(&ji_gh); -fail_gunlock_j: - gfs2_glock_dq_uninit(&j_gh); - } - - fs_info(sdp, "jid=%u: %s\n", jd->jd_jid, (error) ? "Failed" : "Done"); - -fail: - gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP); - return error; -} - -/** - * gfs2_check_journals - Recover any dirty journals - * @sdp: the filesystem - * - */ - -void gfs2_check_journals(struct gfs2_sbd *sdp) -{ - struct gfs2_jdesc *jd; - - for (;;) { - jd = gfs2_jdesc_find_dirty(sdp); - if (!jd) - break; - - if (jd != sdp->sd_jdesc) - gfs2_recover_journal(jd); - } -} - diff --git a/trunk/fs/gfs2/recovery.h b/trunk/fs/gfs2/recovery.h deleted file mode 100644 index 961feedf4d8b..000000000000 --- a/trunk/fs/gfs2/recovery.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __RECOVERY_DOT_H__ -#define __RECOVERY_DOT_H__ - -#include "incore.h" - -static inline void gfs2_replay_incr_blk(struct gfs2_sbd *sdp, unsigned int *blk) -{ - if (++*blk == sdp->sd_jdesc->jd_blocks) - *blk = 0; -} - -int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk, - struct buffer_head **bh); - -int gfs2_revoke_add(struct gfs2_sbd *sdp, u64 blkno, unsigned int where); -int gfs2_revoke_check(struct gfs2_sbd *sdp, u64 blkno, unsigned int where); -void gfs2_revoke_clean(struct gfs2_sbd *sdp); - -int gfs2_find_jhead(struct gfs2_jdesc *jd, - struct gfs2_log_header *head); -int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd); -void gfs2_check_journals(struct gfs2_sbd *sdp); - -#endif /* __RECOVERY_DOT_H__ */ - diff --git a/trunk/fs/gfs2/rgrp.c b/trunk/fs/gfs2/rgrp.c deleted file mode 100644 index b261385c0065..000000000000 --- a/trunk/fs/gfs2/rgrp.c +++ /dev/null @@ -1,1513 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "glops.h" -#include "lops.h" -#include "meta_io.h" -#include "quota.h" -#include "rgrp.h" -#include "super.h" -#include "trans.h" -#include "ops_file.h" -#include "util.h" - -#define BFITNOENT ((u32)~0) - -/* - * These routines are used by the resource group routines (rgrp.c) - * to keep track of block allocation. Each block is represented by two - * bits. So, each byte represents GFS2_NBBY (i.e. 4) blocks. - * - * 0 = Free - * 1 = Used (not metadata) - * 2 = Unlinked (still in use) inode - * 3 = Used (metadata) - */ - -static const char valid_change[16] = { - /* current */ - /* n */ 0, 1, 1, 1, - /* e */ 1, 0, 0, 0, - /* w */ 0, 0, 0, 1, - 1, 0, 0, 0 -}; - -/** - * gfs2_setbit - Set a bit in the bitmaps - * @buffer: the buffer that holds the bitmaps - * @buflen: the length (in bytes) of the buffer - * @block: the block to set - * @new_state: the new state of the block - * - */ - -static void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buffer, - unsigned int buflen, u32 block, - unsigned char new_state) -{ - unsigned char *byte, *end, cur_state; - unsigned int bit; - - byte = buffer + (block / GFS2_NBBY); - bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE; - end = buffer + buflen; - - gfs2_assert(rgd->rd_sbd, byte < end); - - cur_state = (*byte >> bit) & GFS2_BIT_MASK; - - if (valid_change[new_state * 4 + cur_state]) { - *byte ^= cur_state << bit; - *byte |= new_state << bit; - } else - gfs2_consist_rgrpd(rgd); -} - -/** - * gfs2_testbit - test a bit in the bitmaps - * @buffer: the buffer that holds the bitmaps - * @buflen: the length (in bytes) of the buffer - * @block: the block to read - * - */ - -static unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer, - unsigned int buflen, u32 block) -{ - unsigned char *byte, *end, cur_state; - unsigned int bit; - - byte = buffer + (block / GFS2_NBBY); - bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE; - end = buffer + buflen; - - gfs2_assert(rgd->rd_sbd, byte < end); - - cur_state = (*byte >> bit) & GFS2_BIT_MASK; - - return cur_state; -} - -/** - * gfs2_bitfit - Search an rgrp's bitmap buffer to find a bit-pair representing - * a block in a given allocation state. - * @buffer: the buffer that holds the bitmaps - * @buflen: the length (in bytes) of the buffer - * @goal: start search at this block's bit-pair (within @buffer) - * @old_state: GFS2_BLKST_XXX the state of the block we're looking for; - * bit 0 = alloc(1)/free(0), bit 1 = meta(1)/data(0) - * - * Scope of @goal and returned block number is only within this bitmap buffer, - * not entire rgrp or filesystem. @buffer will be offset from the actual - * beginning of a bitmap block buffer, skipping any header structures. - * - * Return: the block number (bitmap buffer scope) that was found - */ - -static u32 gfs2_bitfit(struct gfs2_rgrpd *rgd, unsigned char *buffer, - unsigned int buflen, u32 goal, - unsigned char old_state) -{ - unsigned char *byte, *end, alloc; - u32 blk = goal; - unsigned int bit; - - byte = buffer + (goal / GFS2_NBBY); - bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE; - end = buffer + buflen; - alloc = (old_state & 1) ? 0 : 0x55; - - while (byte < end) { - if ((*byte & 0x55) == alloc) { - blk += (8 - bit) >> 1; - - bit = 0; - byte++; - - continue; - } - - if (((*byte >> bit) & GFS2_BIT_MASK) == old_state) - return blk; - - bit += GFS2_BIT_SIZE; - if (bit >= 8) { - bit = 0; - byte++; - } - - blk++; - } - - return BFITNOENT; -} - -/** - * gfs2_bitcount - count the number of bits in a certain state - * @buffer: the buffer that holds the bitmaps - * @buflen: the length (in bytes) of the buffer - * @state: the state of the block we're looking for - * - * Returns: The number of bits - */ - -static u32 gfs2_bitcount(struct gfs2_rgrpd *rgd, unsigned char *buffer, - unsigned int buflen, unsigned char state) -{ - unsigned char *byte = buffer; - unsigned char *end = buffer + buflen; - unsigned char state1 = state << 2; - unsigned char state2 = state << 4; - unsigned char state3 = state << 6; - u32 count = 0; - - for (; byte < end; byte++) { - if (((*byte) & 0x03) == state) - count++; - if (((*byte) & 0x0C) == state1) - count++; - if (((*byte) & 0x30) == state2) - count++; - if (((*byte) & 0xC0) == state3) - count++; - } - - return count; -} - -/** - * gfs2_rgrp_verify - Verify that a resource group is consistent - * @sdp: the filesystem - * @rgd: the rgrp - * - */ - -void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - struct gfs2_bitmap *bi = NULL; - u32 length = rgd->rd_ri.ri_length; - u32 count[4], tmp; - int buf, x; - - memset(count, 0, 4 * sizeof(u32)); - - /* Count # blocks in each of 4 possible allocation states */ - for (buf = 0; buf < length; buf++) { - bi = rgd->rd_bits + buf; - for (x = 0; x < 4; x++) - count[x] += gfs2_bitcount(rgd, - bi->bi_bh->b_data + - bi->bi_offset, - bi->bi_len, x); - } - - if (count[0] != rgd->rd_rg.rg_free) { - if (gfs2_consist_rgrpd(rgd)) - fs_err(sdp, "free data mismatch: %u != %u\n", - count[0], rgd->rd_rg.rg_free); - return; - } - - tmp = rgd->rd_ri.ri_data - - rgd->rd_rg.rg_free - - rgd->rd_rg.rg_dinodes; - if (count[1] + count[2] != tmp) { - if (gfs2_consist_rgrpd(rgd)) - fs_err(sdp, "used data mismatch: %u != %u\n", - count[1], tmp); - return; - } - - if (count[3] != rgd->rd_rg.rg_dinodes) { - if (gfs2_consist_rgrpd(rgd)) - fs_err(sdp, "used metadata mismatch: %u != %u\n", - count[3], rgd->rd_rg.rg_dinodes); - return; - } - - if (count[2] > count[3]) { - if (gfs2_consist_rgrpd(rgd)) - fs_err(sdp, "unlinked inodes > inodes: %u\n", - count[2]); - return; - } - -} - -static inline int rgrp_contains_block(struct gfs2_rindex *ri, u64 block) -{ - u64 first = ri->ri_data0; - u64 last = first + ri->ri_data; - return first <= block && block < last; -} - -/** - * gfs2_blk2rgrpd - Find resource group for a given data/meta block number - * @sdp: The GFS2 superblock - * @n: The data block number - * - * Returns: The resource group, or NULL if not found - */ - -struct gfs2_rgrpd *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, u64 blk) -{ - struct gfs2_rgrpd *rgd; - - spin_lock(&sdp->sd_rindex_spin); - - list_for_each_entry(rgd, &sdp->sd_rindex_mru_list, rd_list_mru) { - if (rgrp_contains_block(&rgd->rd_ri, blk)) { - list_move(&rgd->rd_list_mru, &sdp->sd_rindex_mru_list); - spin_unlock(&sdp->sd_rindex_spin); - return rgd; - } - } - - spin_unlock(&sdp->sd_rindex_spin); - - return NULL; -} - -/** - * gfs2_rgrpd_get_first - get the first Resource Group in the filesystem - * @sdp: The GFS2 superblock - * - * Returns: The first rgrp in the filesystem - */ - -struct gfs2_rgrpd *gfs2_rgrpd_get_first(struct gfs2_sbd *sdp) -{ - gfs2_assert(sdp, !list_empty(&sdp->sd_rindex_list)); - return list_entry(sdp->sd_rindex_list.next, struct gfs2_rgrpd, rd_list); -} - -/** - * gfs2_rgrpd_get_next - get the next RG - * @rgd: A RG - * - * Returns: The next rgrp - */ - -struct gfs2_rgrpd *gfs2_rgrpd_get_next(struct gfs2_rgrpd *rgd) -{ - if (rgd->rd_list.next == &rgd->rd_sbd->sd_rindex_list) - return NULL; - return list_entry(rgd->rd_list.next, struct gfs2_rgrpd, rd_list); -} - -static void clear_rgrpdi(struct gfs2_sbd *sdp) -{ - struct list_head *head; - struct gfs2_rgrpd *rgd; - struct gfs2_glock *gl; - - spin_lock(&sdp->sd_rindex_spin); - sdp->sd_rindex_forward = NULL; - head = &sdp->sd_rindex_recent_list; - while (!list_empty(head)) { - rgd = list_entry(head->next, struct gfs2_rgrpd, rd_recent); - list_del(&rgd->rd_recent); - } - spin_unlock(&sdp->sd_rindex_spin); - - head = &sdp->sd_rindex_list; - while (!list_empty(head)) { - rgd = list_entry(head->next, struct gfs2_rgrpd, rd_list); - gl = rgd->rd_gl; - - list_del(&rgd->rd_list); - list_del(&rgd->rd_list_mru); - - if (gl) { - gl->gl_object = NULL; - gfs2_glock_put(gl); - } - - kfree(rgd->rd_bits); - kfree(rgd); - } -} - -void gfs2_clear_rgrpd(struct gfs2_sbd *sdp) -{ - mutex_lock(&sdp->sd_rindex_mutex); - clear_rgrpdi(sdp); - mutex_unlock(&sdp->sd_rindex_mutex); -} - -/** - * gfs2_compute_bitstructs - Compute the bitmap sizes - * @rgd: The resource group descriptor - * - * Calculates bitmap descriptors, one for each block that contains bitmap data - * - * Returns: errno - */ - -static int compute_bitstructs(struct gfs2_rgrpd *rgd) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - struct gfs2_bitmap *bi; - u32 length = rgd->rd_ri.ri_length; /* # blocks in hdr & bitmap */ - u32 bytes_left, bytes; - int x; - - if (!length) - return -EINVAL; - - rgd->rd_bits = kcalloc(length, sizeof(struct gfs2_bitmap), GFP_NOFS); - if (!rgd->rd_bits) - return -ENOMEM; - - bytes_left = rgd->rd_ri.ri_bitbytes; - - for (x = 0; x < length; x++) { - bi = rgd->rd_bits + x; - - /* small rgrp; bitmap stored completely in header block */ - if (length == 1) { - bytes = bytes_left; - bi->bi_offset = sizeof(struct gfs2_rgrp); - bi->bi_start = 0; - bi->bi_len = bytes; - /* header block */ - } else if (x == 0) { - bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_rgrp); - bi->bi_offset = sizeof(struct gfs2_rgrp); - bi->bi_start = 0; - bi->bi_len = bytes; - /* last block */ - } else if (x + 1 == length) { - bytes = bytes_left; - bi->bi_offset = sizeof(struct gfs2_meta_header); - bi->bi_start = rgd->rd_ri.ri_bitbytes - bytes_left; - bi->bi_len = bytes; - /* other blocks */ - } else { - bytes = sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_meta_header); - bi->bi_offset = sizeof(struct gfs2_meta_header); - bi->bi_start = rgd->rd_ri.ri_bitbytes - bytes_left; - bi->bi_len = bytes; - } - - bytes_left -= bytes; - } - - if (bytes_left) { - gfs2_consist_rgrpd(rgd); - return -EIO; - } - bi = rgd->rd_bits + (length - 1); - if ((bi->bi_start + bi->bi_len) * GFS2_NBBY != rgd->rd_ri.ri_data) { - if (gfs2_consist_rgrpd(rgd)) { - gfs2_rindex_print(&rgd->rd_ri); - fs_err(sdp, "start=%u len=%u offset=%u\n", - bi->bi_start, bi->bi_len, bi->bi_offset); - } - return -EIO; - } - - return 0; -} - -/** - * gfs2_ri_update - Pull in a new resource index from the disk - * @gl: The glock covering the rindex inode - * - * Returns: 0 on successful update, error code otherwise - */ - -static int gfs2_ri_update(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct inode *inode = &ip->i_inode; - struct gfs2_rgrpd *rgd; - char buf[sizeof(struct gfs2_rindex)]; - struct file_ra_state ra_state; - u64 junk = ip->i_di.di_size; - int error; - - if (do_div(junk, sizeof(struct gfs2_rindex))) { - gfs2_consist_inode(ip); - return -EIO; - } - - clear_rgrpdi(sdp); - - file_ra_state_init(&ra_state, inode->i_mapping); - for (sdp->sd_rgrps = 0;; sdp->sd_rgrps++) { - loff_t pos = sdp->sd_rgrps * sizeof(struct gfs2_rindex); - error = gfs2_internal_read(ip, &ra_state, buf, &pos, - sizeof(struct gfs2_rindex)); - if (!error) - break; - if (error != sizeof(struct gfs2_rindex)) { - if (error > 0) - error = -EIO; - goto fail; - } - - rgd = kzalloc(sizeof(struct gfs2_rgrpd), GFP_NOFS); - error = -ENOMEM; - if (!rgd) - goto fail; - - mutex_init(&rgd->rd_mutex); - lops_init_le(&rgd->rd_le, &gfs2_rg_lops); - rgd->rd_sbd = sdp; - - list_add_tail(&rgd->rd_list, &sdp->sd_rindex_list); - list_add_tail(&rgd->rd_list_mru, &sdp->sd_rindex_mru_list); - - gfs2_rindex_in(&rgd->rd_ri, buf); - error = compute_bitstructs(rgd); - if (error) - goto fail; - - error = gfs2_glock_get(sdp, rgd->rd_ri.ri_addr, - &gfs2_rgrp_glops, CREATE, &rgd->rd_gl); - if (error) - goto fail; - - rgd->rd_gl->gl_object = rgd; - rgd->rd_rg_vn = rgd->rd_gl->gl_vn - 1; - } - - sdp->sd_rindex_vn = ip->i_gl->gl_vn; - return 0; - -fail: - clear_rgrpdi(sdp); - return error; -} - -/** - * gfs2_rindex_hold - Grab a lock on the rindex - * @sdp: The GFS2 superblock - * @ri_gh: the glock holder - * - * We grab a lock on the rindex inode to make sure that it doesn't - * change whilst we are performing an operation. We keep this lock - * for quite long periods of time compared to other locks. This - * doesn't matter, since it is shared and it is very, very rarely - * accessed in the exclusive mode (i.e. only when expanding the filesystem). - * - * This makes sure that we're using the latest copy of the resource index - * special file, which might have been updated if someone expanded the - * filesystem (via gfs2_grow utility), which adds new resource groups. - * - * Returns: 0 on success, error code otherwise - */ - -int gfs2_rindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ri_gh) -{ - struct gfs2_inode *ip = GFS2_I(sdp->sd_rindex); - struct gfs2_glock *gl = ip->i_gl; - int error; - - error = gfs2_glock_nq_init(gl, LM_ST_SHARED, 0, ri_gh); - if (error) - return error; - - /* Read new copy from disk if we don't have the latest */ - if (sdp->sd_rindex_vn != gl->gl_vn) { - mutex_lock(&sdp->sd_rindex_mutex); - if (sdp->sd_rindex_vn != gl->gl_vn) { - error = gfs2_ri_update(ip); - if (error) - gfs2_glock_dq_uninit(ri_gh); - } - mutex_unlock(&sdp->sd_rindex_mutex); - } - - return error; -} - -/** - * gfs2_rgrp_bh_get - Read in a RG's header and bitmaps - * @rgd: the struct gfs2_rgrpd describing the RG to read in - * - * Read in all of a Resource Group's header and bitmap blocks. - * Caller must eventually call gfs2_rgrp_relse() to free the bitmaps. - * - * Returns: errno - */ - -int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - struct gfs2_glock *gl = rgd->rd_gl; - unsigned int length = rgd->rd_ri.ri_length; - struct gfs2_bitmap *bi; - unsigned int x, y; - int error; - - mutex_lock(&rgd->rd_mutex); - - spin_lock(&sdp->sd_rindex_spin); - if (rgd->rd_bh_count) { - rgd->rd_bh_count++; - spin_unlock(&sdp->sd_rindex_spin); - mutex_unlock(&rgd->rd_mutex); - return 0; - } - spin_unlock(&sdp->sd_rindex_spin); - - for (x = 0; x < length; x++) { - bi = rgd->rd_bits + x; - error = gfs2_meta_read(gl, rgd->rd_ri.ri_addr + x, 0, &bi->bi_bh); - if (error) - goto fail; - } - - for (y = length; y--;) { - bi = rgd->rd_bits + y; - error = gfs2_meta_wait(sdp, bi->bi_bh); - if (error) - goto fail; - if (gfs2_metatype_check(sdp, bi->bi_bh, y ? GFS2_METATYPE_RB : - GFS2_METATYPE_RG)) { - error = -EIO; - goto fail; - } - } - - if (rgd->rd_rg_vn != gl->gl_vn) { - gfs2_rgrp_in(&rgd->rd_rg, (rgd->rd_bits[0].bi_bh)->b_data); - rgd->rd_rg_vn = gl->gl_vn; - } - - spin_lock(&sdp->sd_rindex_spin); - rgd->rd_free_clone = rgd->rd_rg.rg_free; - rgd->rd_bh_count++; - spin_unlock(&sdp->sd_rindex_spin); - - mutex_unlock(&rgd->rd_mutex); - - return 0; - -fail: - while (x--) { - bi = rgd->rd_bits + x; - brelse(bi->bi_bh); - bi->bi_bh = NULL; - gfs2_assert_warn(sdp, !bi->bi_clone); - } - mutex_unlock(&rgd->rd_mutex); - - return error; -} - -void gfs2_rgrp_bh_hold(struct gfs2_rgrpd *rgd) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - - spin_lock(&sdp->sd_rindex_spin); - gfs2_assert_warn(rgd->rd_sbd, rgd->rd_bh_count); - rgd->rd_bh_count++; - spin_unlock(&sdp->sd_rindex_spin); -} - -/** - * gfs2_rgrp_bh_put - Release RG bitmaps read in with gfs2_rgrp_bh_get() - * @rgd: the struct gfs2_rgrpd describing the RG to read in - * - */ - -void gfs2_rgrp_bh_put(struct gfs2_rgrpd *rgd) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - int x, length = rgd->rd_ri.ri_length; - - spin_lock(&sdp->sd_rindex_spin); - gfs2_assert_warn(rgd->rd_sbd, rgd->rd_bh_count); - if (--rgd->rd_bh_count) { - spin_unlock(&sdp->sd_rindex_spin); - return; - } - - for (x = 0; x < length; x++) { - struct gfs2_bitmap *bi = rgd->rd_bits + x; - kfree(bi->bi_clone); - bi->bi_clone = NULL; - brelse(bi->bi_bh); - bi->bi_bh = NULL; - } - - spin_unlock(&sdp->sd_rindex_spin); -} - -void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - unsigned int length = rgd->rd_ri.ri_length; - unsigned int x; - - for (x = 0; x < length; x++) { - struct gfs2_bitmap *bi = rgd->rd_bits + x; - if (!bi->bi_clone) - continue; - memcpy(bi->bi_clone + bi->bi_offset, - bi->bi_bh->b_data + bi->bi_offset, bi->bi_len); - } - - spin_lock(&sdp->sd_rindex_spin); - rgd->rd_free_clone = rgd->rd_rg.rg_free; - spin_unlock(&sdp->sd_rindex_spin); -} - -/** - * gfs2_alloc_get - get the struct gfs2_alloc structure for an inode - * @ip: the incore GFS2 inode structure - * - * Returns: the struct gfs2_alloc - */ - -struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip) -{ - struct gfs2_alloc *al = &ip->i_alloc; - - /* FIXME: Should assert that the correct locks are held here... */ - memset(al, 0, sizeof(*al)); - return al; -} - -/** - * try_rgrp_fit - See if a given reservation will fit in a given RG - * @rgd: the RG data - * @al: the struct gfs2_alloc structure describing the reservation - * - * If there's room for the requested blocks to be allocated from the RG: - * Sets the $al_reserved_data field in @al. - * Sets the $al_reserved_meta field in @al. - * Sets the $al_rgd field in @al. - * - * Returns: 1 on success (it fits), 0 on failure (it doesn't fit) - */ - -static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - int ret = 0; - - spin_lock(&sdp->sd_rindex_spin); - if (rgd->rd_free_clone >= al->al_requested) { - al->al_rgd = rgd; - ret = 1; - } - spin_unlock(&sdp->sd_rindex_spin); - - return ret; -} - -/** - * recent_rgrp_first - get first RG from "recent" list - * @sdp: The GFS2 superblock - * @rglast: address of the rgrp used last - * - * Returns: The first rgrp in the recent list - */ - -static struct gfs2_rgrpd *recent_rgrp_first(struct gfs2_sbd *sdp, - u64 rglast) -{ - struct gfs2_rgrpd *rgd = NULL; - - spin_lock(&sdp->sd_rindex_spin); - - if (list_empty(&sdp->sd_rindex_recent_list)) - goto out; - - if (!rglast) - goto first; - - list_for_each_entry(rgd, &sdp->sd_rindex_recent_list, rd_recent) { - if (rgd->rd_ri.ri_addr == rglast) - goto out; - } - -first: - rgd = list_entry(sdp->sd_rindex_recent_list.next, struct gfs2_rgrpd, - rd_recent); -out: - spin_unlock(&sdp->sd_rindex_spin); - return rgd; -} - -/** - * recent_rgrp_next - get next RG from "recent" list - * @cur_rgd: current rgrp - * @remove: - * - * Returns: The next rgrp in the recent list - */ - -static struct gfs2_rgrpd *recent_rgrp_next(struct gfs2_rgrpd *cur_rgd, - int remove) -{ - struct gfs2_sbd *sdp = cur_rgd->rd_sbd; - struct list_head *head; - struct gfs2_rgrpd *rgd; - - spin_lock(&sdp->sd_rindex_spin); - - head = &sdp->sd_rindex_recent_list; - - list_for_each_entry(rgd, head, rd_recent) { - if (rgd == cur_rgd) { - if (cur_rgd->rd_recent.next != head) - rgd = list_entry(cur_rgd->rd_recent.next, - struct gfs2_rgrpd, rd_recent); - else - rgd = NULL; - - if (remove) - list_del(&cur_rgd->rd_recent); - - goto out; - } - } - - rgd = NULL; - if (!list_empty(head)) - rgd = list_entry(head->next, struct gfs2_rgrpd, rd_recent); - -out: - spin_unlock(&sdp->sd_rindex_spin); - return rgd; -} - -/** - * recent_rgrp_add - add an RG to tail of "recent" list - * @new_rgd: The rgrp to add - * - */ - -static void recent_rgrp_add(struct gfs2_rgrpd *new_rgd) -{ - struct gfs2_sbd *sdp = new_rgd->rd_sbd; - struct gfs2_rgrpd *rgd; - unsigned int count = 0; - unsigned int max = sdp->sd_rgrps / gfs2_jindex_size(sdp); - - spin_lock(&sdp->sd_rindex_spin); - - list_for_each_entry(rgd, &sdp->sd_rindex_recent_list, rd_recent) { - if (rgd == new_rgd) - goto out; - - if (++count >= max) - goto out; - } - list_add_tail(&new_rgd->rd_recent, &sdp->sd_rindex_recent_list); - -out: - spin_unlock(&sdp->sd_rindex_spin); -} - -/** - * forward_rgrp_get - get an rgrp to try next from full list - * @sdp: The GFS2 superblock - * - * Returns: The rgrp to try next - */ - -static struct gfs2_rgrpd *forward_rgrp_get(struct gfs2_sbd *sdp) -{ - struct gfs2_rgrpd *rgd; - unsigned int journals = gfs2_jindex_size(sdp); - unsigned int rg = 0, x; - - spin_lock(&sdp->sd_rindex_spin); - - rgd = sdp->sd_rindex_forward; - if (!rgd) { - if (sdp->sd_rgrps >= journals) - rg = sdp->sd_rgrps * sdp->sd_jdesc->jd_jid / journals; - - for (x = 0, rgd = gfs2_rgrpd_get_first(sdp); x < rg; - x++, rgd = gfs2_rgrpd_get_next(rgd)) - /* Do Nothing */; - - sdp->sd_rindex_forward = rgd; - } - - spin_unlock(&sdp->sd_rindex_spin); - - return rgd; -} - -/** - * forward_rgrp_set - set the forward rgrp pointer - * @sdp: the filesystem - * @rgd: The new forward rgrp - * - */ - -static void forward_rgrp_set(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd) -{ - spin_lock(&sdp->sd_rindex_spin); - sdp->sd_rindex_forward = rgd; - spin_unlock(&sdp->sd_rindex_spin); -} - -/** - * get_local_rgrp - Choose and lock a rgrp for allocation - * @ip: the inode to reserve space for - * @rgp: the chosen and locked rgrp - * - * Try to acquire rgrp in way which avoids contending with others. - * - * Returns: errno - */ - -static int get_local_rgrp(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_rgrpd *rgd, *begin = NULL; - struct gfs2_alloc *al = &ip->i_alloc; - int flags = LM_FLAG_TRY; - int skipped = 0; - int loops = 0; - int error; - - /* Try recently successful rgrps */ - - rgd = recent_rgrp_first(sdp, ip->i_last_rg_alloc); - - while (rgd) { - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, - LM_FLAG_TRY, &al->al_rgd_gh); - switch (error) { - case 0: - if (try_rgrp_fit(rgd, al)) - goto out; - gfs2_glock_dq_uninit(&al->al_rgd_gh); - rgd = recent_rgrp_next(rgd, 1); - break; - - case GLR_TRYFAILED: - rgd = recent_rgrp_next(rgd, 0); - break; - - default: - return error; - } - } - - /* Go through full list of rgrps */ - - begin = rgd = forward_rgrp_get(sdp); - - for (;;) { - error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, flags, - &al->al_rgd_gh); - switch (error) { - case 0: - if (try_rgrp_fit(rgd, al)) - goto out; - gfs2_glock_dq_uninit(&al->al_rgd_gh); - break; - - case GLR_TRYFAILED: - skipped++; - break; - - default: - return error; - } - - rgd = gfs2_rgrpd_get_next(rgd); - if (!rgd) - rgd = gfs2_rgrpd_get_first(sdp); - - if (rgd == begin) { - if (++loops >= 2 || !skipped) - return -ENOSPC; - flags = 0; - } - } - -out: - ip->i_last_rg_alloc = rgd->rd_ri.ri_addr; - - if (begin) { - recent_rgrp_add(rgd); - rgd = gfs2_rgrpd_get_next(rgd); - if (!rgd) - rgd = gfs2_rgrpd_get_first(sdp); - forward_rgrp_set(sdp, rgd); - } - - return 0; -} - -/** - * gfs2_inplace_reserve_i - Reserve space in the filesystem - * @ip: the inode to reserve space for - * - * Returns: errno - */ - -int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - int error; - - if (gfs2_assert_warn(sdp, al->al_requested)) - return -EINVAL; - - error = gfs2_rindex_hold(sdp, &al->al_ri_gh); - if (error) - return error; - - error = get_local_rgrp(ip); - if (error) { - gfs2_glock_dq_uninit(&al->al_ri_gh); - return error; - } - - al->al_file = file; - al->al_line = line; - - return 0; -} - -/** - * gfs2_inplace_release - release an inplace reservation - * @ip: the inode the reservation was taken out on - * - * Release a reservation made by gfs2_inplace_reserve(). - */ - -void gfs2_inplace_release(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - - if (gfs2_assert_warn(sdp, al->al_alloced <= al->al_requested) == -1) - fs_warn(sdp, "al_alloced = %u, al_requested = %u " - "al_file = %s, al_line = %u\n", - al->al_alloced, al->al_requested, al->al_file, - al->al_line); - - al->al_rgd = NULL; - gfs2_glock_dq_uninit(&al->al_rgd_gh); - gfs2_glock_dq_uninit(&al->al_ri_gh); -} - -/** - * gfs2_get_block_type - Check a block in a RG is of given type - * @rgd: the resource group holding the block - * @block: the block number - * - * Returns: The block type (GFS2_BLKST_*) - */ - -unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block) -{ - struct gfs2_bitmap *bi = NULL; - u32 length, rgrp_block, buf_block; - unsigned int buf; - unsigned char type; - - length = rgd->rd_ri.ri_length; - rgrp_block = block - rgd->rd_ri.ri_data0; - - for (buf = 0; buf < length; buf++) { - bi = rgd->rd_bits + buf; - if (rgrp_block < (bi->bi_start + bi->bi_len) * GFS2_NBBY) - break; - } - - gfs2_assert(rgd->rd_sbd, buf < length); - buf_block = rgrp_block - bi->bi_start * GFS2_NBBY; - - type = gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset, - bi->bi_len, buf_block); - - return type; -} - -/** - * rgblk_search - find a block in @old_state, change allocation - * state to @new_state - * @rgd: the resource group descriptor - * @goal: the goal block within the RG (start here to search for avail block) - * @old_state: GFS2_BLKST_XXX the before-allocation state to find - * @new_state: GFS2_BLKST_XXX the after-allocation block state - * - * Walk rgrp's bitmap to find bits that represent a block in @old_state. - * Add the found bitmap buffer to the transaction. - * Set the found bits to @new_state to change block's allocation state. - * - * This function never fails, because we wouldn't call it unless we - * know (from reservation results, etc.) that a block is available. - * - * Scope of @goal and returned block is just within rgrp, not the whole - * filesystem. - * - * Returns: the block number allocated - */ - -static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, - unsigned char old_state, unsigned char new_state) -{ - struct gfs2_bitmap *bi = NULL; - u32 length = rgd->rd_ri.ri_length; - u32 blk = 0; - unsigned int buf, x; - - /* Find bitmap block that contains bits for goal block */ - for (buf = 0; buf < length; buf++) { - bi = rgd->rd_bits + buf; - if (goal < (bi->bi_start + bi->bi_len) * GFS2_NBBY) - break; - } - - gfs2_assert(rgd->rd_sbd, buf < length); - - /* Convert scope of "goal" from rgrp-wide to within found bit block */ - goal -= bi->bi_start * GFS2_NBBY; - - /* Search (up to entire) bitmap in this rgrp for allocatable block. - "x <= length", instead of "x < length", because we typically start - the search in the middle of a bit block, but if we can't find an - allocatable block anywhere else, we want to be able wrap around and - search in the first part of our first-searched bit block. */ - for (x = 0; x <= length; x++) { - if (bi->bi_clone) - blk = gfs2_bitfit(rgd, bi->bi_clone + bi->bi_offset, - bi->bi_len, goal, old_state); - else - blk = gfs2_bitfit(rgd, - bi->bi_bh->b_data + bi->bi_offset, - bi->bi_len, goal, old_state); - if (blk != BFITNOENT) - break; - - /* Try next bitmap block (wrap back to rgrp header if at end) */ - buf = (buf + 1) % length; - bi = rgd->rd_bits + buf; - goal = 0; - } - - if (gfs2_assert_withdraw(rgd->rd_sbd, x <= length)) - blk = 0; - - gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); - gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset, - bi->bi_len, blk, new_state); - if (bi->bi_clone) - gfs2_setbit(rgd, bi->bi_clone + bi->bi_offset, - bi->bi_len, blk, new_state); - - return bi->bi_start * GFS2_NBBY + blk; -} - -/** - * rgblk_free - Change alloc state of given block(s) - * @sdp: the filesystem - * @bstart: the start of a run of blocks to free - * @blen: the length of the block run (all must lie within ONE RG!) - * @new_state: GFS2_BLKST_XXX the after-allocation block state - * - * Returns: Resource group containing the block(s) - */ - -static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart, - u32 blen, unsigned char new_state) -{ - struct gfs2_rgrpd *rgd; - struct gfs2_bitmap *bi = NULL; - u32 length, rgrp_blk, buf_blk; - unsigned int buf; - - rgd = gfs2_blk2rgrpd(sdp, bstart); - if (!rgd) { - if (gfs2_consist(sdp)) - fs_err(sdp, "block = %llu\n", (unsigned long long)bstart); - return NULL; - } - - length = rgd->rd_ri.ri_length; - - rgrp_blk = bstart - rgd->rd_ri.ri_data0; - - while (blen--) { - for (buf = 0; buf < length; buf++) { - bi = rgd->rd_bits + buf; - if (rgrp_blk < (bi->bi_start + bi->bi_len) * GFS2_NBBY) - break; - } - - gfs2_assert(rgd->rd_sbd, buf < length); - - buf_blk = rgrp_blk - bi->bi_start * GFS2_NBBY; - rgrp_blk++; - - if (!bi->bi_clone) { - bi->bi_clone = kmalloc(bi->bi_bh->b_size, - GFP_NOFS | __GFP_NOFAIL); - memcpy(bi->bi_clone + bi->bi_offset, - bi->bi_bh->b_data + bi->bi_offset, - bi->bi_len); - } - gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); - gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset, - bi->bi_len, buf_blk, new_state); - } - - return rgd; -} - -/** - * gfs2_alloc_data - Allocate a data block - * @ip: the inode to allocate the data block for - * - * Returns: the allocated block - */ - -u64 gfs2_alloc_data(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_rgrpd *rgd = al->al_rgd; - u32 goal, blk; - u64 block; - - if (rgrp_contains_block(&rgd->rd_ri, ip->i_di.di_goal_data)) - goal = ip->i_di.di_goal_data - rgd->rd_ri.ri_data0; - else - goal = rgd->rd_last_alloc_data; - - blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED); - rgd->rd_last_alloc_data = blk; - - block = rgd->rd_ri.ri_data0 + blk; - ip->i_di.di_goal_data = block; - - gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); - rgd->rd_rg.rg_free--; - - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - - al->al_alloced++; - - gfs2_statfs_change(sdp, 0, -1, 0); - gfs2_quota_change(ip, +1, ip->i_di.di_uid, ip->i_di.di_gid); - - spin_lock(&sdp->sd_rindex_spin); - rgd->rd_free_clone--; - spin_unlock(&sdp->sd_rindex_spin); - - return block; -} - -/** - * gfs2_alloc_meta - Allocate a metadata block - * @ip: the inode to allocate the metadata block for - * - * Returns: the allocated block - */ - -u64 gfs2_alloc_meta(struct gfs2_inode *ip) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al = &ip->i_alloc; - struct gfs2_rgrpd *rgd = al->al_rgd; - u32 goal, blk; - u64 block; - - if (rgrp_contains_block(&rgd->rd_ri, ip->i_di.di_goal_meta)) - goal = ip->i_di.di_goal_meta - rgd->rd_ri.ri_data0; - else - goal = rgd->rd_last_alloc_meta; - - blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, GFS2_BLKST_USED); - rgd->rd_last_alloc_meta = blk; - - block = rgd->rd_ri.ri_data0 + blk; - ip->i_di.di_goal_meta = block; - - gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); - rgd->rd_rg.rg_free--; - - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - - al->al_alloced++; - - gfs2_statfs_change(sdp, 0, -1, 0); - gfs2_quota_change(ip, +1, ip->i_di.di_uid, ip->i_di.di_gid); - gfs2_trans_add_unrevoke(sdp, block); - - spin_lock(&sdp->sd_rindex_spin); - rgd->rd_free_clone--; - spin_unlock(&sdp->sd_rindex_spin); - - return block; -} - -/** - * gfs2_alloc_di - Allocate a dinode - * @dip: the directory that the inode is going in - * - * Returns: the block allocated - */ - -u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation) -{ - struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); - struct gfs2_alloc *al = &dip->i_alloc; - struct gfs2_rgrpd *rgd = al->al_rgd; - u32 blk; - u64 block; - - blk = rgblk_search(rgd, rgd->rd_last_alloc_meta, - GFS2_BLKST_FREE, GFS2_BLKST_DINODE); - - rgd->rd_last_alloc_meta = blk; - - block = rgd->rd_ri.ri_data0 + blk; - - gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); - rgd->rd_rg.rg_free--; - rgd->rd_rg.rg_dinodes++; - *generation = rgd->rd_rg.rg_igeneration++; - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - - al->al_alloced++; - - gfs2_statfs_change(sdp, 0, -1, +1); - gfs2_trans_add_unrevoke(sdp, block); - - spin_lock(&sdp->sd_rindex_spin); - rgd->rd_free_clone--; - spin_unlock(&sdp->sd_rindex_spin); - - return block; -} - -/** - * gfs2_free_data - free a contiguous run of data block(s) - * @ip: the inode these blocks are being freed from - * @bstart: first block of a run of contiguous blocks - * @blen: the length of the block run - * - */ - -void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_rgrpd *rgd; - - rgd = rgblk_free(sdp, bstart, blen, GFS2_BLKST_FREE); - if (!rgd) - return; - - rgd->rd_rg.rg_free += blen; - - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - - gfs2_trans_add_rg(rgd); - - gfs2_statfs_change(sdp, 0, +blen, 0); - gfs2_quota_change(ip, -(s64)blen, - ip->i_di.di_uid, ip->i_di.di_gid); -} - -/** - * gfs2_free_meta - free a contiguous run of data block(s) - * @ip: the inode these blocks are being freed from - * @bstart: first block of a run of contiguous blocks - * @blen: the length of the block run - * - */ - -void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_rgrpd *rgd; - - rgd = rgblk_free(sdp, bstart, blen, GFS2_BLKST_FREE); - if (!rgd) - return; - - rgd->rd_rg.rg_free += blen; - - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - - gfs2_trans_add_rg(rgd); - - gfs2_statfs_change(sdp, 0, +blen, 0); - gfs2_quota_change(ip, -(s64)blen, ip->i_di.di_uid, ip->i_di.di_gid); - gfs2_meta_wipe(ip, bstart, blen); -} - -void gfs2_unlink_di(struct inode *inode) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - struct gfs2_rgrpd *rgd; - u64 blkno = ip->i_num.no_addr; - - rgd = rgblk_free(sdp, blkno, 1, GFS2_BLKST_UNLINKED); - if (!rgd) - return; - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - gfs2_trans_add_rg(rgd); -} - -static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - struct gfs2_rgrpd *tmp_rgd; - - tmp_rgd = rgblk_free(sdp, blkno, 1, GFS2_BLKST_FREE); - if (!tmp_rgd) - return; - gfs2_assert_withdraw(sdp, rgd == tmp_rgd); - - if (!rgd->rd_rg.rg_dinodes) - gfs2_consist_rgrpd(rgd); - rgd->rd_rg.rg_dinodes--; - rgd->rd_rg.rg_free++; - - gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); - gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); - - gfs2_statfs_change(sdp, 0, +1, -1); - gfs2_trans_add_rg(rgd); -} - - -void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip) -{ - gfs2_free_uninit_di(rgd, ip->i_num.no_addr); - gfs2_quota_change(ip, -1, ip->i_di.di_uid, ip->i_di.di_gid); - gfs2_meta_wipe(ip, ip->i_num.no_addr, 1); -} - -/** - * gfs2_rlist_add - add a RG to a list of RGs - * @sdp: the filesystem - * @rlist: the list of resource groups - * @block: the block - * - * Figure out what RG a block belongs to and add that RG to the list - * - * FIXME: Don't use NOFAIL - * - */ - -void gfs2_rlist_add(struct gfs2_sbd *sdp, struct gfs2_rgrp_list *rlist, - u64 block) -{ - struct gfs2_rgrpd *rgd; - struct gfs2_rgrpd **tmp; - unsigned int new_space; - unsigned int x; - - if (gfs2_assert_warn(sdp, !rlist->rl_ghs)) - return; - - rgd = gfs2_blk2rgrpd(sdp, block); - if (!rgd) { - if (gfs2_consist(sdp)) - fs_err(sdp, "block = %llu\n", (unsigned long long)block); - return; - } - - for (x = 0; x < rlist->rl_rgrps; x++) - if (rlist->rl_rgd[x] == rgd) - return; - - if (rlist->rl_rgrps == rlist->rl_space) { - new_space = rlist->rl_space + 10; - - tmp = kcalloc(new_space, sizeof(struct gfs2_rgrpd *), - GFP_NOFS | __GFP_NOFAIL); - - if (rlist->rl_rgd) { - memcpy(tmp, rlist->rl_rgd, - rlist->rl_space * sizeof(struct gfs2_rgrpd *)); - kfree(rlist->rl_rgd); - } - - rlist->rl_space = new_space; - rlist->rl_rgd = tmp; - } - - rlist->rl_rgd[rlist->rl_rgrps++] = rgd; -} - -/** - * gfs2_rlist_alloc - all RGs have been added to the rlist, now allocate - * and initialize an array of glock holders for them - * @rlist: the list of resource groups - * @state: the lock state to acquire the RG lock in - * @flags: the modifier flags for the holder structures - * - * FIXME: Don't use NOFAIL - * - */ - -void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist, unsigned int state, - int flags) -{ - unsigned int x; - - rlist->rl_ghs = kcalloc(rlist->rl_rgrps, sizeof(struct gfs2_holder), - GFP_NOFS | __GFP_NOFAIL); - for (x = 0; x < rlist->rl_rgrps; x++) - gfs2_holder_init(rlist->rl_rgd[x]->rd_gl, - state, flags, - &rlist->rl_ghs[x]); -} - -/** - * gfs2_rlist_free - free a resource group list - * @list: the list of resource groups - * - */ - -void gfs2_rlist_free(struct gfs2_rgrp_list *rlist) -{ - unsigned int x; - - kfree(rlist->rl_rgd); - - if (rlist->rl_ghs) { - for (x = 0; x < rlist->rl_rgrps; x++) - gfs2_holder_uninit(&rlist->rl_ghs[x]); - kfree(rlist->rl_ghs); - } -} - diff --git a/trunk/fs/gfs2/rgrp.h b/trunk/fs/gfs2/rgrp.h deleted file mode 100644 index 9eedfd12bfff..000000000000 --- a/trunk/fs/gfs2/rgrp.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __RGRP_DOT_H__ -#define __RGRP_DOT_H__ - -struct gfs2_rgrpd; -struct gfs2_sbd; -struct gfs2_holder; - -void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd); - -struct gfs2_rgrpd *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, u64 blk); -struct gfs2_rgrpd *gfs2_rgrpd_get_first(struct gfs2_sbd *sdp); -struct gfs2_rgrpd *gfs2_rgrpd_get_next(struct gfs2_rgrpd *rgd); - -void gfs2_clear_rgrpd(struct gfs2_sbd *sdp); -int gfs2_rindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ri_gh); - -int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd); -void gfs2_rgrp_bh_hold(struct gfs2_rgrpd *rgd); -void gfs2_rgrp_bh_put(struct gfs2_rgrpd *rgd); - -void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd); - -struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip); -static inline void gfs2_alloc_put(struct gfs2_inode *ip) -{ - return; /* Se we can see where ip->i_alloc is used */ -} - -int gfs2_inplace_reserve_i(struct gfs2_inode *ip, - char *file, unsigned int line); -#define gfs2_inplace_reserve(ip) \ -gfs2_inplace_reserve_i((ip), __FILE__, __LINE__) - -void gfs2_inplace_release(struct gfs2_inode *ip); - -unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block); - -u64 gfs2_alloc_data(struct gfs2_inode *ip); -u64 gfs2_alloc_meta(struct gfs2_inode *ip); -u64 gfs2_alloc_di(struct gfs2_inode *ip, u64 *generation); - -void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen); -void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen); -void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip); -void gfs2_unlink_di(struct inode *inode); - -struct gfs2_rgrp_list { - unsigned int rl_rgrps; - unsigned int rl_space; - struct gfs2_rgrpd **rl_rgd; - struct gfs2_holder *rl_ghs; -}; - -void gfs2_rlist_add(struct gfs2_sbd *sdp, struct gfs2_rgrp_list *rlist, - u64 block); -void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist, unsigned int state, - int flags); -void gfs2_rlist_free(struct gfs2_rgrp_list *rlist); - -#endif /* __RGRP_DOT_H__ */ diff --git a/trunk/fs/gfs2/super.c b/trunk/fs/gfs2/super.c deleted file mode 100644 index 6a78b1b32e25..000000000000 --- a/trunk/fs/gfs2/super.c +++ /dev/null @@ -1,976 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "bmap.h" -#include "dir.h" -#include "glock.h" -#include "glops.h" -#include "inode.h" -#include "log.h" -#include "meta_io.h" -#include "quota.h" -#include "recovery.h" -#include "rgrp.h" -#include "super.h" -#include "trans.h" -#include "util.h" - -static const u32 gfs2_old_fs_formats[] = { - 0 -}; - -static const u32 gfs2_old_multihost_formats[] = { - 0 -}; - -/** - * gfs2_tune_init - Fill a gfs2_tune structure with default values - * @gt: tune - * - */ - -void gfs2_tune_init(struct gfs2_tune *gt) -{ - spin_lock_init(>->gt_spin); - - gt->gt_ilimit = 100; - gt->gt_ilimit_tries = 3; - gt->gt_ilimit_min = 1; - gt->gt_demote_secs = 300; - gt->gt_incore_log_blocks = 1024; - gt->gt_log_flush_secs = 60; - gt->gt_jindex_refresh_secs = 60; - gt->gt_scand_secs = 15; - gt->gt_recoverd_secs = 60; - gt->gt_logd_secs = 1; - gt->gt_quotad_secs = 5; - gt->gt_quota_simul_sync = 64; - gt->gt_quota_warn_period = 10; - gt->gt_quota_scale_num = 1; - gt->gt_quota_scale_den = 1; - gt->gt_quota_cache_secs = 300; - gt->gt_quota_quantum = 60; - gt->gt_atime_quantum = 3600; - gt->gt_new_files_jdata = 0; - gt->gt_new_files_directio = 0; - gt->gt_max_atomic_write = 4 << 20; - gt->gt_max_readahead = 1 << 18; - gt->gt_lockdump_size = 131072; - gt->gt_stall_secs = 600; - gt->gt_complain_secs = 10; - gt->gt_reclaim_limit = 5000; - gt->gt_entries_per_readdir = 32; - gt->gt_prefetch_secs = 10; - gt->gt_greedy_default = HZ / 10; - gt->gt_greedy_quantum = HZ / 40; - gt->gt_greedy_max = HZ / 4; - gt->gt_statfs_quantum = 30; - gt->gt_statfs_slow = 0; -} - -/** - * gfs2_check_sb - Check superblock - * @sdp: the filesystem - * @sb: The superblock - * @silent: Don't print a message if the check fails - * - * Checks the version code of the FS is one that we understand how to - * read and that the sizes of the various on-disk structures have not - * changed. - */ - -int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb *sb, int silent) -{ - unsigned int x; - - if (sb->sb_header.mh_magic != GFS2_MAGIC || - sb->sb_header.mh_type != GFS2_METATYPE_SB) { - if (!silent) - printk(KERN_WARNING "GFS2: not a GFS2 filesystem\n"); - return -EINVAL; - } - - /* If format numbers match exactly, we're done. */ - - if (sb->sb_fs_format == GFS2_FORMAT_FS && - sb->sb_multihost_format == GFS2_FORMAT_MULTI) - return 0; - - if (sb->sb_fs_format != GFS2_FORMAT_FS) { - for (x = 0; gfs2_old_fs_formats[x]; x++) - if (gfs2_old_fs_formats[x] == sb->sb_fs_format) - break; - - if (!gfs2_old_fs_formats[x]) { - printk(KERN_WARNING - "GFS2: code version (%u, %u) is incompatible " - "with ondisk format (%u, %u)\n", - GFS2_FORMAT_FS, GFS2_FORMAT_MULTI, - sb->sb_fs_format, sb->sb_multihost_format); - printk(KERN_WARNING - "GFS2: I don't know how to upgrade this FS\n"); - return -EINVAL; - } - } - - if (sb->sb_multihost_format != GFS2_FORMAT_MULTI) { - for (x = 0; gfs2_old_multihost_formats[x]; x++) - if (gfs2_old_multihost_formats[x] == - sb->sb_multihost_format) - break; - - if (!gfs2_old_multihost_formats[x]) { - printk(KERN_WARNING - "GFS2: code version (%u, %u) is incompatible " - "with ondisk format (%u, %u)\n", - GFS2_FORMAT_FS, GFS2_FORMAT_MULTI, - sb->sb_fs_format, sb->sb_multihost_format); - printk(KERN_WARNING - "GFS2: I don't know how to upgrade this FS\n"); - return -EINVAL; - } - } - - if (!sdp->sd_args.ar_upgrade) { - printk(KERN_WARNING - "GFS2: code version (%u, %u) is incompatible " - "with ondisk format (%u, %u)\n", - GFS2_FORMAT_FS, GFS2_FORMAT_MULTI, - sb->sb_fs_format, sb->sb_multihost_format); - printk(KERN_INFO - "GFS2: Use the \"upgrade\" mount option to upgrade " - "the FS\n"); - printk(KERN_INFO "GFS2: See the manual for more details\n"); - return -EINVAL; - } - - return 0; -} - - -static int end_bio_io_page(struct bio *bio, unsigned int bytes_done, int error) -{ - struct page *page = bio->bi_private; - if (bio->bi_size) - return 1; - - if (!error) - SetPageUptodate(page); - else - printk(KERN_WARNING "gfs2: error %d reading superblock\n", error); - unlock_page(page); - return 0; -} - -struct page *gfs2_read_super(struct super_block *sb, sector_t sector) -{ - struct page *page; - struct bio *bio; - - page = alloc_page(GFP_KERNEL); - if (unlikely(!page)) - return NULL; - - ClearPageUptodate(page); - ClearPageDirty(page); - lock_page(page); - - bio = bio_alloc(GFP_KERNEL, 1); - if (unlikely(!bio)) { - __free_page(page); - return NULL; - } - - bio->bi_sector = sector; - bio->bi_bdev = sb->s_bdev; - bio_add_page(bio, page, PAGE_SIZE, 0); - - bio->bi_end_io = end_bio_io_page; - bio->bi_private = page; - submit_bio(READ_SYNC | (1 << BIO_RW_META), bio); - wait_on_page_locked(page); - bio_put(bio); - if (!PageUptodate(page)) { - __free_page(page); - return NULL; - } - return page; -} - -/** - * gfs2_read_sb - Read super block - * @sdp: The GFS2 superblock - * @gl: the glock for the superblock (assumed to be held) - * @silent: Don't print message if mount fails - * - */ - -int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent) -{ - u32 hash_blocks, ind_blocks, leaf_blocks; - u32 tmp_blocks; - unsigned int x; - int error; - struct page *page; - char *sb; - - page = gfs2_read_super(sdp->sd_vfs, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); - if (!page) { - if (!silent) - fs_err(sdp, "can't read superblock\n"); - return -EIO; - } - sb = kmap(page); - gfs2_sb_in(&sdp->sd_sb, sb); - kunmap(page); - __free_page(page); - - error = gfs2_check_sb(sdp, &sdp->sd_sb, silent); - if (error) - return error; - - sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift - - GFS2_BASIC_BLOCK_SHIFT; - sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift; - sdp->sd_diptrs = (sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_dinode)) / sizeof(u64); - sdp->sd_inptrs = (sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_meta_header)) / sizeof(u64); - sdp->sd_jbsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header); - sdp->sd_hash_bsize = sdp->sd_sb.sb_bsize / 2; - sdp->sd_hash_bsize_shift = sdp->sd_sb.sb_bsize_shift - 1; - sdp->sd_hash_ptrs = sdp->sd_hash_bsize / sizeof(u64); - sdp->sd_qc_per_block = (sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_meta_header)) / - sizeof(struct gfs2_quota_change); - - /* Compute maximum reservation required to add a entry to a directory */ - - hash_blocks = DIV_ROUND_UP(sizeof(u64) * (1 << GFS2_DIR_MAX_DEPTH), - sdp->sd_jbsize); - - ind_blocks = 0; - for (tmp_blocks = hash_blocks; tmp_blocks > sdp->sd_diptrs;) { - tmp_blocks = DIV_ROUND_UP(tmp_blocks, sdp->sd_inptrs); - ind_blocks += tmp_blocks; - } - - leaf_blocks = 2 + GFS2_DIR_MAX_DEPTH; - - sdp->sd_max_dirres = hash_blocks + ind_blocks + leaf_blocks; - - sdp->sd_heightsize[0] = sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_dinode); - sdp->sd_heightsize[1] = sdp->sd_sb.sb_bsize * sdp->sd_diptrs; - for (x = 2;; x++) { - u64 space, d; - u32 m; - - space = sdp->sd_heightsize[x - 1] * sdp->sd_inptrs; - d = space; - m = do_div(d, sdp->sd_inptrs); - - if (d != sdp->sd_heightsize[x - 1] || m) - break; - sdp->sd_heightsize[x] = space; - } - sdp->sd_max_height = x; - gfs2_assert(sdp, sdp->sd_max_height <= GFS2_MAX_META_HEIGHT); - - sdp->sd_jheightsize[0] = sdp->sd_sb.sb_bsize - - sizeof(struct gfs2_dinode); - sdp->sd_jheightsize[1] = sdp->sd_jbsize * sdp->sd_diptrs; - for (x = 2;; x++) { - u64 space, d; - u32 m; - - space = sdp->sd_jheightsize[x - 1] * sdp->sd_inptrs; - d = space; - m = do_div(d, sdp->sd_inptrs); - - if (d != sdp->sd_jheightsize[x - 1] || m) - break; - sdp->sd_jheightsize[x] = space; - } - sdp->sd_max_jheight = x; - gfs2_assert(sdp, sdp->sd_max_jheight <= GFS2_MAX_META_HEIGHT); - - return 0; -} - -/** - * gfs2_jindex_hold - Grab a lock on the jindex - * @sdp: The GFS2 superblock - * @ji_gh: the holder for the jindex glock - * - * This is very similar to the gfs2_rindex_hold() function, except that - * in general we hold the jindex lock for longer periods of time and - * we grab it far less frequently (in general) then the rgrp lock. - * - * Returns: errno - */ - -int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh) -{ - struct gfs2_inode *dip = GFS2_I(sdp->sd_jindex); - struct qstr name; - char buf[20]; - struct gfs2_jdesc *jd; - int error; - - name.name = buf; - - mutex_lock(&sdp->sd_jindex_mutex); - - for (;;) { - error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, - GL_LOCAL_EXCL, ji_gh); - if (error) - break; - - name.len = sprintf(buf, "journal%u", sdp->sd_journals); - name.hash = gfs2_disk_hash(name.name, name.len); - - error = gfs2_dir_search(sdp->sd_jindex, &name, NULL, NULL); - if (error == -ENOENT) { - error = 0; - break; - } - - gfs2_glock_dq_uninit(ji_gh); - - if (error) - break; - - error = -ENOMEM; - jd = kzalloc(sizeof(struct gfs2_jdesc), GFP_KERNEL); - if (!jd) - break; - - jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1, NULL); - if (!jd->jd_inode || IS_ERR(jd->jd_inode)) { - if (!jd->jd_inode) - error = -ENOENT; - else - error = PTR_ERR(jd->jd_inode); - kfree(jd); - break; - } - - spin_lock(&sdp->sd_jindex_spin); - jd->jd_jid = sdp->sd_journals++; - list_add_tail(&jd->jd_list, &sdp->sd_jindex_list); - spin_unlock(&sdp->sd_jindex_spin); - } - - mutex_unlock(&sdp->sd_jindex_mutex); - - return error; -} - -/** - * gfs2_jindex_free - Clear all the journal index information - * @sdp: The GFS2 superblock - * - */ - -void gfs2_jindex_free(struct gfs2_sbd *sdp) -{ - struct list_head list; - struct gfs2_jdesc *jd; - - spin_lock(&sdp->sd_jindex_spin); - list_add(&list, &sdp->sd_jindex_list); - list_del_init(&sdp->sd_jindex_list); - sdp->sd_journals = 0; - spin_unlock(&sdp->sd_jindex_spin); - - while (!list_empty(&list)) { - jd = list_entry(list.next, struct gfs2_jdesc, jd_list); - list_del(&jd->jd_list); - iput(jd->jd_inode); - kfree(jd); - } -} - -static struct gfs2_jdesc *jdesc_find_i(struct list_head *head, unsigned int jid) -{ - struct gfs2_jdesc *jd; - int found = 0; - - list_for_each_entry(jd, head, jd_list) { - if (jd->jd_jid == jid) { - found = 1; - break; - } - } - - if (!found) - jd = NULL; - - return jd; -} - -struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid) -{ - struct gfs2_jdesc *jd; - - spin_lock(&sdp->sd_jindex_spin); - jd = jdesc_find_i(&sdp->sd_jindex_list, jid); - spin_unlock(&sdp->sd_jindex_spin); - - return jd; -} - -void gfs2_jdesc_make_dirty(struct gfs2_sbd *sdp, unsigned int jid) -{ - struct gfs2_jdesc *jd; - - spin_lock(&sdp->sd_jindex_spin); - jd = jdesc_find_i(&sdp->sd_jindex_list, jid); - if (jd) - jd->jd_dirty = 1; - spin_unlock(&sdp->sd_jindex_spin); -} - -struct gfs2_jdesc *gfs2_jdesc_find_dirty(struct gfs2_sbd *sdp) -{ - struct gfs2_jdesc *jd; - int found = 0; - - spin_lock(&sdp->sd_jindex_spin); - - list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) { - if (jd->jd_dirty) { - jd->jd_dirty = 0; - found = 1; - break; - } - } - spin_unlock(&sdp->sd_jindex_spin); - - if (!found) - jd = NULL; - - return jd; -} - -int gfs2_jdesc_check(struct gfs2_jdesc *jd) -{ - struct gfs2_inode *ip = GFS2_I(jd->jd_inode); - struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - int ar; - int error; - - if (ip->i_di.di_size < (8 << 20) || ip->i_di.di_size > (1 << 30) || - (ip->i_di.di_size & (sdp->sd_sb.sb_bsize - 1))) { - gfs2_consist_inode(ip); - return -EIO; - } - jd->jd_blocks = ip->i_di.di_size >> sdp->sd_sb.sb_bsize_shift; - - error = gfs2_write_alloc_required(ip, 0, ip->i_di.di_size, &ar); - if (!error && ar) { - gfs2_consist_inode(ip); - error = -EIO; - } - - return error; -} - -/** - * gfs2_make_fs_rw - Turn a Read-Only FS into a Read-Write one - * @sdp: the filesystem - * - * Returns: errno - */ - -int gfs2_make_fs_rw(struct gfs2_sbd *sdp) -{ - struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode); - struct gfs2_glock *j_gl = ip->i_gl; - struct gfs2_holder t_gh; - struct gfs2_log_header head; - int error; - - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, - GL_LOCAL_EXCL, &t_gh); - if (error) - return error; - - gfs2_meta_cache_flush(ip); - j_gl->gl_ops->go_inval(j_gl, DIO_METADATA | DIO_DATA); - - error = gfs2_find_jhead(sdp->sd_jdesc, &head); - if (error) - goto fail; - - if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { - gfs2_consist(sdp); - error = -EIO; - goto fail; - } - - /* Initialize some head of the log stuff */ - sdp->sd_log_sequence = head.lh_sequence + 1; - gfs2_log_pointers_init(sdp, head.lh_blkno); - - error = gfs2_quota_init(sdp); - if (error) - goto fail; - - set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - - gfs2_glock_dq_uninit(&t_gh); - - return 0; - -fail: - t_gh.gh_flags |= GL_NOCACHE; - gfs2_glock_dq_uninit(&t_gh); - - return error; -} - -/** - * gfs2_make_fs_ro - Turn a Read-Write FS into a Read-Only one - * @sdp: the filesystem - * - * Returns: errno - */ - -int gfs2_make_fs_ro(struct gfs2_sbd *sdp) -{ - struct gfs2_holder t_gh; - int error; - - gfs2_quota_sync(sdp); - gfs2_statfs_sync(sdp); - - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, - GL_LOCAL_EXCL | GL_NOCACHE, - &t_gh); - if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) - return error; - - gfs2_meta_syncfs(sdp); - gfs2_log_shutdown(sdp); - - clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - - if (t_gh.gh_gl) - gfs2_glock_dq_uninit(&t_gh); - - gfs2_quota_cleanup(sdp); - - return error; -} - -int gfs2_statfs_init(struct gfs2_sbd *sdp) -{ - struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); - struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master; - struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); - struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local; - struct buffer_head *m_bh, *l_bh; - struct gfs2_holder gh; - int error; - - error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE, - &gh); - if (error) - return error; - - error = gfs2_meta_inode_buffer(m_ip, &m_bh); - if (error) - goto out; - - if (sdp->sd_args.ar_spectator) { - spin_lock(&sdp->sd_statfs_spin); - gfs2_statfs_change_in(m_sc, m_bh->b_data + - sizeof(struct gfs2_dinode)); - spin_unlock(&sdp->sd_statfs_spin); - } else { - error = gfs2_meta_inode_buffer(l_ip, &l_bh); - if (error) - goto out_m_bh; - - spin_lock(&sdp->sd_statfs_spin); - gfs2_statfs_change_in(m_sc, m_bh->b_data + - sizeof(struct gfs2_dinode)); - gfs2_statfs_change_in(l_sc, l_bh->b_data + - sizeof(struct gfs2_dinode)); - spin_unlock(&sdp->sd_statfs_spin); - - brelse(l_bh); - } - -out_m_bh: - brelse(m_bh); -out: - gfs2_glock_dq_uninit(&gh); - return 0; -} - -void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free, - s64 dinodes) -{ - struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); - struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local; - struct buffer_head *l_bh; - int error; - - error = gfs2_meta_inode_buffer(l_ip, &l_bh); - if (error) - return; - - mutex_lock(&sdp->sd_statfs_mutex); - gfs2_trans_add_bh(l_ip->i_gl, l_bh, 1); - mutex_unlock(&sdp->sd_statfs_mutex); - - spin_lock(&sdp->sd_statfs_spin); - l_sc->sc_total += total; - l_sc->sc_free += free; - l_sc->sc_dinodes += dinodes; - gfs2_statfs_change_out(l_sc, l_bh->b_data + sizeof(struct gfs2_dinode)); - spin_unlock(&sdp->sd_statfs_spin); - - brelse(l_bh); -} - -int gfs2_statfs_sync(struct gfs2_sbd *sdp) -{ - struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); - struct gfs2_inode *l_ip = GFS2_I(sdp->sd_sc_inode); - struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master; - struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local; - struct gfs2_holder gh; - struct buffer_head *m_bh, *l_bh; - int error; - - error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE, - &gh); - if (error) - return error; - - error = gfs2_meta_inode_buffer(m_ip, &m_bh); - if (error) - goto out; - - spin_lock(&sdp->sd_statfs_spin); - gfs2_statfs_change_in(m_sc, m_bh->b_data + - sizeof(struct gfs2_dinode)); - if (!l_sc->sc_total && !l_sc->sc_free && !l_sc->sc_dinodes) { - spin_unlock(&sdp->sd_statfs_spin); - goto out_bh; - } - spin_unlock(&sdp->sd_statfs_spin); - - error = gfs2_meta_inode_buffer(l_ip, &l_bh); - if (error) - goto out_bh; - - error = gfs2_trans_begin(sdp, 2 * RES_DINODE, 0); - if (error) - goto out_bh2; - - mutex_lock(&sdp->sd_statfs_mutex); - gfs2_trans_add_bh(l_ip->i_gl, l_bh, 1); - mutex_unlock(&sdp->sd_statfs_mutex); - - spin_lock(&sdp->sd_statfs_spin); - m_sc->sc_total += l_sc->sc_total; - m_sc->sc_free += l_sc->sc_free; - m_sc->sc_dinodes += l_sc->sc_dinodes; - memset(l_sc, 0, sizeof(struct gfs2_statfs_change)); - memset(l_bh->b_data + sizeof(struct gfs2_dinode), - 0, sizeof(struct gfs2_statfs_change)); - spin_unlock(&sdp->sd_statfs_spin); - - gfs2_trans_add_bh(m_ip->i_gl, m_bh, 1); - gfs2_statfs_change_out(m_sc, m_bh->b_data + sizeof(struct gfs2_dinode)); - - gfs2_trans_end(sdp); - -out_bh2: - brelse(l_bh); -out_bh: - brelse(m_bh); -out: - gfs2_glock_dq_uninit(&gh); - return error; -} - -/** - * gfs2_statfs_i - Do a statfs - * @sdp: the filesystem - * @sg: the sg structure - * - * Returns: errno - */ - -int gfs2_statfs_i(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc) -{ - struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master; - struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local; - - spin_lock(&sdp->sd_statfs_spin); - - *sc = *m_sc; - sc->sc_total += l_sc->sc_total; - sc->sc_free += l_sc->sc_free; - sc->sc_dinodes += l_sc->sc_dinodes; - - spin_unlock(&sdp->sd_statfs_spin); - - if (sc->sc_free < 0) - sc->sc_free = 0; - if (sc->sc_free > sc->sc_total) - sc->sc_free = sc->sc_total; - if (sc->sc_dinodes < 0) - sc->sc_dinodes = 0; - - return 0; -} - -/** - * statfs_fill - fill in the sg for a given RG - * @rgd: the RG - * @sc: the sc structure - * - * Returns: 0 on success, -ESTALE if the LVB is invalid - */ - -static int statfs_slow_fill(struct gfs2_rgrpd *rgd, - struct gfs2_statfs_change *sc) -{ - gfs2_rgrp_verify(rgd); - sc->sc_total += rgd->rd_ri.ri_data; - sc->sc_free += rgd->rd_rg.rg_free; - sc->sc_dinodes += rgd->rd_rg.rg_dinodes; - return 0; -} - -/** - * gfs2_statfs_slow - Stat a filesystem using asynchronous locking - * @sdp: the filesystem - * @sc: the sc info that will be returned - * - * Any error (other than a signal) will cause this routine to fall back - * to the synchronous version. - * - * FIXME: This really shouldn't busy wait like this. - * - * Returns: errno - */ - -int gfs2_statfs_slow(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc) -{ - struct gfs2_holder ri_gh; - struct gfs2_rgrpd *rgd_next; - struct gfs2_holder *gha, *gh; - unsigned int slots = 64; - unsigned int x; - int done; - int error = 0, err; - - memset(sc, 0, sizeof(struct gfs2_statfs_change)); - gha = kcalloc(slots, sizeof(struct gfs2_holder), GFP_KERNEL); - if (!gha) - return -ENOMEM; - - error = gfs2_rindex_hold(sdp, &ri_gh); - if (error) - goto out; - - rgd_next = gfs2_rgrpd_get_first(sdp); - - for (;;) { - done = 1; - - for (x = 0; x < slots; x++) { - gh = gha + x; - - if (gh->gh_gl && gfs2_glock_poll(gh)) { - err = gfs2_glock_wait(gh); - if (err) { - gfs2_holder_uninit(gh); - error = err; - } else { - if (!error) - error = statfs_slow_fill( - gh->gh_gl->gl_object, sc); - gfs2_glock_dq_uninit(gh); - } - } - - if (gh->gh_gl) - done = 0; - else if (rgd_next && !error) { - error = gfs2_glock_nq_init(rgd_next->rd_gl, - LM_ST_SHARED, - GL_ASYNC, - gh); - rgd_next = gfs2_rgrpd_get_next(rgd_next); - done = 0; - } - - if (signal_pending(current)) - error = -ERESTARTSYS; - } - - if (done) - break; - - yield(); - } - - gfs2_glock_dq_uninit(&ri_gh); - -out: - kfree(gha); - return error; -} - -struct lfcc { - struct list_head list; - struct gfs2_holder gh; -}; - -/** - * gfs2_lock_fs_check_clean - Stop all writes to the FS and check that all - * journals are clean - * @sdp: the file system - * @state: the state to put the transaction lock into - * @t_gh: the hold on the transaction lock - * - * Returns: errno - */ - -static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp, - struct gfs2_holder *t_gh) -{ - struct gfs2_inode *ip; - struct gfs2_holder ji_gh; - struct gfs2_jdesc *jd; - struct lfcc *lfcc; - LIST_HEAD(list); - struct gfs2_log_header lh; - int error; - - error = gfs2_jindex_hold(sdp, &ji_gh); - if (error) - return error; - - list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) { - lfcc = kmalloc(sizeof(struct lfcc), GFP_KERNEL); - if (!lfcc) { - error = -ENOMEM; - goto out; - } - ip = GFS2_I(jd->jd_inode); - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &lfcc->gh); - if (error) { - kfree(lfcc); - goto out; - } - list_add(&lfcc->list, &list); - } - - error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_DEFERRED, - LM_FLAG_PRIORITY | GL_NOCACHE, - t_gh); - - list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) { - error = gfs2_jdesc_check(jd); - if (error) - break; - error = gfs2_find_jhead(jd, &lh); - if (error) - break; - if (!(lh.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { - error = -EBUSY; - break; - } - } - - if (error) - gfs2_glock_dq_uninit(t_gh); - -out: - while (!list_empty(&list)) { - lfcc = list_entry(list.next, struct lfcc, list); - list_del(&lfcc->list); - gfs2_glock_dq_uninit(&lfcc->gh); - kfree(lfcc); - } - gfs2_glock_dq_uninit(&ji_gh); - return error; -} - -/** - * gfs2_freeze_fs - freezes the file system - * @sdp: the file system - * - * This function flushes data and meta data for all machines by - * aquiring the transaction log exclusively. All journals are - * ensured to be in a clean state as well. - * - * Returns: errno - */ - -int gfs2_freeze_fs(struct gfs2_sbd *sdp) -{ - int error = 0; - - mutex_lock(&sdp->sd_freeze_lock); - - if (!sdp->sd_freeze_count++) { - error = gfs2_lock_fs_check_clean(sdp, &sdp->sd_freeze_gh); - if (error) - sdp->sd_freeze_count--; - } - - mutex_unlock(&sdp->sd_freeze_lock); - - return error; -} - -/** - * gfs2_unfreeze_fs - unfreezes the file system - * @sdp: the file system - * - * This function allows the file system to proceed by unlocking - * the exclusively held transaction lock. Other GFS2 nodes are - * now free to acquire the lock shared and go on with their lives. - * - */ - -void gfs2_unfreeze_fs(struct gfs2_sbd *sdp) -{ - mutex_lock(&sdp->sd_freeze_lock); - - if (sdp->sd_freeze_count && !--sdp->sd_freeze_count) - gfs2_glock_dq_uninit(&sdp->sd_freeze_gh); - - mutex_unlock(&sdp->sd_freeze_lock); -} - diff --git a/trunk/fs/gfs2/super.h b/trunk/fs/gfs2/super.h deleted file mode 100644 index 5bb443ae0f59..000000000000 --- a/trunk/fs/gfs2/super.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __SUPER_DOT_H__ -#define __SUPER_DOT_H__ - -#include "incore.h" - -void gfs2_tune_init(struct gfs2_tune *gt); - -int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb *sb, int silent); -int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent); -struct page *gfs2_read_super(struct super_block *sb, sector_t sector); - -static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp) -{ - unsigned int x; - spin_lock(&sdp->sd_jindex_spin); - x = sdp->sd_journals; - spin_unlock(&sdp->sd_jindex_spin); - return x; -} - -int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh); -void gfs2_jindex_free(struct gfs2_sbd *sdp); - -struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid); -void gfs2_jdesc_make_dirty(struct gfs2_sbd *sdp, unsigned int jid); -struct gfs2_jdesc *gfs2_jdesc_find_dirty(struct gfs2_sbd *sdp); -int gfs2_jdesc_check(struct gfs2_jdesc *jd); - -int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename, - struct gfs2_inode **ipp); - -int gfs2_make_fs_rw(struct gfs2_sbd *sdp); -int gfs2_make_fs_ro(struct gfs2_sbd *sdp); - -int gfs2_statfs_init(struct gfs2_sbd *sdp); -void gfs2_statfs_change(struct gfs2_sbd *sdp, - s64 total, s64 free, s64 dinodes); -int gfs2_statfs_sync(struct gfs2_sbd *sdp); -int gfs2_statfs_i(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc); -int gfs2_statfs_slow(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc); - -int gfs2_freeze_fs(struct gfs2_sbd *sdp); -void gfs2_unfreeze_fs(struct gfs2_sbd *sdp); - -#endif /* __SUPER_DOT_H__ */ - diff --git a/trunk/fs/gfs2/sys.c b/trunk/fs/gfs2/sys.c deleted file mode 100644 index 0e0ec988f731..000000000000 --- a/trunk/fs/gfs2/sys.c +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "lm.h" -#include "sys.h" -#include "super.h" -#include "glock.h" -#include "quota.h" -#include "util.h" - -char *gfs2_sys_margs; -spinlock_t gfs2_sys_margs_lock; - -static ssize_t id_show(struct gfs2_sbd *sdp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", sdp->sd_vfs->s_id); -} - -static ssize_t fsname_show(struct gfs2_sbd *sdp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", sdp->sd_fsname); -} - -static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf) -{ - unsigned int count; - - mutex_lock(&sdp->sd_freeze_lock); - count = sdp->sd_freeze_count; - mutex_unlock(&sdp->sd_freeze_lock); - - return snprintf(buf, PAGE_SIZE, "%u\n", count); -} - -static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len) -{ - ssize_t ret = len; - int error = 0; - int n = simple_strtol(buf, NULL, 0); - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - switch (n) { - case 0: - gfs2_unfreeze_fs(sdp); - break; - case 1: - error = gfs2_freeze_fs(sdp); - break; - default: - ret = -EINVAL; - } - - if (error) - fs_warn(sdp, "freeze %d error %d", n, error); - - return ret; -} - -static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf) -{ - unsigned int b = test_bit(SDF_SHUTDOWN, &sdp->sd_flags); - return snprintf(buf, PAGE_SIZE, "%u\n", b); -} - -static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len) -{ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (simple_strtol(buf, NULL, 0) != 1) - return -EINVAL; - - gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: withdrawing from cluster at user's request\n", - sdp->sd_fsname); - return len; -} - -static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf, - size_t len) -{ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (simple_strtol(buf, NULL, 0) != 1) - return -EINVAL; - - gfs2_statfs_sync(sdp); - return len; -} - -static ssize_t shrink_store(struct gfs2_sbd *sdp, const char *buf, size_t len) -{ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (simple_strtol(buf, NULL, 0) != 1) - return -EINVAL; - - gfs2_gl_hash_clear(sdp, NO_WAIT); - return len; -} - -static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf, - size_t len) -{ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (simple_strtol(buf, NULL, 0) != 1) - return -EINVAL; - - gfs2_quota_sync(sdp); - return len; -} - -static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, - size_t len) -{ - u32 id; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - id = simple_strtoul(buf, NULL, 0); - - gfs2_quota_refresh(sdp, 1, id); - return len; -} - -static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, - size_t len) -{ - u32 id; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - id = simple_strtoul(buf, NULL, 0); - - gfs2_quota_refresh(sdp, 0, id); - return len; -} - -struct gfs2_attr { - struct attribute attr; - ssize_t (*show)(struct gfs2_sbd *, char *); - ssize_t (*store)(struct gfs2_sbd *, const char *, size_t); -}; - -#define GFS2_ATTR(name, mode, show, store) \ -static struct gfs2_attr gfs2_attr_##name = __ATTR(name, mode, show, store) - -GFS2_ATTR(id, 0444, id_show, NULL); -GFS2_ATTR(fsname, 0444, fsname_show, NULL); -GFS2_ATTR(freeze, 0644, freeze_show, freeze_store); -GFS2_ATTR(shrink, 0200, NULL, shrink_store); -GFS2_ATTR(withdraw, 0644, withdraw_show, withdraw_store); -GFS2_ATTR(statfs_sync, 0200, NULL, statfs_sync_store); -GFS2_ATTR(quota_sync, 0200, NULL, quota_sync_store); -GFS2_ATTR(quota_refresh_user, 0200, NULL, quota_refresh_user_store); -GFS2_ATTR(quota_refresh_group, 0200, NULL, quota_refresh_group_store); - -static struct attribute *gfs2_attrs[] = { - &gfs2_attr_id.attr, - &gfs2_attr_fsname.attr, - &gfs2_attr_freeze.attr, - &gfs2_attr_shrink.attr, - &gfs2_attr_withdraw.attr, - &gfs2_attr_statfs_sync.attr, - &gfs2_attr_quota_sync.attr, - &gfs2_attr_quota_refresh_user.attr, - &gfs2_attr_quota_refresh_group.attr, - NULL, -}; - -static ssize_t gfs2_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj); - struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr); - return a->show ? a->show(sdp, buf) : 0; -} - -static ssize_t gfs2_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t len) -{ - struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj); - struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr); - return a->store ? a->store(sdp, buf, len) : len; -} - -static struct sysfs_ops gfs2_attr_ops = { - .show = gfs2_attr_show, - .store = gfs2_attr_store, -}; - -static struct kobj_type gfs2_ktype = { - .default_attrs = gfs2_attrs, - .sysfs_ops = &gfs2_attr_ops, -}; - -static struct kset gfs2_kset = { - .subsys = &fs_subsys, - .kobj = {.name = "gfs2"}, - .ktype = &gfs2_ktype, -}; - -/* - * display struct lm_lockstruct fields - */ - -struct lockstruct_attr { - struct attribute attr; - ssize_t (*show)(struct gfs2_sbd *, char *); -}; - -#define LOCKSTRUCT_ATTR(name, fmt) \ -static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \ -{ \ - return snprintf(buf, PAGE_SIZE, fmt, sdp->sd_lockstruct.ls_##name); \ -} \ -static struct lockstruct_attr lockstruct_attr_##name = __ATTR_RO(name) - -LOCKSTRUCT_ATTR(jid, "%u\n"); -LOCKSTRUCT_ATTR(first, "%u\n"); -LOCKSTRUCT_ATTR(lvb_size, "%u\n"); -LOCKSTRUCT_ATTR(flags, "%d\n"); - -static struct attribute *lockstruct_attrs[] = { - &lockstruct_attr_jid.attr, - &lockstruct_attr_first.attr, - &lockstruct_attr_lvb_size.attr, - &lockstruct_attr_flags.attr, - NULL, -}; - -/* - * display struct gfs2_args fields - */ - -struct args_attr { - struct attribute attr; - ssize_t (*show)(struct gfs2_sbd *, char *); -}; - -#define ARGS_ATTR(name, fmt) \ -static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \ -{ \ - return snprintf(buf, PAGE_SIZE, fmt, sdp->sd_args.ar_##name); \ -} \ -static struct args_attr args_attr_##name = __ATTR_RO(name) - -ARGS_ATTR(lockproto, "%s\n"); -ARGS_ATTR(locktable, "%s\n"); -ARGS_ATTR(hostdata, "%s\n"); -ARGS_ATTR(spectator, "%d\n"); -ARGS_ATTR(ignore_local_fs, "%d\n"); -ARGS_ATTR(localcaching, "%d\n"); -ARGS_ATTR(localflocks, "%d\n"); -ARGS_ATTR(debug, "%d\n"); -ARGS_ATTR(upgrade, "%d\n"); -ARGS_ATTR(num_glockd, "%u\n"); -ARGS_ATTR(posix_acl, "%d\n"); -ARGS_ATTR(quota, "%u\n"); -ARGS_ATTR(suiddir, "%d\n"); -ARGS_ATTR(data, "%d\n"); - -/* one oddball doesn't fit the macro mold */ -static ssize_t noatime_show(struct gfs2_sbd *sdp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", - !!test_bit(SDF_NOATIME, &sdp->sd_flags)); -} -static struct args_attr args_attr_noatime = __ATTR_RO(noatime); - -static struct attribute *args_attrs[] = { - &args_attr_lockproto.attr, - &args_attr_locktable.attr, - &args_attr_hostdata.attr, - &args_attr_spectator.attr, - &args_attr_ignore_local_fs.attr, - &args_attr_localcaching.attr, - &args_attr_localflocks.attr, - &args_attr_debug.attr, - &args_attr_upgrade.attr, - &args_attr_num_glockd.attr, - &args_attr_posix_acl.attr, - &args_attr_quota.attr, - &args_attr_suiddir.attr, - &args_attr_data.attr, - &args_attr_noatime.attr, - NULL, -}; - -/* - * display counters from superblock - */ - -struct counters_attr { - struct attribute attr; - ssize_t (*show)(struct gfs2_sbd *, char *); -}; - -#define COUNTERS_ATTR(name, fmt) \ -static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \ -{ \ - return snprintf(buf, PAGE_SIZE, fmt, \ - (unsigned int)atomic_read(&sdp->sd_##name)); \ -} \ -static struct counters_attr counters_attr_##name = __ATTR_RO(name) - -COUNTERS_ATTR(glock_count, "%u\n"); -COUNTERS_ATTR(glock_held_count, "%u\n"); -COUNTERS_ATTR(inode_count, "%u\n"); -COUNTERS_ATTR(reclaimed, "%u\n"); - -static struct attribute *counters_attrs[] = { - &counters_attr_glock_count.attr, - &counters_attr_glock_held_count.attr, - &counters_attr_inode_count.attr, - &counters_attr_reclaimed.attr, - NULL, -}; - -/* - * get and set struct gfs2_tune fields - */ - -static ssize_t quota_scale_show(struct gfs2_sbd *sdp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%u %u\n", - sdp->sd_tune.gt_quota_scale_num, - sdp->sd_tune.gt_quota_scale_den); -} - -static ssize_t quota_scale_store(struct gfs2_sbd *sdp, const char *buf, - size_t len) -{ - struct gfs2_tune *gt = &sdp->sd_tune; - unsigned int x, y; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (sscanf(buf, "%u %u", &x, &y) != 2 || !y) - return -EINVAL; - - spin_lock(>->gt_spin); - gt->gt_quota_scale_num = x; - gt->gt_quota_scale_den = y; - spin_unlock(>->gt_spin); - return len; -} - -static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field, - int check_zero, const char *buf, size_t len) -{ - struct gfs2_tune *gt = &sdp->sd_tune; - unsigned int x; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - x = simple_strtoul(buf, NULL, 0); - - if (check_zero && !x) - return -EINVAL; - - spin_lock(>->gt_spin); - *field = x; - spin_unlock(>->gt_spin); - return len; -} - -struct tune_attr { - struct attribute attr; - ssize_t (*show)(struct gfs2_sbd *, char *); - ssize_t (*store)(struct gfs2_sbd *, const char *, size_t); -}; - -#define TUNE_ATTR_3(name, show, store) \ -static struct tune_attr tune_attr_##name = __ATTR(name, 0644, show, store) - -#define TUNE_ATTR_2(name, store) \ -static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \ -{ \ - return snprintf(buf, PAGE_SIZE, "%u\n", sdp->sd_tune.gt_##name); \ -} \ -TUNE_ATTR_3(name, name##_show, store) - -#define TUNE_ATTR(name, check_zero) \ -static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\ -{ \ - return tune_set(sdp, &sdp->sd_tune.gt_##name, check_zero, buf, len); \ -} \ -TUNE_ATTR_2(name, name##_store) - -#define TUNE_ATTR_DAEMON(name, process) \ -static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\ -{ \ - ssize_t r = tune_set(sdp, &sdp->sd_tune.gt_##name, 1, buf, len); \ - wake_up_process(sdp->sd_##process); \ - return r; \ -} \ -TUNE_ATTR_2(name, name##_store) - -TUNE_ATTR(ilimit, 0); -TUNE_ATTR(ilimit_tries, 0); -TUNE_ATTR(ilimit_min, 0); -TUNE_ATTR(demote_secs, 0); -TUNE_ATTR(incore_log_blocks, 0); -TUNE_ATTR(log_flush_secs, 0); -TUNE_ATTR(jindex_refresh_secs, 0); -TUNE_ATTR(quota_warn_period, 0); -TUNE_ATTR(quota_quantum, 0); -TUNE_ATTR(atime_quantum, 0); -TUNE_ATTR(max_readahead, 0); -TUNE_ATTR(complain_secs, 0); -TUNE_ATTR(reclaim_limit, 0); -TUNE_ATTR(prefetch_secs, 0); -TUNE_ATTR(statfs_slow, 0); -TUNE_ATTR(new_files_jdata, 0); -TUNE_ATTR(new_files_directio, 0); -TUNE_ATTR(quota_simul_sync, 1); -TUNE_ATTR(quota_cache_secs, 1); -TUNE_ATTR(max_atomic_write, 1); -TUNE_ATTR(stall_secs, 1); -TUNE_ATTR(entries_per_readdir, 1); -TUNE_ATTR(greedy_default, 1); -TUNE_ATTR(greedy_quantum, 1); -TUNE_ATTR(greedy_max, 1); -TUNE_ATTR(statfs_quantum, 1); -TUNE_ATTR_DAEMON(scand_secs, scand_process); -TUNE_ATTR_DAEMON(recoverd_secs, recoverd_process); -TUNE_ATTR_DAEMON(logd_secs, logd_process); -TUNE_ATTR_DAEMON(quotad_secs, quotad_process); -TUNE_ATTR_3(quota_scale, quota_scale_show, quota_scale_store); - -static struct attribute *tune_attrs[] = { - &tune_attr_ilimit.attr, - &tune_attr_ilimit_tries.attr, - &tune_attr_ilimit_min.attr, - &tune_attr_demote_secs.attr, - &tune_attr_incore_log_blocks.attr, - &tune_attr_log_flush_secs.attr, - &tune_attr_jindex_refresh_secs.attr, - &tune_attr_quota_warn_period.attr, - &tune_attr_quota_quantum.attr, - &tune_attr_atime_quantum.attr, - &tune_attr_max_readahead.attr, - &tune_attr_complain_secs.attr, - &tune_attr_reclaim_limit.attr, - &tune_attr_prefetch_secs.attr, - &tune_attr_statfs_slow.attr, - &tune_attr_quota_simul_sync.attr, - &tune_attr_quota_cache_secs.attr, - &tune_attr_max_atomic_write.attr, - &tune_attr_stall_secs.attr, - &tune_attr_entries_per_readdir.attr, - &tune_attr_greedy_default.attr, - &tune_attr_greedy_quantum.attr, - &tune_attr_greedy_max.attr, - &tune_attr_statfs_quantum.attr, - &tune_attr_scand_secs.attr, - &tune_attr_recoverd_secs.attr, - &tune_attr_logd_secs.attr, - &tune_attr_quotad_secs.attr, - &tune_attr_quota_scale.attr, - &tune_attr_new_files_jdata.attr, - &tune_attr_new_files_directio.attr, - NULL, -}; - -static struct attribute_group lockstruct_group = { - .name = "lockstruct", - .attrs = lockstruct_attrs, -}; - -static struct attribute_group counters_group = { - .name = "counters", - .attrs = counters_attrs, -}; - -static struct attribute_group args_group = { - .name = "args", - .attrs = args_attrs, -}; - -static struct attribute_group tune_group = { - .name = "tune", - .attrs = tune_attrs, -}; - -int gfs2_sys_fs_add(struct gfs2_sbd *sdp) -{ - int error; - - sdp->sd_kobj.kset = &gfs2_kset; - sdp->sd_kobj.ktype = &gfs2_ktype; - - error = kobject_set_name(&sdp->sd_kobj, "%s", sdp->sd_table_name); - if (error) - goto fail; - - error = kobject_register(&sdp->sd_kobj); - if (error) - goto fail; - - error = sysfs_create_group(&sdp->sd_kobj, &lockstruct_group); - if (error) - goto fail_reg; - - error = sysfs_create_group(&sdp->sd_kobj, &counters_group); - if (error) - goto fail_lockstruct; - - error = sysfs_create_group(&sdp->sd_kobj, &args_group); - if (error) - goto fail_counters; - - error = sysfs_create_group(&sdp->sd_kobj, &tune_group); - if (error) - goto fail_args; - - return 0; - -fail_args: - sysfs_remove_group(&sdp->sd_kobj, &args_group); -fail_counters: - sysfs_remove_group(&sdp->sd_kobj, &counters_group); -fail_lockstruct: - sysfs_remove_group(&sdp->sd_kobj, &lockstruct_group); -fail_reg: - kobject_unregister(&sdp->sd_kobj); -fail: - fs_err(sdp, "error %d adding sysfs files", error); - return error; -} - -void gfs2_sys_fs_del(struct gfs2_sbd *sdp) -{ - sysfs_remove_group(&sdp->sd_kobj, &tune_group); - sysfs_remove_group(&sdp->sd_kobj, &args_group); - sysfs_remove_group(&sdp->sd_kobj, &counters_group); - sysfs_remove_group(&sdp->sd_kobj, &lockstruct_group); - kobject_unregister(&sdp->sd_kobj); -} - -int gfs2_sys_init(void) -{ - gfs2_sys_margs = NULL; - spin_lock_init(&gfs2_sys_margs_lock); - return kset_register(&gfs2_kset); -} - -void gfs2_sys_uninit(void) -{ - kfree(gfs2_sys_margs); - kset_unregister(&gfs2_kset); -} - diff --git a/trunk/fs/gfs2/sys.h b/trunk/fs/gfs2/sys.h deleted file mode 100644 index 1ca8cdac5304..000000000000 --- a/trunk/fs/gfs2/sys.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __SYS_DOT_H__ -#define __SYS_DOT_H__ - -#include -struct gfs2_sbd; - -/* Allow args to be passed to GFS2 when using an initial ram disk */ -extern char *gfs2_sys_margs; -extern spinlock_t gfs2_sys_margs_lock; - -int gfs2_sys_fs_add(struct gfs2_sbd *sdp); -void gfs2_sys_fs_del(struct gfs2_sbd *sdp); - -int gfs2_sys_init(void); -void gfs2_sys_uninit(void); - -#endif /* __SYS_DOT_H__ */ - diff --git a/trunk/fs/gfs2/trans.c b/trunk/fs/gfs2/trans.c deleted file mode 100644 index f8dabf8446bb..000000000000 --- a/trunk/fs/gfs2/trans.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "log.h" -#include "lops.h" -#include "meta_io.h" -#include "trans.h" -#include "util.h" - -int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks, - unsigned int revokes) -{ - struct gfs2_trans *tr; - int error; - - BUG_ON(current->journal_info); - BUG_ON(blocks == 0 && revokes == 0); - - tr = kzalloc(sizeof(struct gfs2_trans), GFP_NOFS); - if (!tr) - return -ENOMEM; - - tr->tr_ip = (unsigned long)__builtin_return_address(0); - tr->tr_blocks = blocks; - tr->tr_revokes = revokes; - tr->tr_reserved = 1; - if (blocks) - tr->tr_reserved += 6 + blocks; - if (revokes) - tr->tr_reserved += gfs2_struct2blk(sdp, revokes, - sizeof(u64)); - INIT_LIST_HEAD(&tr->tr_list_buf); - - gfs2_holder_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &tr->tr_t_gh); - - error = gfs2_glock_nq(&tr->tr_t_gh); - if (error) - goto fail_holder_uninit; - - if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { - tr->tr_t_gh.gh_flags |= GL_NOCACHE; - error = -EROFS; - goto fail_gunlock; - } - - error = gfs2_log_reserve(sdp, tr->tr_reserved); - if (error) - goto fail_gunlock; - - current->journal_info = tr; - - return 0; - -fail_gunlock: - gfs2_glock_dq(&tr->tr_t_gh); - -fail_holder_uninit: - gfs2_holder_uninit(&tr->tr_t_gh); - kfree(tr); - - return error; -} - -void gfs2_trans_end(struct gfs2_sbd *sdp) -{ - struct gfs2_trans *tr = current->journal_info; - - BUG_ON(!tr); - current->journal_info = NULL; - - if (!tr->tr_touched) { - gfs2_log_release(sdp, tr->tr_reserved); - gfs2_glock_dq(&tr->tr_t_gh); - gfs2_holder_uninit(&tr->tr_t_gh); - kfree(tr); - return; - } - - if (gfs2_assert_withdraw(sdp, tr->tr_num_buf <= tr->tr_blocks)) { - fs_err(sdp, "tr_num_buf = %u, tr_blocks = %u ", - tr->tr_num_buf, tr->tr_blocks); - print_symbol(KERN_WARNING "GFS2: Transaction created at: %s\n", tr->tr_ip); - } - if (gfs2_assert_withdraw(sdp, tr->tr_num_revoke <= tr->tr_revokes)) { - fs_err(sdp, "tr_num_revoke = %u, tr_revokes = %u ", - tr->tr_num_revoke, tr->tr_revokes); - print_symbol(KERN_WARNING "GFS2: Transaction created at: %s\n", tr->tr_ip); - } - - gfs2_log_commit(sdp, tr); - gfs2_glock_dq(&tr->tr_t_gh); - gfs2_holder_uninit(&tr->tr_t_gh); - kfree(tr); - - if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS) - gfs2_log_flush(sdp, NULL); -} - -void gfs2_trans_add_gl(struct gfs2_glock *gl) -{ - lops_add(gl->gl_sbd, &gl->gl_le); -} - -/** - * gfs2_trans_add_bh - Add a to-be-modified buffer to the current transaction - * @gl: the glock the buffer belongs to - * @bh: The buffer to add - * @meta: True in the case of adding metadata - * - */ - -void gfs2_trans_add_bh(struct gfs2_glock *gl, struct buffer_head *bh, int meta) -{ - struct gfs2_sbd *sdp = gl->gl_sbd; - struct gfs2_bufdata *bd; - - bd = bh->b_private; - if (bd) - gfs2_assert(sdp, bd->bd_gl == gl); - else { - gfs2_attach_bufdata(gl, bh, meta); - bd = bh->b_private; - } - lops_add(sdp, &bd->bd_le); -} - -void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, u64 blkno) -{ - struct gfs2_revoke *rv = kmalloc(sizeof(struct gfs2_revoke), - GFP_NOFS | __GFP_NOFAIL); - lops_init_le(&rv->rv_le, &gfs2_revoke_lops); - rv->rv_blkno = blkno; - lops_add(sdp, &rv->rv_le); -} - -void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno) -{ - struct gfs2_revoke *rv; - int found = 0; - - gfs2_log_lock(sdp); - - list_for_each_entry(rv, &sdp->sd_log_le_revoke, rv_le.le_list) { - if (rv->rv_blkno == blkno) { - list_del(&rv->rv_le.le_list); - gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke); - sdp->sd_log_num_revoke--; - found = 1; - break; - } - } - - gfs2_log_unlock(sdp); - - if (found) { - struct gfs2_trans *tr = current->journal_info; - kfree(rv); - tr->tr_num_revoke_rm++; - } -} - -void gfs2_trans_add_rg(struct gfs2_rgrpd *rgd) -{ - lops_add(rgd->rd_sbd, &rgd->rd_le); -} - diff --git a/trunk/fs/gfs2/trans.h b/trunk/fs/gfs2/trans.h deleted file mode 100644 index 23d4cbe1de5b..000000000000 --- a/trunk/fs/gfs2/trans.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __TRANS_DOT_H__ -#define __TRANS_DOT_H__ - -#include -struct gfs2_sbd; -struct gfs2_rgrpd; -struct gfs2_glock; - -#define RES_DINODE 1 -#define RES_INDIRECT 1 -#define RES_JDATA 1 -#define RES_DATA 1 -#define RES_LEAF 1 -#define RES_RG_BIT 2 -#define RES_EATTR 1 -#define RES_STATFS 1 -#define RES_QUOTA 2 - -int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks, - unsigned int revokes); - -void gfs2_trans_end(struct gfs2_sbd *sdp); - -void gfs2_trans_add_gl(struct gfs2_glock *gl); -void gfs2_trans_add_bh(struct gfs2_glock *gl, struct buffer_head *bh, int meta); -void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, u64 blkno); -void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, u64 blkno); -void gfs2_trans_add_rg(struct gfs2_rgrpd *rgd); - -#endif /* __TRANS_DOT_H__ */ diff --git a/trunk/fs/gfs2/util.c b/trunk/fs/gfs2/util.c deleted file mode 100644 index 196c604faadc..000000000000 --- a/trunk/fs/gfs2/util.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gfs2.h" -#include "incore.h" -#include "glock.h" -#include "lm.h" -#include "util.h" - -kmem_cache_t *gfs2_glock_cachep __read_mostly; -kmem_cache_t *gfs2_inode_cachep __read_mostly; -kmem_cache_t *gfs2_bufdata_cachep __read_mostly; - -void gfs2_assert_i(struct gfs2_sbd *sdp) -{ - printk(KERN_EMERG "GFS2: fsid=%s: fatal assertion failed\n", - sdp->sd_fsname); -} - -/** - * gfs2_assert_withdraw_i - Cause the machine to withdraw if @assertion is false - * Returns: -1 if this call withdrew the machine, - * -2 if it was already withdrawn - */ - -int gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion, - const char *function, char *file, unsigned int line) -{ - int me; - me = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: assertion \"%s\" failed\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, assertion, - sdp->sd_fsname, function, file, line); - dump_stack(); - return (me) ? -1 : -2; -} - -/** - * gfs2_assert_warn_i - Print a message to the console if @assertion is false - * Returns: -1 if we printed something - * -2 if we didn't - */ - -int gfs2_assert_warn_i(struct gfs2_sbd *sdp, char *assertion, - const char *function, char *file, unsigned int line) -{ - if (time_before(jiffies, - sdp->sd_last_warning + - gfs2_tune_get(sdp, gt_complain_secs) * HZ)) - return -2; - - printk(KERN_WARNING - "GFS2: fsid=%s: warning: assertion \"%s\" failed\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, assertion, - sdp->sd_fsname, function, file, line); - - if (sdp->sd_args.ar_debug) - BUG(); - else - dump_stack(); - - sdp->sd_last_warning = jiffies; - - return -1; -} - -/** - * gfs2_consist_i - Flag a filesystem consistency error and withdraw - * Returns: -1 if this call withdrew the machine, - * 0 if it was already withdrawn - */ - -int gfs2_consist_i(struct gfs2_sbd *sdp, int cluster_wide, const char *function, - char *file, unsigned int line) -{ - int rv; - rv = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: filesystem consistency error\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, function, file, line); - return rv; -} - -/** - * gfs2_consist_inode_i - Flag an inode consistency error and withdraw - * Returns: -1 if this call withdrew the machine, - * 0 if it was already withdrawn - */ - -int gfs2_consist_inode_i(struct gfs2_inode *ip, int cluster_wide, - const char *function, char *file, unsigned int line) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - int rv; - rv = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: filesystem consistency error\n" - "GFS2: fsid=%s: inode = %llu %llu\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)ip->i_num.no_formal_ino, - (unsigned long long)ip->i_num.no_addr, - sdp->sd_fsname, function, file, line); - return rv; -} - -/** - * gfs2_consist_rgrpd_i - Flag a RG consistency error and withdraw - * Returns: -1 if this call withdrew the machine, - * 0 if it was already withdrawn - */ - -int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide, - const char *function, char *file, unsigned int line) -{ - struct gfs2_sbd *sdp = rgd->rd_sbd; - int rv; - rv = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: filesystem consistency error\n" - "GFS2: fsid=%s: RG = %llu\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)rgd->rd_ri.ri_addr, - sdp->sd_fsname, function, file, line); - return rv; -} - -/** - * gfs2_meta_check_ii - Flag a magic number consistency error and withdraw - * Returns: -1 if this call withdrew the machine, - * -2 if it was already withdrawn - */ - -int gfs2_meta_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh, - const char *type, const char *function, char *file, - unsigned int line) -{ - int me; - me = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: invalid metadata block\n" - "GFS2: fsid=%s: bh = %llu (%s)\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)bh->b_blocknr, type, - sdp->sd_fsname, function, file, line); - return (me) ? -1 : -2; -} - -/** - * gfs2_metatype_check_ii - Flag a metadata type consistency error and withdraw - * Returns: -1 if this call withdrew the machine, - * -2 if it was already withdrawn - */ - -int gfs2_metatype_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh, - u16 type, u16 t, const char *function, - char *file, unsigned int line) -{ - int me; - me = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: invalid metadata block\n" - "GFS2: fsid=%s: bh = %llu (type: exp=%u, found=%u)\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)bh->b_blocknr, type, t, - sdp->sd_fsname, function, file, line); - return (me) ? -1 : -2; -} - -/** - * gfs2_io_error_i - Flag an I/O error and withdraw - * Returns: -1 if this call withdrew the machine, - * 0 if it was already withdrawn - */ - -int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, char *file, - unsigned int line) -{ - int rv; - rv = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: I/O error\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, function, file, line); - return rv; -} - -/** - * gfs2_io_error_bh_i - Flag a buffer I/O error and withdraw - * Returns: -1 if this call withdrew the machine, - * 0 if it was already withdrawn - */ - -int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, - const char *function, char *file, unsigned int line) -{ - int rv; - rv = gfs2_lm_withdraw(sdp, - "GFS2: fsid=%s: fatal: I/O error\n" - "GFS2: fsid=%s: block = %llu\n" - "GFS2: fsid=%s: function = %s, file = %s, line = %u\n", - sdp->sd_fsname, - sdp->sd_fsname, (unsigned long long)bh->b_blocknr, - sdp->sd_fsname, function, file, line); - return rv; -} - -void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap, - unsigned int bit, int new_value) -{ - unsigned int c, o, b = bit; - int old_value; - - c = b / (8 * PAGE_SIZE); - b %= 8 * PAGE_SIZE; - o = b / 8; - b %= 8; - - old_value = (bitmap[c][o] & (1 << b)); - gfs2_assert_withdraw(sdp, !old_value != !new_value); - - if (new_value) - bitmap[c][o] |= 1 << b; - else - bitmap[c][o] &= ~(1 << b); -} - diff --git a/trunk/fs/gfs2/util.h b/trunk/fs/gfs2/util.h deleted file mode 100644 index 76a50899fe9e..000000000000 --- a/trunk/fs/gfs2/util.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __UTIL_DOT_H__ -#define __UTIL_DOT_H__ - -#include "incore.h" - -#define fs_printk(level, fs, fmt, arg...) \ - printk(level "GFS2: fsid=%s: " fmt , (fs)->sd_fsname , ## arg) - -#define fs_info(fs, fmt, arg...) \ - fs_printk(KERN_INFO , fs , fmt , ## arg) - -#define fs_warn(fs, fmt, arg...) \ - fs_printk(KERN_WARNING , fs , fmt , ## arg) - -#define fs_err(fs, fmt, arg...) \ - fs_printk(KERN_ERR, fs , fmt , ## arg) - - -void gfs2_assert_i(struct gfs2_sbd *sdp); - -#define gfs2_assert(sdp, assertion) \ -do { \ - if (unlikely(!(assertion))) { \ - gfs2_assert_i(sdp); \ - BUG(); \ - } \ -} while (0) - - -int gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion, - const char *function, char *file, unsigned int line); - -#define gfs2_assert_withdraw(sdp, assertion) \ -((likely(assertion)) ? 0 : gfs2_assert_withdraw_i((sdp), #assertion, \ - __FUNCTION__, __FILE__, __LINE__)) - - -int gfs2_assert_warn_i(struct gfs2_sbd *sdp, char *assertion, - const char *function, char *file, unsigned int line); - -#define gfs2_assert_warn(sdp, assertion) \ -((likely(assertion)) ? 0 : gfs2_assert_warn_i((sdp), #assertion, \ - __FUNCTION__, __FILE__, __LINE__)) - - -int gfs2_consist_i(struct gfs2_sbd *sdp, int cluster_wide, - const char *function, char *file, unsigned int line); - -#define gfs2_consist(sdp) \ -gfs2_consist_i((sdp), 0, __FUNCTION__, __FILE__, __LINE__) - - -int gfs2_consist_inode_i(struct gfs2_inode *ip, int cluster_wide, - const char *function, char *file, unsigned int line); - -#define gfs2_consist_inode(ip) \ -gfs2_consist_inode_i((ip), 0, __FUNCTION__, __FILE__, __LINE__) - - -int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide, - const char *function, char *file, unsigned int line); - -#define gfs2_consist_rgrpd(rgd) \ -gfs2_consist_rgrpd_i((rgd), 0, __FUNCTION__, __FILE__, __LINE__) - - -int gfs2_meta_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh, - const char *type, const char *function, - char *file, unsigned int line); - -static inline int gfs2_meta_check_i(struct gfs2_sbd *sdp, - struct buffer_head *bh, - const char *function, - char *file, unsigned int line) -{ - struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data; - u32 magic = mh->mh_magic; - magic = be32_to_cpu(magic); - if (unlikely(magic != GFS2_MAGIC)) - return gfs2_meta_check_ii(sdp, bh, "magic number", function, - file, line); - return 0; -} - -#define gfs2_meta_check(sdp, bh) \ -gfs2_meta_check_i((sdp), (bh), __FUNCTION__, __FILE__, __LINE__) - - -int gfs2_metatype_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh, - u16 type, u16 t, - const char *function, - char *file, unsigned int line); - -static inline int gfs2_metatype_check_i(struct gfs2_sbd *sdp, - struct buffer_head *bh, - u16 type, - const char *function, - char *file, unsigned int line) -{ - struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data; - u32 magic = mh->mh_magic; - u16 t = be32_to_cpu(mh->mh_type); - magic = be32_to_cpu(magic); - if (unlikely(magic != GFS2_MAGIC)) - return gfs2_meta_check_ii(sdp, bh, "magic number", function, - file, line); - if (unlikely(t != type)) - return gfs2_metatype_check_ii(sdp, bh, type, t, function, - file, line); - return 0; -} - -#define gfs2_metatype_check(sdp, bh, type) \ -gfs2_metatype_check_i((sdp), (bh), (type), __FUNCTION__, __FILE__, __LINE__) - -static inline void gfs2_metatype_set(struct buffer_head *bh, u16 type, - u16 format) -{ - struct gfs2_meta_header *mh; - mh = (struct gfs2_meta_header *)bh->b_data; - mh->mh_type = cpu_to_be32(type); - mh->mh_format = cpu_to_be32(format); -} - - -int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, - char *file, unsigned int line); - -#define gfs2_io_error(sdp) \ -gfs2_io_error_i((sdp), __FUNCTION__, __FILE__, __LINE__); - - -int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, - const char *function, char *file, unsigned int line); - -#define gfs2_io_error_bh(sdp, bh) \ -gfs2_io_error_bh_i((sdp), (bh), __FUNCTION__, __FILE__, __LINE__); - - -extern kmem_cache_t *gfs2_glock_cachep; -extern kmem_cache_t *gfs2_inode_cachep; -extern kmem_cache_t *gfs2_bufdata_cachep; - -static inline unsigned int gfs2_tune_get_i(struct gfs2_tune *gt, - unsigned int *p) -{ - unsigned int x; - spin_lock(>->gt_spin); - x = *p; - spin_unlock(>->gt_spin); - return x; -} - -#define gfs2_tune_get(sdp, field) \ -gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field) - -void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap, - unsigned int bit, int new_value); - -#endif /* __UTIL_DOT_H__ */ - diff --git a/trunk/fs/isofs/namei.c b/trunk/fs/isofs/namei.c index e7ba0c30e071..c04b3a14a3e9 100644 --- a/trunk/fs/isofs/namei.c +++ b/trunk/fs/isofs/namei.c @@ -6,7 +6,6 @@ * (C) 1991 Linus Torvalds - minix filesystem */ -#include /* Joliet? */ #include #include "isofs.h" diff --git a/trunk/fs/lockd/clntlock.c b/trunk/fs/lockd/clntlock.c index e8c7765419e8..87e1d03e8267 100644 --- a/trunk/fs/lockd/clntlock.c +++ b/trunk/fs/lockd/clntlock.c @@ -143,13 +143,43 @@ u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) * server crash. */ +/* + * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number, + * that we mark locks for reclaiming, and that we bump the pseudo NSM state. + */ +static void nlmclnt_prepare_reclaim(struct nlm_host *host) +{ + down_write(&host->h_rwsem); + host->h_monitored = 0; + host->h_state++; + host->h_nextrebind = 0; + nlm_rebind_host(host); + + /* + * Mark the locks for reclaiming. + */ + list_splice_init(&host->h_granted, &host->h_reclaim); + + dprintk("NLM: reclaiming locks for host %s\n", host->h_name); +} + +static void nlmclnt_finish_reclaim(struct nlm_host *host) +{ + host->h_reclaiming = 0; + up_write(&host->h_rwsem); + dprintk("NLM: done reclaiming locks for host %s", host->h_name); +} + /* * Reclaim all locks on server host. We do this by spawning a separate * reclaimer thread. */ void -nlmclnt_recovery(struct nlm_host *host) +nlmclnt_recovery(struct nlm_host *host, u32 newstate) { + if (host->h_nsmstate == newstate) + return; + host->h_nsmstate = newstate; if (!host->h_reclaiming++) { nlm_get_host(host); __module_get(THIS_MODULE); @@ -169,30 +199,18 @@ reclaimer(void *ptr) daemonize("%s-reclaim", host->h_name); allow_signal(SIGKILL); - down_write(&host->h_rwsem); - /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ lock_kernel(); lockd_up(0); /* note: this cannot fail as lockd is already running */ - dprintk("lockd: reclaiming locks for host %s", host->h_name); - + nlmclnt_prepare_reclaim(host); + /* First, reclaim all locks that have been marked. */ restart: nsmstate = host->h_nsmstate; - - /* Force a portmap getport - the peer's lockd will - * most likely end up on a different port. - */ - host->h_nextrebind = jiffies; - nlm_rebind_host(host); - - /* First, reclaim all locks that have been granted. */ - list_splice_init(&host->h_granted, &host->h_reclaim); list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { list_del_init(&fl->fl_u.nfs_fl.list); - /* Why are we leaking memory here? --okir */ if (signalled()) continue; if (nlmclnt_reclaim(host, fl) != 0) @@ -200,13 +218,11 @@ reclaimer(void *ptr) list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); if (host->h_nsmstate != nsmstate) { /* Argh! The server rebooted again! */ + list_splice_init(&host->h_granted, &host->h_reclaim); goto restart; } } - - host->h_reclaiming = 0; - up_write(&host->h_rwsem); - dprintk("NLM: done reclaiming locks for host %s", host->h_name); + nlmclnt_finish_reclaim(host); /* Now, wake up all processes that sleep on a blocked lock */ list_for_each_entry(block, &nlm_blocked, b_list) { diff --git a/trunk/fs/lockd/clntproc.c b/trunk/fs/lockd/clntproc.c index 3d84f600b633..0116729cec5f 100644 --- a/trunk/fs/lockd/clntproc.c +++ b/trunk/fs/lockd/clntproc.c @@ -36,14 +36,14 @@ static const struct rpc_call_ops nlmclnt_cancel_ops; /* * Cookie counter for NLM requests */ -static atomic_t nlm_cookie = ATOMIC_INIT(0x1234); +static u32 nlm_cookie = 0x1234; -void nlmclnt_next_cookie(struct nlm_cookie *c) +static inline void nlmclnt_next_cookie(struct nlm_cookie *c) { - u32 cookie = atomic_inc_return(&nlm_cookie); - - memcpy(c->data, &cookie, 4); + memcpy(c->data, &nlm_cookie, 4); + memset(c->data+4, 0, 4); c->len=4; + nlm_cookie++; } static struct nlm_lockowner *nlm_get_lockowner(struct nlm_lockowner *lockowner) @@ -153,7 +153,6 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) { struct rpc_clnt *client = NFS_CLIENT(inode); struct sockaddr_in addr; - struct nfs_server *nfssrv = NFS_SERVER(inode); struct nlm_host *host; struct nlm_rqst *call; sigset_t oldset; @@ -167,9 +166,7 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) } rpc_peeraddr(client, (struct sockaddr *) &addr, sizeof(addr)); - host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers, - nfssrv->nfs_client->cl_hostname, - strlen(nfssrv->nfs_client->cl_hostname)); + host = nlmclnt_lookup_host(&addr, client->cl_xprt->prot, vers); if (host == NULL) return -ENOLCK; @@ -502,7 +499,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) unsigned char fl_flags = fl->fl_flags; int status = -ENOLCK; - if (nsm_monitor(host) < 0) { + if (!host->h_monitored && nsm_monitor(host) < 0) { printk(KERN_NOTICE "lockd: failed to monitor %s\n", host->h_name); goto out; diff --git a/trunk/fs/lockd/host.c b/trunk/fs/lockd/host.c index fb24a9730345..a0d0b58ce7a4 100644 --- a/trunk/fs/lockd/host.c +++ b/trunk/fs/lockd/host.c @@ -27,60 +27,46 @@ #define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ) #define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ) -static struct hlist_head nlm_hosts[NLM_HOST_NRHASH]; +static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; static unsigned long next_gc; static int nrhosts; static DEFINE_MUTEX(nlm_host_mutex); static void nlm_gc_hosts(void); -static struct nsm_handle * __nsm_find(const struct sockaddr_in *, - const char *, int, int); /* * Find an NLM server handle in the cache. If there is none, create it. */ struct nlm_host * -nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version, - const char *hostname, int hostname_len) +nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version) { - return nlm_lookup_host(0, sin, proto, version, - hostname, hostname_len); + return nlm_lookup_host(0, sin, proto, version); } /* * Find an NLM client handle in the cache. If there is none, create it. */ struct nlm_host * -nlmsvc_lookup_host(struct svc_rqst *rqstp, - const char *hostname, int hostname_len) +nlmsvc_lookup_host(struct svc_rqst *rqstp) { return nlm_lookup_host(1, &rqstp->rq_addr, - rqstp->rq_prot, rqstp->rq_vers, - hostname, hostname_len); + rqstp->rq_prot, rqstp->rq_vers); } /* * Common host lookup routine for server & client */ struct nlm_host * -nlm_lookup_host(int server, const struct sockaddr_in *sin, - int proto, int version, - const char *hostname, - int hostname_len) +nlm_lookup_host(int server, struct sockaddr_in *sin, + int proto, int version) { - struct hlist_head *chain; - struct hlist_node *pos; - struct nlm_host *host; - struct nsm_handle *nsm = NULL; + struct nlm_host *host, **hp; + u32 addr; int hash; - dprintk("lockd: nlm_lookup_host(%u.%u.%u.%u, p=%d, v=%d, my role=%s, name=%.*s)\n", - NIPQUAD(sin->sin_addr.s_addr), proto, version, - server? "server" : "client", - hostname_len, - hostname? hostname : ""); - + dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n", + (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version); hash = NLM_ADDRHASH(sin->sin_addr.s_addr); @@ -90,22 +76,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, if (time_after_eq(jiffies, next_gc)) nlm_gc_hosts(); - /* We may keep several nlm_host objects for a peer, because each - * nlm_host is identified by - * (address, protocol, version, server/client) - * We could probably simplify this a little by putting all those - * different NLM rpc_clients into one single nlm_host object. - * This would allow us to have one nlm_host per address. - */ - chain = &nlm_hosts[hash]; - hlist_for_each_entry(host, pos, chain, h_hash) { - if (!nlm_cmp_addr(&host->h_addr, sin)) - continue; - - /* See if we have an NSM handle for this client */ - if (!nsm) - nsm = host->h_nsmhandle; - + for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { if (host->h_proto != proto) continue; if (host->h_version != version) @@ -113,30 +84,28 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, if (host->h_server != server) continue; - /* Move to head of hash chain. */ - hlist_del(&host->h_hash); - hlist_add_head(&host->h_hash, chain); - - nlm_get_host(host); - goto out; + if (nlm_cmp_addr(&host->h_addr, sin)) { + if (hp != nlm_hosts + hash) { + *hp = host->h_next; + host->h_next = nlm_hosts[hash]; + nlm_hosts[hash] = host; + } + nlm_get_host(host); + mutex_unlock(&nlm_host_mutex); + return host; + } } - if (nsm) - atomic_inc(&nsm->sm_count); - host = NULL; - - /* Sadly, the host isn't in our hash table yet. See if - * we have an NSM handle for it. If not, create one. - */ - if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len))) - goto out; + /* Ooops, no host found, create it */ + dprintk("lockd: creating host entry\n"); host = kzalloc(sizeof(*host), GFP_KERNEL); - if (!host) { - nsm_release(nsm); - goto out; - } - host->h_name = nsm->sm_name; + if (!host) + goto nohost; + + addr = sin->sin_addr.s_addr; + sprintf(host->h_name, "%u.%u.%u.%u", NIPQUAD(addr)); + host->h_addr = *sin; host->h_addr.sin_port = 0; /* ouch! */ host->h_version = version; @@ -150,9 +119,9 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, init_rwsem(&host->h_rwsem); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ - host->h_nsmhandle = nsm; host->h_server = server; - hlist_add_head(&host->h_hash, chain); + host->h_next = nlm_hosts[hash]; + nlm_hosts[hash] = host; INIT_LIST_HEAD(&host->h_lockowners); spin_lock_init(&host->h_lock); INIT_LIST_HEAD(&host->h_granted); @@ -161,39 +130,35 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin, if (++nrhosts > NLM_HOST_MAX) next_gc = 0; -out: +nohost: mutex_unlock(&nlm_host_mutex); return host; } -/* - * Destroy a host - */ -static void -nlm_destroy_host(struct nlm_host *host) +struct nlm_host * +nlm_find_client(void) { - struct rpc_clnt *clnt; - - BUG_ON(!list_empty(&host->h_lockowners)); - BUG_ON(atomic_read(&host->h_count)); - - /* - * Release NSM handle and unmonitor host. + /* find a nlm_host for a client for which h_killed == 0. + * and return it */ - nsm_unmonitor(host); - - if ((clnt = host->h_rpcclnt) != NULL) { - if (atomic_read(&clnt->cl_users)) { - printk(KERN_WARNING - "lockd: active RPC handle\n"); - clnt->cl_dead = 1; - } else { - rpc_destroy_client(host->h_rpcclnt); + int hash; + mutex_lock(&nlm_host_mutex); + for (hash = 0 ; hash < NLM_HOST_NRHASH; hash++) { + struct nlm_host *host, **hp; + for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { + if (host->h_server && + host->h_killed == 0) { + nlm_get_host(host); + mutex_unlock(&nlm_host_mutex); + return host; + } } } - kfree(host); + mutex_unlock(&nlm_host_mutex); + return NULL; } + /* * Create the NLM RPC client for an NLM peer */ @@ -294,65 +259,6 @@ void nlm_release_host(struct nlm_host *host) } } -/* - * We were notified that the host indicated by address &sin - * has rebooted. - * Release all resources held by that peer. - */ -void nlm_host_rebooted(const struct sockaddr_in *sin, - const char *hostname, int hostname_len, - u32 new_state) -{ - struct hlist_head *chain; - struct hlist_node *pos; - struct nsm_handle *nsm; - struct nlm_host *host; - - dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n", - hostname, NIPQUAD(sin->sin_addr)); - - /* Find the NSM handle for this peer */ - if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0))) - return; - - /* When reclaiming locks on this peer, make sure that - * we set up a new notification */ - nsm->sm_monitored = 0; - - /* Mark all hosts tied to this NSM state as having rebooted. - * We run the loop repeatedly, because we drop the host table - * lock for this. - * To avoid processing a host several times, we match the nsmstate. - */ -again: mutex_lock(&nlm_host_mutex); - for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { - hlist_for_each_entry(host, pos, chain, h_hash) { - if (host->h_nsmhandle == nsm - && host->h_nsmstate != new_state) { - host->h_nsmstate = new_state; - host->h_state++; - - nlm_get_host(host); - mutex_unlock(&nlm_host_mutex); - - if (host->h_server) { - /* We're server for this guy, just ditch - * all the locks he held. */ - nlmsvc_free_host_resources(host); - } else { - /* He's the server, initiate lock recovery. */ - nlmclnt_recovery(host); - } - - nlm_release_host(host); - goto again; - } - } - } - - mutex_unlock(&nlm_host_mutex); -} - /* * Shut down the hosts module. * Note that this routine is called only at server shutdown time. @@ -360,17 +266,16 @@ again: mutex_lock(&nlm_host_mutex); void nlm_shutdown_hosts(void) { - struct hlist_head *chain; - struct hlist_node *pos; struct nlm_host *host; + int i; dprintk("lockd: shutting down host module\n"); mutex_lock(&nlm_host_mutex); /* First, make all hosts eligible for gc */ dprintk("lockd: nuking all hosts...\n"); - for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { - hlist_for_each_entry(host, pos, chain, h_hash) + for (i = 0; i < NLM_HOST_NRHASH; i++) { + for (host = nlm_hosts[i]; host; host = host->h_next) host->h_expires = jiffies - 1; } @@ -382,8 +287,8 @@ nlm_shutdown_hosts(void) if (nrhosts) { printk(KERN_WARNING "lockd: couldn't shutdown host module!\n"); dprintk("lockd: %d hosts left:\n", nrhosts); - for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { - hlist_for_each_entry(host, pos, chain, h_hash) { + for (i = 0; i < NLM_HOST_NRHASH; i++) { + for (host = nlm_hosts[i]; host; host = host->h_next) { dprintk(" %s (cnt %d use %d exp %ld)\n", host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires); @@ -400,32 +305,45 @@ nlm_shutdown_hosts(void) static void nlm_gc_hosts(void) { - struct hlist_head *chain; - struct hlist_node *pos, *next; - struct nlm_host *host; + struct nlm_host **q, *host; + struct rpc_clnt *clnt; + int i; dprintk("lockd: host garbage collection\n"); - for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { - hlist_for_each_entry(host, pos, chain, h_hash) + for (i = 0; i < NLM_HOST_NRHASH; i++) { + for (host = nlm_hosts[i]; host; host = host->h_next) host->h_inuse = 0; } /* Mark all hosts that hold locks, blocks or shares */ nlmsvc_mark_resources(); - for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { - hlist_for_each_entry_safe(host, pos, next, chain, h_hash) { + for (i = 0; i < NLM_HOST_NRHASH; i++) { + q = &nlm_hosts[i]; + while ((host = *q) != NULL) { if (atomic_read(&host->h_count) || host->h_inuse || time_before(jiffies, host->h_expires)) { dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n", host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires); + q = &host->h_next; continue; } dprintk("lockd: delete host %s\n", host->h_name); - hlist_del_init(&host->h_hash); - - nlm_destroy_host(host); + *q = host->h_next; + /* Don't unmonitor hosts that have been invalidated */ + if (host->h_monitored && !host->h_killed) + nsm_unmonitor(host); + if ((clnt = host->h_rpcclnt) != NULL) { + if (atomic_read(&clnt->cl_users)) { + printk(KERN_WARNING + "lockd: active RPC handle\n"); + clnt->cl_dead = 1; + } else { + rpc_destroy_client(host->h_rpcclnt); + } + } + kfree(host); nrhosts--; } } @@ -433,88 +351,3 @@ nlm_gc_hosts(void) next_gc = jiffies + NLM_HOST_COLLECT; } - -/* - * Manage NSM handles - */ -static LIST_HEAD(nsm_handles); -static DEFINE_MUTEX(nsm_mutex); - -static struct nsm_handle * -__nsm_find(const struct sockaddr_in *sin, - const char *hostname, int hostname_len, - int create) -{ - struct nsm_handle *nsm = NULL; - struct list_head *pos; - - if (!sin) - return NULL; - - if (hostname && memchr(hostname, '/', hostname_len) != NULL) { - if (printk_ratelimit()) { - printk(KERN_WARNING "Invalid hostname \"%.*s\" " - "in NFS lock request\n", - hostname_len, hostname); - } - return NULL; - } - - mutex_lock(&nsm_mutex); - list_for_each(pos, &nsm_handles) { - nsm = list_entry(pos, struct nsm_handle, sm_link); - - if (hostname && nsm_use_hostnames) { - if (strlen(nsm->sm_name) != hostname_len - || memcmp(nsm->sm_name, hostname, hostname_len)) - continue; - } else if (!nlm_cmp_addr(&nsm->sm_addr, sin)) - continue; - atomic_inc(&nsm->sm_count); - goto out; - } - - if (!create) { - nsm = NULL; - goto out; - } - - nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL); - if (nsm != NULL) { - nsm->sm_addr = *sin; - nsm->sm_name = (char *) (nsm + 1); - memcpy(nsm->sm_name, hostname, hostname_len); - nsm->sm_name[hostname_len] = '\0'; - atomic_set(&nsm->sm_count, 1); - - list_add(&nsm->sm_link, &nsm_handles); - } - -out: - mutex_unlock(&nsm_mutex); - return nsm; -} - -struct nsm_handle * -nsm_find(const struct sockaddr_in *sin, const char *hostname, int hostname_len) -{ - return __nsm_find(sin, hostname, hostname_len, 1); -} - -/* - * Release an NSM handle - */ -void -nsm_release(struct nsm_handle *nsm) -{ - if (!nsm) - return; - if (atomic_dec_and_test(&nsm->sm_count)) { - mutex_lock(&nsm_mutex); - if (atomic_read(&nsm->sm_count) == 0) { - list_del(&nsm->sm_link); - kfree(nsm); - } - mutex_unlock(&nsm_mutex); - } -} diff --git a/trunk/fs/lockd/mon.c b/trunk/fs/lockd/mon.c index e0179f8c327f..a816b920d431 100644 --- a/trunk/fs/lockd/mon.c +++ b/trunk/fs/lockd/mon.c @@ -24,13 +24,13 @@ static struct rpc_program nsm_program; /* * Local NSM state */ -int nsm_local_state; +u32 nsm_local_state; /* * Common procedure for SM_MON/SM_UNMON calls */ static int -nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) +nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) { struct rpc_clnt *clnt; int status; @@ -46,11 +46,10 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) goto out; } - memset(&args, 0, sizeof(args)); - args.mon_name = nsm->sm_name; - args.addr = nsm->sm_addr.sin_addr.s_addr; + args.addr = host->h_addr.sin_addr.s_addr; + args.proto= (host->h_proto<<1) | host->h_server; args.prog = NLM_PROGRAM; - args.vers = 3; + args.vers = host->h_version; args.proc = NLMPROC_NSM_NOTIFY; memset(res, 0, sizeof(*res)); @@ -71,22 +70,17 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) int nsm_monitor(struct nlm_host *host) { - struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_res res; int status; dprintk("lockd: nsm_monitor(%s)\n", host->h_name); - BUG_ON(nsm == NULL); - if (nsm->sm_monitored) - return 0; - - status = nsm_mon_unmon(nsm, SM_MON, &res); + status = nsm_mon_unmon(host, SM_MON, &res); if (status < 0 || res.status != 0) printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name); else - nsm->sm_monitored = 1; + host->h_monitored = 1; return status; } @@ -96,26 +90,16 @@ nsm_monitor(struct nlm_host *host) int nsm_unmonitor(struct nlm_host *host) { - struct nsm_handle *nsm = host->h_nsmhandle; struct nsm_res res; - int status = 0; - - if (nsm == NULL) - return 0; - host->h_nsmhandle = NULL; - - if (atomic_read(&nsm->sm_count) == 1 - && nsm->sm_monitored && !nsm->sm_sticky) { - dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name); - - status = nsm_mon_unmon(nsm, SM_UNMON, &res); - if (status < 0) - printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", - host->h_name); - else - nsm->sm_monitored = 0; - } - nsm_release(nsm); + int status; + + dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name); + + status = nsm_mon_unmon(host, SM_UNMON, &res); + if (status < 0) + printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", host->h_name); + else + host->h_monitored = 0; return status; } @@ -151,7 +135,7 @@ nsm_create(void) static u32 * xdr_encode_common(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) { - char buffer[20], *name; + char buffer[20]; /* * Use the dotted-quad IP address of the remote host as @@ -159,13 +143,8 @@ xdr_encode_common(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) * hostname first for whatever remote hostname it receives, * so this works alright. */ - if (nsm_use_hostnames) { - name = argp->mon_name; - } else { - sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr)); - name = buffer; - } - if (!(p = xdr_encode_string(p, name)) + sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr)); + if (!(p = xdr_encode_string(p, buffer)) || !(p = xdr_encode_string(p, utsname()->nodename))) return ERR_PTR(-EIO); *p++ = htonl(argp->prog); @@ -181,11 +160,9 @@ xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp) p = xdr_encode_common(rqstp, p, argp); if (IS_ERR(p)) return PTR_ERR(p); - - /* Surprise - there may even be room for an IPv6 address now */ *p++ = argp->addr; - *p++ = 0; - *p++ = 0; + *p++ = argp->vers; + *p++ = argp->proto; *p++ = 0; rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p); return 0; diff --git a/trunk/fs/lockd/svc.c b/trunk/fs/lockd/svc.c index 634139232aaf..3cc369e5693f 100644 --- a/trunk/fs/lockd/svc.c +++ b/trunk/fs/lockd/svc.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #define NLMDBG_FACILITY NLMDBG_SVC @@ -62,7 +61,6 @@ static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); static unsigned long nlm_grace_period; static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; static int nlm_udpport, nlm_tcpport; -int nsm_use_hostnames = 0; /* * Constants needed for the sysctl interface. @@ -397,22 +395,6 @@ static ctl_table nlm_sysctls[] = { .extra1 = (int *) &nlm_port_min, .extra2 = (int *) &nlm_port_max, }, - { - .ctl_name = CTL_UNNUMBERED, - .procname = "nsm_use_hostnames", - .data = &nsm_use_hostnames, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = CTL_UNNUMBERED, - .procname = "nsm_local_state", - .data = &nsm_local_state, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, { .ctl_name = 0 } }; @@ -501,7 +483,6 @@ module_param_call(nlm_udpport, param_set_port, param_get_int, &nlm_udpport, 0644); module_param_call(nlm_tcpport, param_set_port, param_get_int, &nlm_tcpport, 0644); -module_param(nsm_use_hostnames, bool, 0644); /* * Initialising and terminating the module. diff --git a/trunk/fs/lockd/svc4proc.c b/trunk/fs/lockd/svc4proc.c index fa370f6eb07b..a2dd9ccb9b32 100644 --- a/trunk/fs/lockd/svc4proc.c +++ b/trunk/fs/lockd/svc4proc.c @@ -38,8 +38,8 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, return nlm_lck_denied_nolocks; /* Obtain host handle */ - if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) - || (argp->monitor && nsm_monitor(host) < 0)) + if (!(host = nlmsvc_lookup_host(rqstp)) + || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0)) goto no_locks; *hostp = host; @@ -260,9 +260,7 @@ static int nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *a struct nlm_rqst *call; int stat; - host = nlmsvc_lookup_host(rqstp, - argp->lock.caller, - argp->lock.len); + host = nlmsvc_lookup_host(rqstp); if (host == NULL) return rpc_system_err; @@ -422,6 +420,10 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { struct sockaddr_in saddr = rqstp->rq_addr; + int vers = argp->vers; + int prot = argp->proto >> 1; + + struct nlm_host *host; dprintk("lockd: SM_NOTIFY called\n"); if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) @@ -436,10 +438,21 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, /* Obtain the host pointer for this NFS server and try to * reclaim all locks we hold on this server. */ - memset(&saddr, 0, sizeof(saddr)); saddr.sin_addr.s_addr = argp->addr; - nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state); + if ((argp->proto & 1)==0) { + if ((host = nlmclnt_lookup_host(&saddr, prot, vers)) != NULL) { + nlmclnt_recovery(host, argp->state); + nlm_release_host(host); + } + } else { + /* If we run on an NFS server, delete all locks held by the client */ + + if ((host = nlm_lookup_host(1, &saddr, prot, vers)) != NULL) { + nlmsvc_free_host_resources(host); + nlm_release_host(host); + } + } return rpc_success; } @@ -455,7 +468,7 @@ nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, dprintk("lockd: GRANTED_RES called\n"); - nlmsvc_grant_reply(&argp->cookie, argp->status); + nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status); return rpc_success; } diff --git a/trunk/fs/lockd/svclock.c b/trunk/fs/lockd/svclock.c index 814c6064c9e0..93c00ee7189d 100644 --- a/trunk/fs/lockd/svclock.c +++ b/trunk/fs/lockd/svclock.c @@ -40,7 +40,7 @@ static void nlmsvc_release_block(struct nlm_block *block); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); -static void nlmsvc_remove_block(struct nlm_block *block); +static int nlmsvc_remove_block(struct nlm_block *block); static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock); static void nlmsvc_freegrantargs(struct nlm_rqst *call); @@ -49,7 +49,7 @@ static const struct rpc_call_ops nlmsvc_grant_ops; /* * The list of blocked locks to retry */ -static LIST_HEAD(nlm_blocked); +static struct nlm_block * nlm_blocked; /* * Insert a blocked lock into the global list @@ -57,44 +57,48 @@ static LIST_HEAD(nlm_blocked); static void nlmsvc_insert_block(struct nlm_block *block, unsigned long when) { - struct nlm_block *b; - struct list_head *pos; + struct nlm_block **bp, *b; dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when); - if (list_empty(&block->b_list)) { - kref_get(&block->b_count); - } else { - list_del_init(&block->b_list); - } - - pos = &nlm_blocked; + kref_get(&block->b_count); + if (block->b_queued) + nlmsvc_remove_block(block); + bp = &nlm_blocked; if (when != NLM_NEVER) { if ((when += jiffies) == NLM_NEVER) when ++; - list_for_each(pos, &nlm_blocked) { - b = list_entry(pos, struct nlm_block, b_list); - if (time_after(b->b_when,when) || b->b_when == NLM_NEVER) - break; - } - /* On normal exit from the loop, pos == &nlm_blocked, - * so we will be adding to the end of the list - good - */ - } + while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER) + bp = &b->b_next; + } else + while ((b = *bp) != 0) + bp = &b->b_next; - list_add_tail(&block->b_list, pos); + block->b_queued = 1; block->b_when = when; + block->b_next = b; + *bp = block; } /* * Remove a block from the global list */ -static inline void +static int nlmsvc_remove_block(struct nlm_block *block) { - if (!list_empty(&block->b_list)) { - list_del_init(&block->b_list); - nlmsvc_release_block(block); + struct nlm_block **bp, *b; + + if (!block->b_queued) + return 1; + for (bp = &nlm_blocked; (b = *bp) != 0; bp = &b->b_next) { + if (b == block) { + *bp = block->b_next; + block->b_queued = 0; + nlmsvc_release_block(block); + return 1; + } } + + return 0; } /* @@ -103,14 +107,14 @@ nlmsvc_remove_block(struct nlm_block *block) static struct nlm_block * nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock) { - struct nlm_block *block; + struct nlm_block **head, *block; struct file_lock *fl; dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n", file, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end, lock->fl.fl_type); - list_for_each_entry(block, &nlm_blocked, b_list) { + for (head = &nlm_blocked; (block = *head) != 0; head = &block->b_next) { fl = &block->b_call->a_args.lock.fl; dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n", block->b_file, fl->fl_pid, @@ -139,20 +143,20 @@ static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b) * Find a block with a given NLM cookie. */ static inline struct nlm_block * -nlmsvc_find_block(struct nlm_cookie *cookie) +nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin) { struct nlm_block *block; - list_for_each_entry(block, &nlm_blocked, b_list) { - if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie)) - goto found; + for (block = nlm_blocked; block; block = block->b_next) { + dprintk("cookie: head of blocked queue %p, block %p\n", + nlm_blocked, block); + if (nlm_cookie_match(&block->b_call->a_args.cookie,cookie) + && nlm_cmp_addr(sin, &block->b_host->h_addr)) + break; } - return NULL; - -found: - dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block); - kref_get(&block->b_count); + if (block != NULL) + kref_get(&block->b_count); return block; } @@ -165,11 +169,6 @@ nlmsvc_find_block(struct nlm_cookie *cookie) * request, but (as I found out later) that's because some implementations * do just this. Never mind the standards comittees, they support our * logging industries. - * - * 10 years later: I hope we can safely ignore these old and broken - * clients by now. Let's fix this so we can uniquely identify an incoming - * GRANTED_RES message by cookie, without having to rely on the client's IP - * address. --okir */ static inline struct nlm_block * nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, @@ -180,7 +179,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_rqst *call = NULL; /* Create host handle for callback */ - host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); + host = nlmsvc_lookup_host(rqstp); if (host == NULL) return NULL; @@ -193,8 +192,6 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, if (block == NULL) goto failed; kref_init(&block->b_count); - INIT_LIST_HEAD(&block->b_list); - INIT_LIST_HEAD(&block->b_flist); if (!nlmsvc_setgrantargs(call, lock)) goto failed_free; @@ -202,7 +199,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, /* Set notifier function for VFS, and init args */ call->a_args.lock.fl.fl_flags |= FL_SLEEP; call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations; - nlmclnt_next_cookie(&call->a_args.cookie); + call->a_args.cookie = *cookie; /* see above */ dprintk("lockd: created block %p...\n", block); @@ -213,7 +210,8 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, file->f_count++; /* Add to file's list of blocks */ - list_add(&block->b_flist, &file->f_blocks); + block->b_fnext = file->f_blocks; + file->f_blocks = block; /* Set up RPC arguments for callback */ block->b_call = call; @@ -250,13 +248,19 @@ static void nlmsvc_free_block(struct kref *kref) { struct nlm_block *block = container_of(kref, struct nlm_block, b_count); struct nlm_file *file = block->b_file; + struct nlm_block **bp; dprintk("lockd: freeing block %p...\n", block); + down(&file->f_sema); /* Remove block from file's list of blocks */ - mutex_lock(&file->f_mutex); - list_del_init(&block->b_flist); - mutex_unlock(&file->f_mutex); + for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) { + if (*bp == block) { + *bp = block->b_fnext; + break; + } + } + up(&file->f_sema); nlmsvc_freegrantargs(block->b_call); nlm_release_call(block->b_call); @@ -270,32 +274,47 @@ static void nlmsvc_release_block(struct nlm_block *block) kref_put(&block->b_count, nlmsvc_free_block); } -/* - * Loop over all blocks and delete blocks held by - * a matching host. - */ -void nlmsvc_traverse_blocks(struct nlm_host *host, - struct nlm_file *file, - nlm_host_match_fn_t match) +static void nlmsvc_act_mark(struct nlm_host *host, struct nlm_file *file) { - struct nlm_block *block, *next; + struct nlm_block *block; + + down(&file->f_sema); + for (block = file->f_blocks; block != NULL; block = block->b_fnext) + block->b_host->h_inuse = 1; + up(&file->f_sema); +} + +static void nlmsvc_act_unlock(struct nlm_host *host, struct nlm_file *file) +{ + struct nlm_block *block; restart: - mutex_lock(&file->f_mutex); - list_for_each_entry_safe(block, next, &file->f_blocks, b_flist) { - if (!match(block->b_host, host)) + down(&file->f_sema); + for (block = file->f_blocks; block != NULL; block = block->b_fnext) { + if (host != NULL && host != block->b_host) continue; - /* Do not destroy blocks that are not on - * the global retry list - why? */ - if (list_empty(&block->b_list)) + if (!block->b_queued) continue; kref_get(&block->b_count); - mutex_unlock(&file->f_mutex); + up(&file->f_sema); nlmsvc_unlink_block(block); nlmsvc_release_block(block); goto restart; } - mutex_unlock(&file->f_mutex); + up(&file->f_sema); +} + +/* + * Loop over all blocks and perform the action specified. + * (NLM_ACT_CHECK handled by nlmsvc_inspect_file). + */ +void +nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action) +{ + if (action == NLM_ACT_MARK) + nlmsvc_act_mark(host, file); + else + nlmsvc_act_unlock(host, file); } /* @@ -354,7 +373,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, lock->fl.fl_flags &= ~FL_SLEEP; again: /* Lock file against concurrent access */ - mutex_lock(&file->f_mutex); + down(&file->f_sema); /* Get existing block (in case client is busy-waiting) */ block = nlmsvc_lookup_block(file, lock); if (block == NULL) { @@ -392,10 +411,10 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, /* If we don't have a block, create and initialize it. Then * retry because we may have slept in kmalloc. */ - /* We have to release f_mutex as nlmsvc_create_block may try to + /* We have to release f_sema as nlmsvc_create_block may try to * to claim it while doing host garbage collection */ if (newblock == NULL) { - mutex_unlock(&file->f_mutex); + up(&file->f_sema); dprintk("lockd: blocking on this lock (allocating).\n"); if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie))) return nlm_lck_denied_nolocks; @@ -405,7 +424,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, /* Append to list of blocked */ nlmsvc_insert_block(newblock, NLM_NEVER); out: - mutex_unlock(&file->f_mutex); + up(&file->f_sema); nlmsvc_release_block(newblock); nlmsvc_release_block(block); dprintk("lockd: nlmsvc_lock returned %u\n", ret); @@ -432,7 +451,6 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, (long long)conflock->fl.fl_start, (long long)conflock->fl.fl_end); conflock->caller = "somehost"; /* FIXME */ - conflock->len = strlen(conflock->caller); conflock->oh.len = 0; /* don't return OH info */ conflock->svid = conflock->fl.fl_pid; return nlm_lck_denied; @@ -489,9 +507,9 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - mutex_lock(&file->f_mutex); + down(&file->f_sema); block = nlmsvc_lookup_block(file, lock); - mutex_unlock(&file->f_mutex); + up(&file->f_sema); if (block != NULL) { status = nlmsvc_unlink_block(block); nlmsvc_release_block(block); @@ -509,10 +527,10 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) static void nlmsvc_notify_blocked(struct file_lock *fl) { - struct nlm_block *block; + struct nlm_block **bp, *block; dprintk("lockd: VFS unblock notification for block %p\n", fl); - list_for_each_entry(block, &nlm_blocked, b_list) { + for (bp = &nlm_blocked; (block = *bp) != 0; bp = &block->b_next) { if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) { nlmsvc_insert_block(block, 0); svc_wake_up(block->b_daemon); @@ -645,14 +663,17 @@ static const struct rpc_call_ops nlmsvc_grant_ops = { * block. */ void -nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status) +nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status) { struct nlm_block *block; + struct nlm_file *file; - dprintk("grant_reply: looking for cookie %x, s=%d \n", - *(unsigned int *)(cookie->data), status); - if (!(block = nlmsvc_find_block(cookie))) + dprintk("grant_reply: looking for cookie %x, host (%08x), s=%d \n", + *(unsigned int *)(cookie->data), + ntohl(rqstp->rq_addr.sin_addr.s_addr), status); + if (!(block = nlmsvc_find_block(cookie, &rqstp->rq_addr))) return; + file = block->b_file; if (block) { if (status == NLM_LCK_DENIED_GRACE_PERIOD) { @@ -675,19 +696,16 @@ nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status) unsigned long nlmsvc_retry_blocked(void) { - unsigned long timeout = MAX_SCHEDULE_TIMEOUT; - struct nlm_block *block; - - while (!list_empty(&nlm_blocked)) { - block = list_entry(nlm_blocked.next, struct nlm_block, b_list); + struct nlm_block *block; + dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", + nlm_blocked, + nlm_blocked? nlm_blocked->b_when : 0); + while ((block = nlm_blocked) != 0) { if (block->b_when == NLM_NEVER) break; - if (time_after(block->b_when,jiffies)) { - timeout = block->b_when - jiffies; + if (time_after(block->b_when,jiffies)) break; - } - dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", block, block->b_when); kref_get(&block->b_count); @@ -695,5 +713,8 @@ nlmsvc_retry_blocked(void) nlmsvc_release_block(block); } - return timeout; + if ((block = nlm_blocked) && block->b_when != NLM_NEVER) + return (block->b_when - jiffies); + + return MAX_SCHEDULE_TIMEOUT; } diff --git a/trunk/fs/lockd/svcproc.c b/trunk/fs/lockd/svcproc.c index 75b2c81bcb93..dbb66a3b5cd9 100644 --- a/trunk/fs/lockd/svcproc.c +++ b/trunk/fs/lockd/svcproc.c @@ -66,8 +66,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, return nlm_lck_denied_nolocks; /* Obtain host handle */ - if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) - || (argp->monitor && nsm_monitor(host) < 0)) + if (!(host = nlmsvc_lookup_host(rqstp)) + || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0)) goto no_locks; *hostp = host; @@ -287,9 +287,7 @@ static int nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args *ar struct nlm_rqst *call; int stat; - host = nlmsvc_lookup_host(rqstp, - argp->lock.caller, - argp->lock.len); + host = nlmsvc_lookup_host(rqstp); if (host == NULL) return rpc_system_err; @@ -451,6 +449,9 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { struct sockaddr_in saddr = rqstp->rq_addr; + int vers = argp->vers; + int prot = argp->proto >> 1; + struct nlm_host *host; dprintk("lockd: SM_NOTIFY called\n"); if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) @@ -465,9 +466,19 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, /* Obtain the host pointer for this NFS server and try to * reclaim all locks we hold on this server. */ - memset(&saddr, 0, sizeof(saddr)); saddr.sin_addr.s_addr = argp->addr; - nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state); + if ((argp->proto & 1)==0) { + if ((host = nlmclnt_lookup_host(&saddr, prot, vers)) != NULL) { + nlmclnt_recovery(host, argp->state); + nlm_release_host(host); + } + } else { + /* If we run on an NFS server, delete all locks held by the client */ + if ((host = nlm_lookup_host(1, &saddr, prot, vers)) != NULL) { + nlmsvc_free_host_resources(host); + nlm_release_host(host); + } + } return rpc_success; } @@ -484,7 +495,7 @@ nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp, dprintk("lockd: GRANTED_RES called\n"); - nlmsvc_grant_reply(&argp->cookie, argp->status); + nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status); return rpc_success; } diff --git a/trunk/fs/lockd/svcshare.c b/trunk/fs/lockd/svcshare.c index b9926ce8782e..27288c83da96 100644 --- a/trunk/fs/lockd/svcshare.c +++ b/trunk/fs/lockd/svcshare.c @@ -85,20 +85,24 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file, } /* - * Traverse all shares for a given file, and delete - * those owned by the given (type of) host + * Traverse all shares for a given file (and host). + * NLM_ACT_CHECK is handled by nlmsvc_inspect_file. */ -void nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, - nlm_host_match_fn_t match) +void +nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action) { struct nlm_share *share, **shpp; shpp = &file->f_shares; while ((share = *shpp) != NULL) { - if (match(share->s_host, host)) { - *shpp = share->s_next; - kfree(share); - continue; + if (action == NLM_ACT_MARK) + share->s_host->h_inuse = 1; + else if (action == NLM_ACT_UNLOCK) { + if (host == NULL || host == share->s_host) { + *shpp = share->s_next; + kfree(share); + continue; + } } shpp = &share->s_next; } diff --git a/trunk/fs/lockd/svcsubs.c b/trunk/fs/lockd/svcsubs.c index 514f5f20701e..a92dd98f8401 100644 --- a/trunk/fs/lockd/svcsubs.c +++ b/trunk/fs/lockd/svcsubs.c @@ -25,9 +25,9 @@ /* * Global file hash table */ -#define FILE_HASH_BITS 7 +#define FILE_HASH_BITS 5 #define FILE_NRHASH (1<f_next) if (!nfs_compare_fh(&file->f_handle, f)) goto found; @@ -106,9 +105,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, goto out_unlock; memcpy(&file->f_handle, f, sizeof(struct nfs_fh)); - mutex_init(&file->f_mutex); - INIT_HLIST_NODE(&file->f_list); - INIT_LIST_HEAD(&file->f_blocks); + file->f_hash = hash; + init_MUTEX(&file->f_sema); /* Open the file. Note that this must not sleep for too long, else * we would lock up lockd:-) So no NFS re-exports, folks. @@ -117,11 +115,12 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, * the file. */ if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) { - dprintk("lockd: open failed (error %d)\n", nfserr); + dprintk("lockd: open failed (nfserr %d)\n", ntohl(nfserr)); goto out_free; } - hlist_add_head(&file->f_list, &nlm_files[hash]); + file->f_next = nlm_files[hash]; + nlm_files[hash] = file; found: dprintk("lockd: found file %p (count %d)\n", file, file->f_count); @@ -150,14 +149,22 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, static inline void nlm_delete_file(struct nlm_file *file) { + struct nlm_file **fp, *f; + nlm_debug_print_file("closing file", file); - if (!hlist_unhashed(&file->f_list)) { - hlist_del(&file->f_list); - nlmsvc_ops->fclose(file->f_file); - kfree(file); - } else { - printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); + + fp = nlm_files + file->f_hash; + while ((f = *fp) != NULL) { + if (f == file) { + *fp = file->f_next; + nlmsvc_ops->fclose(file->f_file); + kfree(file); + return; + } + fp = &f->f_next; } + + printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); } /* @@ -165,8 +172,7 @@ nlm_delete_file(struct nlm_file *file) * action. */ static int -nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, - nlm_host_match_fn_t match) +nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action) { struct inode *inode = nlmsvc_file_inode(file); struct file_lock *fl; @@ -180,11 +186,17 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, /* update current lock count */ file->f_locks++; - lockhost = (struct nlm_host *) fl->fl_owner; - if (match(lockhost, host)) { + if (action == NLM_ACT_MARK) + lockhost->h_inuse = 1; + else if (action == NLM_ACT_CHECK) + return 1; + else if (action == NLM_ACT_UNLOCK) { struct file_lock lock = *fl; + if (host && lockhost != host) + continue; + lock.fl_type = F_UNLCK; lock.fl_start = 0; lock.fl_end = OFFSET_MAX; @@ -201,66 +213,53 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, } /* - * Inspect a single file + * Operate on a single file */ static inline int -nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match) +nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action) { - nlmsvc_traverse_blocks(host, file, match); - nlmsvc_traverse_shares(host, file, match); - return nlm_traverse_locks(host, file, match); -} - -/* - * Quick check whether there are still any locks, blocks or - * shares on a given file. - */ -static inline int -nlm_file_inuse(struct nlm_file *file) -{ - struct inode *inode = nlmsvc_file_inode(file); - struct file_lock *fl; - - if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares) - return 1; - - for (fl = inode->i_flock; fl; fl = fl->fl_next) { - if (fl->fl_lmops == &nlmsvc_lock_operations) + if (action == NLM_ACT_CHECK) { + /* Fast path for mark and sweep garbage collection */ + if (file->f_count || file->f_blocks || file->f_shares) return 1; + } else { + nlmsvc_traverse_blocks(host, file, action); + nlmsvc_traverse_shares(host, file, action); } - file->f_locks = 0; - return 0; + return nlm_traverse_locks(host, file, action); } /* * Loop over all files in the file table. */ static int -nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match) +nlm_traverse_files(struct nlm_host *host, int action) { - struct hlist_node *pos, *next; - struct nlm_file *file; + struct nlm_file *file, **fp; int i, ret = 0; mutex_lock(&nlm_file_mutex); for (i = 0; i < FILE_NRHASH; i++) { - hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) { + fp = nlm_files + i; + while ((file = *fp) != NULL) { file->f_count++; mutex_unlock(&nlm_file_mutex); /* Traverse locks, blocks and shares of this file * and update file->f_locks count */ - if (nlm_inspect_file(host, file, match)) + if (nlm_inspect_file(host, file, action)) ret = 1; mutex_lock(&nlm_file_mutex); file->f_count--; /* No more references to this file. Let go of it. */ - if (list_empty(&file->f_blocks) && !file->f_locks + if (!file->f_blocks && !file->f_locks && !file->f_shares && !file->f_count) { - hlist_del(&file->f_list); + *fp = file->f_next; nlmsvc_ops->fclose(file->f_file); kfree(file); + } else { + fp = &file->f_next; } } } @@ -287,46 +286,14 @@ nlm_release_file(struct nlm_file *file) mutex_lock(&nlm_file_mutex); /* If there are no more locks etc, delete the file */ - if (--file->f_count == 0 && !nlm_file_inuse(file)) - nlm_delete_file(file); + if(--file->f_count == 0) { + if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK)) + nlm_delete_file(file); + } mutex_unlock(&nlm_file_mutex); } -/* - * Helpers function for resource traversal - * - * nlmsvc_mark_host: - * used by the garbage collector; simply sets h_inuse. - * Always returns 0. - * - * nlmsvc_same_host: - * returns 1 iff the two hosts match. Used to release - * all resources bound to a specific host. - * - * nlmsvc_is_client: - * returns 1 iff the host is a client. - * Used by nlmsvc_invalidate_all - */ -static int -nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy) -{ - host->h_inuse = 1; - return 0; -} - -static int -nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other) -{ - return host == other; -} - -static int -nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy) -{ - return host->h_server; -} - /* * Mark all hosts that still hold resources */ @@ -334,7 +301,8 @@ void nlmsvc_mark_resources(void) { dprintk("lockd: nlmsvc_mark_resources\n"); - nlm_traverse_files(NULL, nlmsvc_mark_host); + + nlm_traverse_files(NULL, NLM_ACT_MARK); } /* @@ -345,25 +313,23 @@ nlmsvc_free_host_resources(struct nlm_host *host) { dprintk("lockd: nlmsvc_free_host_resources\n"); - if (nlm_traverse_files(host, nlmsvc_same_host)) { + if (nlm_traverse_files(host, NLM_ACT_UNLOCK)) printk(KERN_WARNING - "lockd: couldn't remove all locks held by %s\n", + "lockd: couldn't remove all locks held by %s", host->h_name); - BUG(); - } } /* - * Remove all locks held for clients + * delete all hosts structs for clients */ void nlmsvc_invalidate_all(void) { - /* Release all locks held by NFS clients. - * Previously, the code would call - * nlmsvc_free_host_resources for each client in - * turn, which is about as inefficient as it gets. - * Now we just do it once in nlm_traverse_files. - */ - nlm_traverse_files(NULL, nlmsvc_is_client); + struct nlm_host *host; + while ((host = nlm_find_client()) != NULL) { + nlmsvc_free_host_resources(host); + host->h_expires = 0; + host->h_killed = 1; + nlm_release_host(host); + } } diff --git a/trunk/fs/nfs/client.c b/trunk/fs/nfs/client.c index 8106f3b29e4a..6e4e48c5092a 100644 --- a/trunk/fs/nfs/client.c +++ b/trunk/fs/nfs/client.c @@ -10,7 +10,6 @@ */ -#include #include #include diff --git a/trunk/fs/nfs/getroot.c b/trunk/fs/nfs/getroot.c index 76b08ae9ed82..20c6f39ea38a 100644 --- a/trunk/fs/nfs/getroot.c +++ b/trunk/fs/nfs/getroot.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include diff --git a/trunk/fs/nfs/namespace.c b/trunk/fs/nfs/namespace.c index 60408646176b..ec1114b33d89 100644 --- a/trunk/fs/nfs/namespace.c +++ b/trunk/fs/nfs/namespace.c @@ -7,8 +7,6 @@ * NFS namespace */ -#include - #include #include #include diff --git a/trunk/fs/nfs/nfs4namespace.c b/trunk/fs/nfs/nfs4namespace.c index 24e47f3bbd17..b872779d7cd5 100644 --- a/trunk/fs/nfs/nfs4namespace.c +++ b/trunk/fs/nfs/nfs4namespace.c @@ -7,8 +7,6 @@ * NFSv4 namespace */ -#include - #include #include #include diff --git a/trunk/fs/nfs/nfsroot.c b/trunk/fs/nfs/nfsroot.c index 1d656a645199..8dfefe41a8da 100644 --- a/trunk/fs/nfs/nfsroot.c +++ b/trunk/fs/nfs/nfsroot.c @@ -69,7 +69,6 @@ * Fabian Frederick: Option parser rebuilt (using parser lib) */ -#include #include #include #include diff --git a/trunk/fs/nfs/super.c b/trunk/fs/nfs/super.c index e8d40030cab4..28659a919d6e 100644 --- a/trunk/fs/nfs/super.c +++ b/trunk/fs/nfs/super.c @@ -20,7 +20,6 @@ * of another (see nfs_lookup()) */ -#include #include #include diff --git a/trunk/fs/nfsd/export.c b/trunk/fs/nfsd/export.c index e13fa23bd108..cfe141e5d759 100644 --- a/trunk/fs/nfsd/export.c +++ b/trunk/fs/nfsd/export.c @@ -319,25 +319,12 @@ svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) static struct cache_head *export_table[EXPORT_HASHMAX]; -static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc) -{ - int i; - - for (i = 0; i < fsloc->locations_count; i++) { - kfree(fsloc->locations[i].path); - kfree(fsloc->locations[i].hosts); - } - kfree(fsloc->locations); -} - static void svc_export_put(struct kref *ref) { struct svc_export *exp = container_of(ref, struct svc_export, h.ref); dput(exp->ex_dentry); mntput(exp->ex_mnt); auth_domain_put(exp->ex_client); - kfree(exp->ex_path); - nfsd4_fslocs_free(&exp->ex_fslocs); kfree(exp); } @@ -399,69 +386,6 @@ static int check_export(struct inode *inode, int flags) } -#ifdef CONFIG_NFSD_V4 - -static int -fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) -{ - int len; - int migrated, i, err; - - len = qword_get(mesg, buf, PAGE_SIZE); - if (len != 5 || memcmp(buf, "fsloc", 5)) - return 0; - - /* listsize */ - err = get_int(mesg, &fsloc->locations_count); - if (err) - return err; - if (fsloc->locations_count > MAX_FS_LOCATIONS) - return -EINVAL; - if (fsloc->locations_count == 0) - return 0; - - fsloc->locations = kzalloc(fsloc->locations_count - * sizeof(struct nfsd4_fs_location), GFP_KERNEL); - if (!fsloc->locations) - return -ENOMEM; - for (i=0; i < fsloc->locations_count; i++) { - /* colon separated host list */ - err = -EINVAL; - len = qword_get(mesg, buf, PAGE_SIZE); - if (len <= 0) - goto out_free_all; - err = -ENOMEM; - fsloc->locations[i].hosts = kstrdup(buf, GFP_KERNEL); - if (!fsloc->locations[i].hosts) - goto out_free_all; - err = -EINVAL; - /* slash separated path component list */ - len = qword_get(mesg, buf, PAGE_SIZE); - if (len <= 0) - goto out_free_all; - err = -ENOMEM; - fsloc->locations[i].path = kstrdup(buf, GFP_KERNEL); - if (!fsloc->locations[i].path) - goto out_free_all; - } - /* migrated */ - err = get_int(mesg, &migrated); - if (err) - goto out_free_all; - err = -EINVAL; - if (migrated < 0 || migrated > 1) - goto out_free_all; - fsloc->migrated = migrated; - return 0; -out_free_all: - nfsd4_fslocs_free(fsloc); - return err; -} - -#else /* CONFIG_NFSD_V4 */ -static inline int fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) { return 0; } -#endif - static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) { /* client path expiry [flags anonuid anongid fsid] */ @@ -474,7 +398,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) int an_int; nd.dentry = NULL; - exp.ex_path = NULL; if (mesg[mlen-1] != '\n') return -EINVAL; @@ -505,10 +428,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) exp.ex_client = dom; exp.ex_mnt = nd.mnt; exp.ex_dentry = nd.dentry; - exp.ex_path = kstrdup(buf, GFP_KERNEL); - err = -ENOMEM; - if (!exp.ex_path) - goto out; /* expiry */ err = -EINVAL; @@ -516,11 +435,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) if (exp.h.expiry_time == 0) goto out; - /* fs locations */ - exp.ex_fslocs.locations = NULL; - exp.ex_fslocs.locations_count = 0; - exp.ex_fslocs.migrated = 0; - /* flags */ err = get_int(&mesg, &an_int); if (err == -ENOENT) @@ -546,10 +460,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) err = check_export(nd.dentry->d_inode, exp.ex_flags); if (err) goto out; - - err = fsloc_parse(&mesg, buf, &exp.ex_fslocs); - if (err) - goto out; } expp = svc_export_lookup(&exp); @@ -563,7 +473,6 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) else exp_put(expp); out: - kfree(exp.ex_path); if (nd.dentry) path_release(&nd); out_no_path: @@ -573,8 +482,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) return err; } -static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); +static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong); static int svc_export_show(struct seq_file *m, struct cache_detail *cd, @@ -593,8 +501,8 @@ static int svc_export_show(struct seq_file *m, seq_putc(m, '('); if (test_bit(CACHE_VALID, &h->flags) && !test_bit(CACHE_NEGATIVE, &h->flags)) - exp_flags(m, exp->ex_flags, exp->ex_fsid, - exp->ex_anon_uid, exp->ex_anon_gid, &exp->ex_fslocs); + exp_flags(m, exp->ex_flags, exp->ex_fsid, + exp->ex_anon_uid, exp->ex_anon_gid); seq_puts(m, ")\n"); return 0; } @@ -616,10 +524,6 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) new->ex_client = item->ex_client; new->ex_dentry = dget(item->ex_dentry); new->ex_mnt = mntget(item->ex_mnt); - new->ex_path = NULL; - new->ex_fslocs.locations = NULL; - new->ex_fslocs.locations_count = 0; - new->ex_fslocs.migrated = 0; } static void export_update(struct cache_head *cnew, struct cache_head *citem) @@ -631,14 +535,6 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem) new->ex_anon_uid = item->ex_anon_uid; new->ex_anon_gid = item->ex_anon_gid; new->ex_fsid = item->ex_fsid; - new->ex_path = item->ex_path; - item->ex_path = NULL; - new->ex_fslocs.locations = item->ex_fslocs.locations; - item->ex_fslocs.locations = NULL; - new->ex_fslocs.locations_count = item->ex_fslocs.locations_count; - item->ex_fslocs.locations_count = 0; - new->ex_fslocs.migrated = item->ex_fslocs.migrated; - item->ex_fslocs.migrated = 0; } static struct cache_head *svc_export_alloc(void) @@ -1152,21 +1048,30 @@ int exp_pseudoroot(struct auth_domain *clp, struct svc_fh *fhp, struct cache_req *creq) { + struct svc_expkey *fsid_key; struct svc_export *exp; int rv; u32 fsidv[2]; mk_fsid_v1(fsidv, 0); - exp = exp_find(clp, 1, fsidv, creq); - if (IS_ERR(exp) && PTR_ERR(exp) == -EAGAIN) + fsid_key = exp_find_key(clp, 1, fsidv, creq); + if (IS_ERR(fsid_key) && PTR_ERR(fsid_key) == -EAGAIN) return nfserr_dropit; - if (exp == NULL) + if (!fsid_key || IS_ERR(fsid_key)) return nfserr_perm; + + exp = exp_get_by_name(clp, fsid_key->ek_mnt, fsid_key->ek_dentry, creq); + if (exp == NULL) + rv = nfserr_perm; else if (IS_ERR(exp)) - return nfserrno(PTR_ERR(exp)); - rv = fh_compose(fhp, exp, exp->ex_dentry, NULL); - exp_put(exp); + rv = nfserrno(PTR_ERR(exp)); + else { + rv = fh_compose(fhp, exp, + fsid_key->ek_dentry, NULL); + exp_put(exp); + } + cache_put(&fsid_key->h, &svc_expkey_cache); return rv; } @@ -1253,8 +1158,7 @@ static struct flags { { 0, {"", ""}} }; -static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) +static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong) { int first = 0; struct flags *flg; @@ -1270,21 +1174,6 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, seq_printf(m, "%sanonuid=%d", first++?",":"", anonu); if (anong != (gid_t)-2 && anong != (0x10000-2)) seq_printf(m, "%sanongid=%d", first++?",":"", anong); - if (fsloc && fsloc->locations_count > 0) { - char *loctype = (fsloc->migrated) ? "refer" : "replicas"; - int i; - - seq_printf(m, "%s%s=", first++?",":"", loctype); - seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\"); - seq_putc(m, '@'); - seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\"); - for (i = 1; i < fsloc->locations_count; i++) { - seq_putc(m, ';'); - seq_escape(m, fsloc->locations[i].path, ",;@ \t\n\\"); - seq_putc(m, '@'); - seq_escape(m, fsloc->locations[i].hosts, ",;@ \t\n\\"); - } - } } static int e_show(struct seq_file *m, void *p) diff --git a/trunk/fs/nfsd/nfs2acl.c b/trunk/fs/nfsd/nfs2acl.c index 9187755661df..fe56b38364cc 100644 --- a/trunk/fs/nfsd/nfs2acl.c +++ b/trunk/fs/nfsd/nfs2acl.c @@ -241,7 +241,7 @@ static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = w; while (w > 0) { - if (!rqstp->rq_respages[rqstp->rq_resused++]) + if (!svc_take_res_page(rqstp)) return 0; w -= PAGE_SIZE; } @@ -333,5 +333,4 @@ struct svc_version nfsd_acl_version2 = { .vs_proc = nfsd_acl_procedures2, .vs_dispatch = nfsd_dispatch, .vs_xdrsize = NFS3_SVC_XDRSIZE, - .vs_hidden = 1, }; diff --git a/trunk/fs/nfsd/nfs3acl.c b/trunk/fs/nfsd/nfs3acl.c index d4bdc00c1169..16e10c170aed 100644 --- a/trunk/fs/nfsd/nfs3acl.c +++ b/trunk/fs/nfsd/nfs3acl.c @@ -185,7 +185,7 @@ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = w; while (w > 0) { - if (!rqstp->rq_respages[rqstp->rq_resused++]) + if (!svc_take_res_page(rqstp)) return 0; w -= PAGE_SIZE; } @@ -263,6 +263,5 @@ struct svc_version nfsd_acl_version3 = { .vs_proc = nfsd_acl_procedures3, .vs_dispatch = nfsd_dispatch, .vs_xdrsize = NFS3_SVC_XDRSIZE, - .vs_hidden = 1, }; diff --git a/trunk/fs/nfsd/nfs3proc.c b/trunk/fs/nfsd/nfs3proc.c index a5ebc7dbb384..f61142afea44 100644 --- a/trunk/fs/nfsd/nfs3proc.c +++ b/trunk/fs/nfsd/nfs3proc.c @@ -160,7 +160,6 @@ nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, struct nfsd3_readres *resp) { int nfserr; - u32 max_blocksize = svc_max_payload(rqstp); dprintk("nfsd: READ(3) %s %lu bytes at %lu\n", SVCFH_fmt(&argp->fh), @@ -173,15 +172,15 @@ nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp, */ resp->count = argp->count; - if (max_blocksize < resp->count) - resp->count = max_blocksize; + if (NFSSVC_MAXBLKSIZE < resp->count) + resp->count = NFSSVC_MAXBLKSIZE; svc_reserve(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + resp->count +4); fh_copy(&resp->fh, &argp->fh); nfserr = nfsd_read(rqstp, &resp->fh, NULL, argp->offset, - rqstp->rq_vec, argp->vlen, + argp->vec, argp->vlen, &resp->count); if (nfserr == 0) { struct inode *inode = resp->fh.fh_dentry->d_inode; @@ -211,7 +210,7 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp, resp->committed = argp->stable; nfserr = nfsd_write(rqstp, &resp->fh, NULL, argp->offset, - rqstp->rq_vec, argp->vlen, + argp->vec, argp->vlen, argp->len, &resp->committed); resp->count = argp->count; @@ -539,16 +538,15 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, struct nfsd3_fsinfores *resp) { int nfserr; - u32 max_blocksize = svc_max_payload(rqstp); dprintk("nfsd: FSINFO(3) %s\n", SVCFH_fmt(&argp->fh)); - resp->f_rtmax = max_blocksize; - resp->f_rtpref = max_blocksize; + resp->f_rtmax = NFSSVC_MAXBLKSIZE; + resp->f_rtpref = NFSSVC_MAXBLKSIZE; resp->f_rtmult = PAGE_SIZE; - resp->f_wtmax = max_blocksize; - resp->f_wtpref = max_blocksize; + resp->f_wtmax = NFSSVC_MAXBLKSIZE; + resp->f_wtpref = NFSSVC_MAXBLKSIZE; resp->f_wtmult = PAGE_SIZE; resp->f_dtpref = PAGE_SIZE; resp->f_maxfilesize = ~(u32) 0; diff --git a/trunk/fs/nfsd/nfs3xdr.c b/trunk/fs/nfsd/nfs3xdr.c index 247d518248bf..243d94b9653a 100644 --- a/trunk/fs/nfsd/nfs3xdr.c +++ b/trunk/fs/nfsd/nfs3xdr.c @@ -330,7 +330,6 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p, { unsigned int len; int v,pn; - u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh)) || !(p = xdr_decode_hyper(p, &args->offset))) @@ -338,16 +337,17 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p, len = args->count = ntohl(*p++); - if (len > max_blocksize) - len = max_blocksize; + if (len > NFSSVC_MAXBLKSIZE) + len = NFSSVC_MAXBLKSIZE; /* set up the kvec */ v=0; while (len > 0) { - pn = rqstp->rq_resused++; - rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); - rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; - len -= rqstp->rq_vec[v].iov_len; + pn = rqstp->rq_resused; + svc_take_page(rqstp); + args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]); + args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; + len -= args->vec[v].iov_len; v++; } args->vlen = v; @@ -359,7 +359,6 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, struct nfsd3_writeargs *args) { unsigned int len, v, hdr; - u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh)) || !(p = xdr_decode_hyper(p, &args->offset))) @@ -374,22 +373,22 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, rqstp->rq_arg.len - hdr < len) return 0; - rqstp->rq_vec[0].iov_base = (void*)p; - rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; + args->vec[0].iov_base = (void*)p; + args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr; - if (len > max_blocksize) - len = max_blocksize; + if (len > NFSSVC_MAXBLKSIZE) + len = NFSSVC_MAXBLKSIZE; v= 0; - while (len > rqstp->rq_vec[v].iov_len) { - len -= rqstp->rq_vec[v].iov_len; + while (len > args->vec[v].iov_len) { + len -= args->vec[v].iov_len; v++; - rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]); - rqstp->rq_vec[v].iov_len = PAGE_SIZE; + args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]); + args->vec[v].iov_len = PAGE_SIZE; } - rqstp->rq_vec[v].iov_len = len; + args->vec[v].iov_len = len; args->vlen = v+1; - return args->count == args->len && rqstp->rq_vec[0].iov_len > 0; + return args->count == args->len && args->vec[0].iov_len > 0; } int @@ -447,11 +446,11 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p, * This page appears in the rq_res.pages list, but as pages_len is always * 0, it won't get in the way */ + svc_take_page(rqstp); len = ntohl(*p++); if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) return 0; - args->tname = new = - page_address(rqstp->rq_respages[rqstp->rq_resused++]); + args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); args->tlen = len; /* first copy and check from the first page */ old = (char*)p; @@ -523,8 +522,8 @@ nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p, { if (!(p = decode_fh(p, &args->fh))) return 0; - args->buffer = - page_address(rqstp->rq_respages[rqstp->rq_resused++]); + svc_take_page(rqstp); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); return xdr_argsize_check(rqstp, p); } @@ -555,8 +554,8 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, if (args->count > PAGE_SIZE) args->count = PAGE_SIZE; - args->buffer = - page_address(rqstp->rq_respages[rqstp->rq_resused++]); + svc_take_page(rqstp); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); return xdr_argsize_check(rqstp, p); } @@ -566,7 +565,6 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, struct nfsd3_readdirargs *args) { int len, pn; - u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh))) return 0; @@ -575,12 +573,13 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p, args->dircount = ntohl(*p++); args->count = ntohl(*p++); - len = (args->count > max_blocksize) ? max_blocksize : + len = (args->count > NFSSVC_MAXBLKSIZE) ? NFSSVC_MAXBLKSIZE : args->count; args->count = len; while (len > 0) { - pn = rqstp->rq_resused++; + pn = rqstp->rq_resused; + svc_take_page(rqstp); if (!args->buffer) args->buffer = page_address(rqstp->rq_respages[pn]); len -= PAGE_SIZE; @@ -669,6 +668,7 @@ nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->len; if (resp->len & 3) { /* need to pad the tail */ + rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); @@ -693,6 +693,7 @@ nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->count; if (resp->count & 3) { /* need to pad the tail */ + rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3); @@ -767,6 +768,7 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = (resp->count) << 2; /* add the 'tail' to the end of the 'head' page - page 0. */ + rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p++ = 0; /* no more entries */ *p++ = htonl(resp->common.err == nfserr_eof); diff --git a/trunk/fs/nfsd/nfs4acl.c b/trunk/fs/nfsd/nfs4acl.c index 5d94555cdc83..edb107e61b91 100644 --- a/trunk/fs/nfsd/nfs4acl.c +++ b/trunk/fs/nfsd/nfs4acl.c @@ -63,8 +63,6 @@ #define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \ | NFS4_ACE_DIRECTORY_INHERIT_ACE | NFS4_ACE_INHERIT_ONLY_ACE) -#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS | NFS4_ACE_IDENTIFIER_GROUP) - #define MASK_EQUAL(mask1, mask2) \ ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) ) @@ -98,26 +96,24 @@ deny_mask(u32 allow_mask, unsigned int flags) /* XXX: modify functions to return NFS errors; they're only ever * used by nfs code, after all.... */ -/* We only map from NFSv4 to POSIX ACLs when setting ACLs, when we err on the - * side of being more restrictive, so the mode bit mapping below is - * pessimistic. An optimistic version would be needed to handle DENY's, - * but we espect to coalesce all ALLOWs and DENYs before mapping to mode - * bits. */ - -static void -low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) +static int +mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) { - u32 write_mode = NFS4_WRITE_MODE; + u32 ignore = 0; - if (flags & NFS4_ACL_DIR) - write_mode |= NFS4_ACE_DELETE_CHILD; + if (!(flags & NFS4_ACL_DIR)) + ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */ + perm |= ignore; *mode = 0; if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE) *mode |= ACL_READ; - if ((perm & write_mode) == write_mode) + if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE) *mode |= ACL_WRITE; if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE) *mode |= ACL_EXECUTE; + if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags))) + return -EINVAL; + return 0; } struct ace_container { @@ -342,6 +338,38 @@ sort_pacl(struct posix_acl *pacl) return; } +static int +write_pace(struct nfs4_ace *ace, struct posix_acl *pacl, + struct posix_acl_entry **pace, short tag, unsigned int flags) +{ + struct posix_acl_entry *this = *pace; + + if (*pace == pacl->a_entries + pacl->a_count) + return -EINVAL; /* fell off the end */ + (*pace)++; + this->e_tag = tag; + if (tag == ACL_USER_OBJ) + flags |= NFS4_ACL_OWNER; + if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags)) + return -EINVAL; + this->e_id = (tag == ACL_USER || tag == ACL_GROUP ? + ace->who : ACL_UNDEFINED_ID); + return 0; +} + +static struct nfs4_ace * +get_next_v4_ace(struct list_head **p, struct list_head *head) +{ + struct nfs4_ace *ace; + + *p = (*p)->next; + if (*p == head) + return NULL; + ace = list_entry(*p, struct nfs4_ace, l_ace); + + return ace; +} + int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, struct posix_acl **dpacl, unsigned int flags) @@ -357,23 +385,42 @@ nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, goto out; error = nfs4_acl_split(acl, dacl); - if (error) + if (error < 0) goto out_acl; - *pacl = _nfsv4_to_posix_one(acl, flags); - if (IS_ERR(*pacl)) { - error = PTR_ERR(*pacl); - *pacl = NULL; - goto out_acl; + if (pacl != NULL) { + if (acl->naces == 0) { + error = -ENODATA; + goto try_dpacl; + } + + *pacl = _nfsv4_to_posix_one(acl, flags); + if (IS_ERR(*pacl)) { + error = PTR_ERR(*pacl); + *pacl = NULL; + goto out_acl; + } } - *dpacl = _nfsv4_to_posix_one(dacl, flags); - if (IS_ERR(*dpacl)) { - error = PTR_ERR(*dpacl); - *dpacl = NULL; +try_dpacl: + if (dpacl != NULL) { + if (dacl->naces == 0) { + if (pacl == NULL || *pacl == NULL) + error = -ENODATA; + goto out_acl; + } + + error = 0; + *dpacl = _nfsv4_to_posix_one(dacl, flags); + if (IS_ERR(*dpacl)) { + error = PTR_ERR(*dpacl); + *dpacl = NULL; + goto out_acl; + } } + out_acl: - if (error) { + if (error && pacl) { posix_acl_release(*pacl); *pacl = NULL; } @@ -382,311 +429,349 @@ nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, return error; } -/* - * While processing the NFSv4 ACE, this maintains bitmasks representing - * which permission bits have been allowed and which denied to a given - * entity: */ -struct posix_ace_state { - u32 allow; - u32 deny; -}; - -struct posix_user_ace_state { - uid_t uid; - struct posix_ace_state perms; -}; - -struct posix_ace_state_array { - int n; - struct posix_user_ace_state aces[]; -}; - -/* - * While processing the NFSv4 ACE, this maintains the partial permissions - * calculated so far: */ - -struct posix_acl_state { - struct posix_ace_state owner; - struct posix_ace_state group; - struct posix_ace_state other; - struct posix_ace_state everyone; - struct posix_ace_state mask; /* Deny unused in this case */ - struct posix_ace_state_array *users; - struct posix_ace_state_array *groups; -}; - static int -init_state(struct posix_acl_state *state, int cnt) +same_who(struct nfs4_ace *a, struct nfs4_ace *b) { - int alloc; - - memset(state, 0, sizeof(struct posix_acl_state)); - /* - * In the worst case, each individual acl could be for a distinct - * named user or group, but we don't no which, so we allocate - * enough space for either: - */ - alloc = sizeof(struct posix_ace_state_array) - + cnt*sizeof(struct posix_ace_state); - state->users = kzalloc(alloc, GFP_KERNEL); - if (!state->users) - return -ENOMEM; - state->groups = kzalloc(alloc, GFP_KERNEL); - if (!state->groups) { - kfree(state->users); - return -ENOMEM; - } - return 0; + return a->whotype == b->whotype && + (a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who); } -static void -free_state(struct posix_acl_state *state) { - kfree(state->users); - kfree(state->groups); +static int +complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny, + unsigned int flags) +{ + int ignore = 0; + if (!(flags & NFS4_ACL_DIR)) + ignore |= NFS4_ACE_DELETE_CHILD; + return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags), + ignore|deny->access_mask) && + allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE && + deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE && + allow->flag == deny->flag && + same_who(allow, deny); } -static inline void add_to_mask(struct posix_acl_state *state, struct posix_ace_state *astate) +static inline int +user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p, + struct posix_acl *pacl, struct posix_acl_entry **pace, + unsigned int flags) { - state->mask.allow |= astate->allow; -} + int error = -EINVAL; + struct nfs4_ace *ace, *ace2; -/* - * Certain bits (SYNCHRONIZE, DELETE, WRITE_OWNER, READ/WRITE_NAMED_ATTRS, - * READ_ATTRIBUTES, READ_ACL) are currently unenforceable and don't translate - * to traditional read/write/execute permissions. - * - * It's problematic to reject acls that use certain mode bits, because it - * places the burden on users to learn the rules about which bits one - * particular server sets, without giving the user a lot of help--we return an - * error that could mean any number of different things. To make matters - * worse, the problematic bits might be introduced by some application that's - * automatically mapping from some other acl model. - * - * So wherever possible we accept anything, possibly erring on the side of - * denying more permissions than necessary. - * - * However we do reject *explicit* DENY's of a few bits representing - * permissions we could never deny: - */ + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + if (ace2type(ace) != ACL_USER_OBJ) + goto out; + error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags); + if (error < 0) + goto out; + error = -EINVAL; + ace2 = get_next_v4_ace(p, &n4acl->ace_head); + if (ace2 == NULL) + goto out; + if (!complementary_ace_pair(ace, ace2, flags)) + goto out; + error = 0; +out: + return error; +} -static inline int check_deny(u32 mask, int isowner) +static inline int +users_from_v4(struct nfs4_acl *n4acl, struct list_head **p, + struct nfs4_ace **mask_ace, + struct posix_acl *pacl, struct posix_acl_entry **pace, + unsigned int flags) { - if (mask & (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL)) - return -EINVAL; - if (!isowner) - return 0; - if (mask & (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)) - return -EINVAL; - return 0; + int error = -EINVAL; + struct nfs4_ace *ace, *ace2; + + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + while (ace2type(ace) == ACL_USER) { + if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) + goto out; + if (*mask_ace && + !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) + goto out; + *mask_ace = ace; + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) + goto out; + error = write_pace(ace, pacl, pace, ACL_USER, flags); + if (error < 0) + goto out; + error = -EINVAL; + ace2 = get_next_v4_ace(p, &n4acl->ace_head); + if (ace2 == NULL) + goto out; + if (!complementary_ace_pair(ace, ace2, flags)) + goto out; + if ((*mask_ace)->flag != ace2->flag || + !same_who(*mask_ace, ace2)) + goto out; + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + } + error = 0; +out: + return error; } -static struct posix_acl * -posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) +static inline int +group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p, + struct nfs4_ace **mask_ace, + struct posix_acl *pacl, struct posix_acl_entry **pace, + unsigned int flags) { - struct posix_acl_entry *pace; - struct posix_acl *pacl; - int nace; - int i, error = 0; + int error = -EINVAL; + struct nfs4_ace *ace, *ace2; + struct ace_container *ac; + struct list_head group_l; - nace = 4 + state->users->n + state->groups->n; - pacl = posix_acl_alloc(nace, GFP_KERNEL); - if (!pacl) - return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&group_l); + ace = list_entry(*p, struct nfs4_ace, l_ace); - pace = pacl->a_entries; - pace->e_tag = ACL_USER_OBJ; - error = check_deny(state->owner.deny, 1); - if (error) - goto out_err; - low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; - - for (i=0; i < state->users->n; i++) { - pace++; - pace->e_tag = ACL_USER; - error = check_deny(state->users->aces[i].perms.deny, 0); - if (error) - goto out_err; - low_mode_from_nfs4(state->users->aces[i].perms.allow, - &pace->e_perm, flags); - pace->e_id = state->users->aces[i].uid; - add_to_mask(state, &state->users->aces[i].perms); - } + /* group owner (mask and allow aces) */ - pace++; - pace->e_tag = ACL_GROUP_OBJ; - error = check_deny(state->group.deny, 0); - if (error) - goto out_err; - low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; - add_to_mask(state, &state->group); - - for (i=0; i < state->groups->n; i++) { - pace++; - pace->e_tag = ACL_GROUP; - error = check_deny(state->groups->aces[i].perms.deny, 0); - if (error) - goto out_err; - low_mode_from_nfs4(state->groups->aces[i].perms.allow, - &pace->e_perm, flags); - pace->e_id = state->groups->aces[i].uid; - add_to_mask(state, &state->groups->aces[i].perms); + if (pacl->a_count != 3) { + /* then the group owner should be preceded by mask */ + if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) + goto out; + if (*mask_ace && + !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) + goto out; + *mask_ace = ace; + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + + if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace)) + goto out; } - pace++; - pace->e_tag = ACL_MASK; - low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; + if (ace2type(ace) != ACL_GROUP_OBJ) + goto out; - pace++; - pace->e_tag = ACL_OTHER; - error = check_deny(state->other.deny, 0); - if (error) - goto out_err; - low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; + ac = kmalloc(sizeof(*ac), GFP_KERNEL); + error = -ENOMEM; + if (ac == NULL) + goto out; + ac->ace = ace; + list_add_tail(&ac->ace_l, &group_l); - return pacl; -out_err: - posix_acl_release(pacl); - return ERR_PTR(error); -} + error = -EINVAL; + if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) + goto out; -static inline void allow_bits(struct posix_ace_state *astate, u32 mask) -{ - /* Allow all bits in the mask not already denied: */ - astate->allow |= mask & ~astate->deny; -} + error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags); + if (error < 0) + goto out; -static inline void deny_bits(struct posix_ace_state *astate, u32 mask) -{ - /* Deny all bits in the mask not already allowed: */ - astate->deny |= mask & ~astate->allow; -} + error = -EINVAL; + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; -static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array *a, uid_t uid) -{ - int i; + /* groups (mask and allow aces) */ + + while (ace2type(ace) == ACL_GROUP) { + if (*mask_ace == NULL) + goto out; + + if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE || + !MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask)) + goto out; + *mask_ace = ace; + + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + ac = kmalloc(sizeof(*ac), GFP_KERNEL); + error = -ENOMEM; + if (ac == NULL) + goto out; + error = -EINVAL; + if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE || + !same_who(ace, *mask_ace)) + goto out; - for (i = 0; i < a->n; i++) - if (a->aces[i].uid == uid) - return i; - /* Not found: */ - a->n++; - a->aces[i].uid = uid; - a->aces[i].perms.allow = state->everyone.allow; - a->aces[i].perms.deny = state->everyone.deny; + ac->ace = ace; + list_add_tail(&ac->ace_l, &group_l); + + error = write_pace(ace, pacl, pace, ACL_GROUP, flags); + if (error < 0) + goto out; + error = -EINVAL; + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + } + + /* group owner (deny ace) */ + + if (ace2type(ace) != ACL_GROUP_OBJ) + goto out; + ac = list_entry(group_l.next, struct ace_container, ace_l); + ace2 = ac->ace; + if (!complementary_ace_pair(ace2, ace, flags)) + goto out; + list_del(group_l.next); + kfree(ac); + + /* groups (deny aces) */ + + while (!list_empty(&group_l)) { + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + if (ace2type(ace) != ACL_GROUP) + goto out; + ac = list_entry(group_l.next, struct ace_container, ace_l); + ace2 = ac->ace; + if (!complementary_ace_pair(ace2, ace, flags)) + goto out; + list_del(group_l.next); + kfree(ac); + } - return i; + ace = get_next_v4_ace(p, &n4acl->ace_head); + if (ace == NULL) + goto out; + if (ace2type(ace) != ACL_OTHER) + goto out; + error = 0; +out: + while (!list_empty(&group_l)) { + ac = list_entry(group_l.next, struct ace_container, ace_l); + list_del(group_l.next); + kfree(ac); + } + return error; } -static void deny_bits_array(struct posix_ace_state_array *a, u32 mask) +static inline int +mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p, + struct nfs4_ace **mask_ace, + struct posix_acl *pacl, struct posix_acl_entry **pace, + unsigned int flags) { - int i; + int error = -EINVAL; + struct nfs4_ace *ace; - for (i=0; i < a->n; i++) - deny_bits(&a->aces[i].perms, mask); + ace = list_entry(*p, struct nfs4_ace, l_ace); + if (pacl->a_count != 3) { + if (*mask_ace == NULL) + goto out; + (*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags); + write_pace(*mask_ace, pacl, pace, ACL_MASK, flags); + } + error = 0; +out: + return error; } -static void allow_bits_array(struct posix_ace_state_array *a, u32 mask) +static inline int +other_from_v4(struct nfs4_acl *n4acl, struct list_head **p, + struct posix_acl *pacl, struct posix_acl_entry **pace, + unsigned int flags) { - int i; + int error = -EINVAL; + struct nfs4_ace *ace, *ace2; - for (i=0; i < a->n; i++) - allow_bits(&a->aces[i].perms, mask); + ace = list_entry(*p, struct nfs4_ace, l_ace); + if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) + goto out; + error = write_pace(ace, pacl, pace, ACL_OTHER, flags); + if (error < 0) + goto out; + error = -EINVAL; + ace2 = get_next_v4_ace(p, &n4acl->ace_head); + if (ace2 == NULL) + goto out; + if (!complementary_ace_pair(ace, ace2, flags)) + goto out; + error = 0; +out: + return error; } -static void process_one_v4_ace(struct posix_acl_state *state, - struct nfs4_ace *ace) +static int +calculate_posix_ace_count(struct nfs4_acl *n4acl) { - u32 mask = ace->access_mask; - int i; - - switch (ace2type(ace)) { - case ACL_USER_OBJ: - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { - allow_bits(&state->owner, mask); - } else { - deny_bits(&state->owner, mask); - } - break; - case ACL_USER: - i = find_uid(state, state->users, ace->who); - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { - allow_bits(&state->users->aces[i].perms, mask); - } else { - deny_bits(&state->users->aces[i].perms, mask); - mask = state->users->aces[i].perms.deny; - deny_bits(&state->owner, mask); - } - break; - case ACL_GROUP_OBJ: - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { - allow_bits(&state->group, mask); - } else { - deny_bits(&state->group, mask); - mask = state->group.deny; - deny_bits(&state->owner, mask); - deny_bits(&state->everyone, mask); - deny_bits_array(state->users, mask); - deny_bits_array(state->groups, mask); - } - break; - case ACL_GROUP: - i = find_uid(state, state->groups, ace->who); - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { - allow_bits(&state->groups->aces[i].perms, mask); - } else { - deny_bits(&state->groups->aces[i].perms, mask); - mask = state->groups->aces[i].perms.deny; - deny_bits(&state->owner, mask); - deny_bits(&state->group, mask); - deny_bits(&state->everyone, mask); - deny_bits_array(state->users, mask); - deny_bits_array(state->groups, mask); - } - break; - case ACL_OTHER: - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { - allow_bits(&state->owner, mask); - allow_bits(&state->group, mask); - allow_bits(&state->other, mask); - allow_bits(&state->everyone, mask); - allow_bits_array(state->users, mask); - allow_bits_array(state->groups, mask); - } else { - deny_bits(&state->owner, mask); - deny_bits(&state->group, mask); - deny_bits(&state->other, mask); - deny_bits(&state->everyone, mask); - deny_bits_array(state->users, mask); - deny_bits_array(state->groups, mask); - } + if (n4acl->naces == 6) /* owner, owner group, and other only */ + return 3; + else { /* Otherwise there must be a mask entry. */ + /* Also, the remaining entries are for named users and + * groups, and come in threes (mask, allow, deny): */ + if (n4acl->naces < 7) + return -EINVAL; + if ((n4acl->naces - 7) % 3) + return -EINVAL; + return 4 + (n4acl->naces - 7)/3; } } + static struct posix_acl * _nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags) { - struct posix_acl_state state; struct posix_acl *pacl; - struct nfs4_ace *ace; - int ret; + int error = -EINVAL, nace = 0; + struct list_head *p; + struct nfs4_ace *mask_ace = NULL; + struct posix_acl_entry *pace; - ret = init_state(&state, n4acl->naces); - if (ret) - return ERR_PTR(ret); + nace = calculate_posix_ace_count(n4acl); + if (nace < 0) + goto out_err; - list_for_each_entry(ace, &n4acl->ace_head, l_ace) - process_one_v4_ace(&state, ace); + pacl = posix_acl_alloc(nace, GFP_KERNEL); + error = -ENOMEM; + if (pacl == NULL) + goto out_err; - pacl = posix_state_to_acl(&state, flags); + pace = &pacl->a_entries[0]; + p = &n4acl->ace_head; - free_state(&state); + error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags); + if (error) + goto out_acl; - if (!IS_ERR(pacl)) - sort_pacl(pacl); + error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); + if (error) + goto out_acl; + + error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace, + flags); + if (error) + goto out_acl; + + error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags); + if (error) + goto out_acl; + error = other_from_v4(n4acl, &p, pacl, &pace, flags); + if (error) + goto out_acl; + + error = -EINVAL; + if (p->next != &n4acl->ace_head) + goto out_acl; + if (pace != pacl->a_entries + pacl->a_count) + goto out_acl; + + sort_pacl(pacl); + + return pacl; +out_acl: + posix_acl_release(pacl); +out_err: + pacl = ERR_PTR(error); return pacl; } @@ -700,41 +785,22 @@ nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl) list_for_each_safe(h, n, &acl->ace_head) { ace = list_entry(h, struct nfs4_ace, l_ace); - if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE && - ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE) - return -EINVAL; - - if (ace->flag & ~NFS4_SUPPORTED_FLAGS) - return -EINVAL; - - switch (ace->flag & NFS4_INHERITANCE_FLAGS) { - case 0: - /* Leave this ace in the effective acl: */ + if ((ace->flag & NFS4_INHERITANCE_FLAGS) + != NFS4_INHERITANCE_FLAGS) continue; - case NFS4_INHERITANCE_FLAGS: - /* Add this ace to the default acl and remove it - * from the effective acl: */ - error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, - ace->access_mask, ace->whotype, ace->who); - if (error) - return error; - list_del(h); - kfree(ace); - acl->naces--; - break; - case NFS4_INHERITANCE_FLAGS & ~NFS4_ACE_INHERIT_ONLY_ACE: - /* Add this ace to the default, but leave it in - * the effective acl as well: */ - error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, + + error = nfs4_acl_add_ace(dacl, ace->type, ace->flag, ace->access_mask, ace->whotype, ace->who); - if (error) - return error; - break; - default: - return -EINVAL; - } + if (error < 0) + goto out; + + list_del(h); + kfree(ace); + acl->naces--; } - return 0; + +out: + return error; } static short @@ -864,6 +930,23 @@ nfs4_acl_write_who(int who, char *p) return -1; } +static inline int +match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who) +{ + switch (ace->whotype) { + case NFS4_ACL_WHO_NAMED: + return who == ace->who; + case NFS4_ACL_WHO_OWNER: + return who == owner; + case NFS4_ACL_WHO_GROUP: + return who == group; + case NFS4_ACL_WHO_EVERYONE: + return 1; + default: + return 0; + } +} + EXPORT_SYMBOL(nfs4_acl_new); EXPORT_SYMBOL(nfs4_acl_free); EXPORT_SYMBOL(nfs4_acl_add_ace); diff --git a/trunk/fs/nfsd/nfs4proc.c b/trunk/fs/nfsd/nfs4proc.c index 8333db12caca..15ded7a30a72 100644 --- a/trunk/fs/nfsd/nfs4proc.c +++ b/trunk/fs/nfsd/nfs4proc.c @@ -646,7 +646,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ *p++ = nfssvc_boot.tv_usec; status = nfsd_write(rqstp, current_fh, filp, write->wr_offset, - rqstp->rq_vec, write->wr_vlen, write->wr_buflen, + write->wr_vec, write->wr_vlen, write->wr_buflen, &write->wr_how_written); if (filp) fput(filp); @@ -802,29 +802,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, * SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH * require a valid current filehandle */ - if (!current_fh->fh_dentry) { - if (!((op->opnum == OP_PUTFH) || - (op->opnum == OP_PUTROOTFH) || - (op->opnum == OP_SETCLIENTID) || - (op->opnum == OP_SETCLIENTID_CONFIRM) || - (op->opnum == OP_RENEW) || - (op->opnum == OP_RESTOREFH) || - (op->opnum == OP_RELEASE_LOCKOWNER))) { - op->status = nfserr_nofilehandle; - goto encode_op; - } - } - /* Check must be done at start of each operation, except - * for GETATTR and ops not listed as returning NFS4ERR_MOVED - */ - else if (current_fh->fh_export->ex_fslocs.migrated && - !((op->opnum == OP_GETATTR) || - (op->opnum == OP_PUTROOTFH) || - (op->opnum == OP_PUTPUBFH) || - (op->opnum == OP_RENEW) || - (op->opnum == OP_SETCLIENTID) || - (op->opnum == OP_RELEASE_LOCKOWNER))) { - op->status = nfserr_moved; + if ((!current_fh->fh_dentry) && + !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) || + (op->opnum == OP_SETCLIENTID) || + (op->opnum == OP_SETCLIENTID_CONFIRM) || + (op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) || + (op->opnum == OP_RELEASE_LOCKOWNER))) { + op->status = nfserr_nofilehandle; goto encode_op; } switch (op->opnum) { diff --git a/trunk/fs/nfsd/nfs4xdr.c b/trunk/fs/nfsd/nfs4xdr.c index 41fc241b729a..5be00436b5b8 100644 --- a/trunk/fs/nfsd/nfs4xdr.c +++ b/trunk/fs/nfsd/nfs4xdr.c @@ -60,14 +60,6 @@ #define NFSDDBG_FACILITY NFSDDBG_XDR -/* - * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing - * directory in order to indicate to the client that a filesystem boundary is present - * We use a fixed fsid for a referral - */ -#define NFS4_REFERRAL_FSID_MAJOR 0x8000000ULL -#define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL - static int check_filename(char *str, int len, int err) { @@ -934,26 +926,26 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); goto xdr_error; } - argp->rqstp->rq_vec[0].iov_base = p; - argp->rqstp->rq_vec[0].iov_len = avail; + write->wr_vec[0].iov_base = p; + write->wr_vec[0].iov_len = avail; v = 0; len = write->wr_buflen; - while (len > argp->rqstp->rq_vec[v].iov_len) { - len -= argp->rqstp->rq_vec[v].iov_len; + while (len > write->wr_vec[v].iov_len) { + len -= write->wr_vec[v].iov_len; v++; - argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]); + write->wr_vec[v].iov_base = page_address(argp->pagelist[0]); argp->pagelist++; if (argp->pagelen >= PAGE_SIZE) { - argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE; + write->wr_vec[v].iov_len = PAGE_SIZE; argp->pagelen -= PAGE_SIZE; } else { - argp->rqstp->rq_vec[v].iov_len = argp->pagelen; + write->wr_vec[v].iov_len = argp->pagelen; argp->pagelen -= len; } } - argp->end = (u32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len); - argp->p = (u32*) (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); - argp->rqstp->rq_vec[v].iov_len = len; + argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len); + argp->p = (u32*) (write->wr_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); + write->wr_vec[v].iov_len = len; write->wr_vlen = v+1; DECODE_TAIL; @@ -1231,119 +1223,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) stateowner->so_replay.rp_buflen); \ } } while (0); -/* Encode as an array of strings the string given with components - * seperated @sep. - */ -static int nfsd4_encode_components(char sep, char *components, - u32 **pp, int *buflen) -{ - u32 *p = *pp; - u32 *countp = p; - int strlen, count=0; - char *str, *end; - - dprintk("nfsd4_encode_components(%s)\n", components); - if ((*buflen -= 4) < 0) - return nfserr_resource; - WRITE32(0); /* We will fill this in with @count later */ - end = str = components; - while (*end) { - for (; *end && (*end != sep); end++) - ; /* Point to end of component */ - strlen = end - str; - if (strlen) { - if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0) - return nfserr_resource; - WRITE32(strlen); - WRITEMEM(str, strlen); - count++; - } - else - end++; - str = end; - } - *pp = p; - p = countp; - WRITE32(count); - return 0; -} - -/* - * encode a location element of a fs_locations structure - */ -static int nfsd4_encode_fs_location4(struct nfsd4_fs_location *location, - u32 **pp, int *buflen) -{ - int status; - u32 *p = *pp; - - status = nfsd4_encode_components(':', location->hosts, &p, buflen); - if (status) - return status; - status = nfsd4_encode_components('/', location->path, &p, buflen); - if (status) - return status; - *pp = p; - return 0; -} - -/* - * Return the path to an export point in the pseudo filesystem namespace - * Returned string is safe to use as long as the caller holds a reference - * to @exp. - */ -static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp) -{ - struct svc_fh tmp_fh; - char *path, *rootpath; - int stat; - - fh_init(&tmp_fh, NFS4_FHSIZE); - stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle); - if (stat) - return ERR_PTR(stat); - rootpath = tmp_fh.fh_export->ex_path; - - path = exp->ex_path; - - if (strncmp(path, rootpath, strlen(rootpath))) { - printk("nfsd: fs_locations failed;" - "%s is not contained in %s\n", path, rootpath); - return ERR_PTR(-EOPNOTSUPP); - } - - return path + strlen(rootpath); -} - -/* - * encode a fs_locations structure - */ -static int nfsd4_encode_fs_locations(struct svc_rqst *rqstp, - struct svc_export *exp, - u32 **pp, int *buflen) -{ - int status, i; - u32 *p = *pp; - struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; - char *root = nfsd4_path(rqstp, exp); - - if (IS_ERR(root)) - return PTR_ERR(root); - status = nfsd4_encode_components('/', root, &p, buflen); - if (status) - return status; - if ((*buflen -= 4) < 0) - return nfserr_resource; - WRITE32(fslocs->locations_count); - for (i=0; ilocations_count; i++) { - status = nfsd4_encode_fs_location4(&fslocs->locations[i], - &p, buflen); - if (status) - return status; - } - *pp = p; - return 0; -} static u32 nfs4_ftypes[16] = { NF4BAD, NF4FIFO, NF4CHR, NF4BAD, @@ -1393,25 +1272,6 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); } -#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ - FATTR4_WORD0_RDATTR_ERROR) -#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID - -static int fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err) -{ - /* As per referral draft: */ - if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS || - *bmval1 & ~WORD1_ABSENT_FS_ATTRS) { - if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR || - *bmval0 & FATTR4_WORD0_FS_LOCATIONS) - *rdattr_err = NFSERR_MOVED; - else - return nfserr_moved; - } - *bmval0 &= WORD0_ABSENT_FS_ATTRS; - *bmval1 &= WORD1_ABSENT_FS_ATTRS; - return 0; -} /* * Note: @fhp can be NULL; in this case, we might have to compose the filehandle @@ -1434,7 +1294,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, u32 *attrlenp; u32 dummy; u64 dummy64; - u32 rdattr_err = 0; u32 *p = buffer; int status; int aclsupport = 0; @@ -1444,12 +1303,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0); BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1); - if (exp->ex_fslocs.migrated) { - status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err); - if (status) - goto out; - } - status = vfs_getattr(exp->ex_mnt, dentry, &stat); if (status) goto out_nfserr; @@ -1481,11 +1334,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, goto out_nfserr; } } - if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { - if (exp->ex_fslocs.locations == NULL) { - bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS; - } - } if ((buflen -= 16) < 0) goto out_resource; @@ -1495,15 +1343,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, attrlenp = p++; /* to be backfilled later */ if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { - u32 word0 = NFSD_SUPPORTED_ATTRS_WORD0; if ((buflen -= 12) < 0) goto out_resource; - if (!aclsupport) - word0 &= ~FATTR4_WORD0_ACL; - if (!exp->ex_fslocs.locations) - word0 &= ~FATTR4_WORD0_FS_LOCATIONS; WRITE32(2); - WRITE32(word0); + WRITE32(aclsupport ? + NFSD_SUPPORTED_ATTRS_WORD0 : + NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL); WRITE32(NFSD_SUPPORTED_ATTRS_WORD1); } if (bmval0 & FATTR4_WORD0_TYPE) { @@ -1557,10 +1402,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_FSID) { if ((buflen -= 16) < 0) goto out_resource; - if (exp->ex_fslocs.migrated) { - WRITE64(NFS4_REFERRAL_FSID_MAJOR); - WRITE64(NFS4_REFERRAL_FSID_MINOR); - } else if (is_fsid(fhp, rqstp->rq_reffh)) { + if (is_fsid(fhp, rqstp->rq_reffh)) { WRITE64((u64)exp->ex_fsid); WRITE64((u64)0); } else { @@ -1583,7 +1425,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { if ((buflen -= 4) < 0) goto out_resource; - WRITE32(rdattr_err); + WRITE32(0); } if (bmval0 & FATTR4_WORD0_ACL) { struct nfs4_ace *ace; @@ -1671,13 +1513,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, goto out_resource; WRITE64((u64) statfs.f_files); } - if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { - status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen); - if (status == nfserr_resource) - goto out_resource; - if (status) - goto out; - } if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { if ((buflen -= 4) < 0) goto out_resource; @@ -1701,12 +1536,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_MAXREAD) { if ((buflen -= 8) < 0) goto out_resource; - WRITE64((u64) svc_max_payload(rqstp)); + WRITE64((u64) NFSSVC_MAXBLKSIZE); } if (bmval0 & FATTR4_WORD0_MAXWRITE) { if ((buflen -= 8) < 0) goto out_resource; - WRITE64((u64) svc_max_payload(rqstp)); + WRITE64((u64) NFSSVC_MAXBLKSIZE); } if (bmval1 & FATTR4_WORD1_MODE) { if ((buflen -= 4) < 0) @@ -2010,6 +1845,7 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_ge nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, resp->p, &buflen, getattr->ga_bmval, resp->rqstp); + if (!nfserr) resp->p += buflen; return nfserr; @@ -2203,8 +2039,7 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct n } static int -nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, - struct nfsd4_read *read) +nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read) { u32 eof; int v, pn; @@ -2219,33 +2054,31 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, RESERVE_SPACE(8); /* eof flag and byte count */ - maxcount = svc_max_payload(resp->rqstp); + maxcount = NFSSVC_MAXBLKSIZE; if (maxcount > read->rd_length) maxcount = read->rd_length; len = maxcount; v = 0; while (len > 0) { - pn = resp->rqstp->rq_resused++; - resp->rqstp->rq_vec[v].iov_base = - page_address(resp->rqstp->rq_respages[pn]); - resp->rqstp->rq_vec[v].iov_len = - len < PAGE_SIZE ? len : PAGE_SIZE; + pn = resp->rqstp->rq_resused; + svc_take_page(resp->rqstp); + read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]); + read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE; v++; len -= PAGE_SIZE; } read->rd_vlen = v; nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp, - read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, + read->rd_offset, read->rd_iov, read->rd_vlen, &maxcount); if (nfserr == nfserr_symlink) nfserr = nfserr_inval; if (nfserr) return nfserr; - eof = (read->rd_offset + maxcount >= - read->rd_fhp->fh_dentry->d_inode->i_size); + eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size); WRITE32(eof); WRITE32(maxcount); @@ -2255,6 +2088,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, resp->xbuf->page_len = maxcount; /* Use rest of head for padding and remaining ops: */ + resp->rqstp->rq_restailpage = 0; resp->xbuf->tail[0].iov_base = p; resp->xbuf->tail[0].iov_len = 0; if (maxcount&3) { @@ -2279,7 +2113,8 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r if (resp->xbuf->page_len) return nfserr_resource; - page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); + svc_take_page(resp->rqstp); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); maxcount = PAGE_SIZE; RESERVE_SPACE(4); @@ -2303,6 +2138,7 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r resp->xbuf->page_len = maxcount; /* Use rest of head for padding and remaining ops: */ + resp->rqstp->rq_restailpage = 0; resp->xbuf->tail[0].iov_base = p; resp->xbuf->tail[0].iov_len = 0; if (maxcount&3) { @@ -2353,7 +2189,8 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re goto err_no_verf; } - page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); + svc_take_page(resp->rqstp); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); readdir->common.err = 0; readdir->buflen = maxcount; readdir->buffer = page; @@ -2378,10 +2215,10 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re p = readdir->buffer; *p++ = 0; /* no more entries */ *p++ = htonl(readdir->common.err == nfserr_eof); - resp->xbuf->page_len = ((char*)p) - (char*)page_address( - resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); + resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); /* Use rest of head for padding and remaining ops: */ + resp->rqstp->rq_restailpage = 0; resp->xbuf->tail[0].iov_base = tailbase; resp->xbuf->tail[0].iov_len = 0; resp->p = resp->xbuf->tail[0].iov_base; diff --git a/trunk/fs/nfsd/nfsctl.c b/trunk/fs/nfsd/nfsctl.c index 39aed901514b..5c6a477c20ec 100644 --- a/trunk/fs/nfsd/nfsctl.c +++ b/trunk/fs/nfsd/nfsctl.c @@ -57,7 +57,6 @@ enum { NFSD_Pool_Threads, NFSD_Versions, NFSD_Ports, - NFSD_MaxBlkSize, /* * The below MUST come last. Otherwise we leave a hole in nfsd_files[] * with !CONFIG_NFSD_V4 and simple_fill_super() goes oops @@ -83,7 +82,6 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size); static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); static ssize_t write_versions(struct file *file, char *buf, size_t size); static ssize_t write_ports(struct file *file, char *buf, size_t size); -static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); #ifdef CONFIG_NFSD_V4 static ssize_t write_leasetime(struct file *file, char *buf, size_t size); static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); @@ -102,7 +100,6 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { [NFSD_Pool_Threads] = write_pool_threads, [NFSD_Versions] = write_versions, [NFSD_Ports] = write_ports, - [NFSD_MaxBlkSize] = write_maxblksize, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = write_leasetime, [NFSD_RecoveryDir] = write_recoverydir, @@ -526,20 +523,18 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) err = nfsd_create_serv(); if (!err) { int proto = 0; - err = svc_addsock(nfsd_serv, fd, buf, &proto); - if (err >= 0) { - err = lockd_up(proto); - if (err < 0) - svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf); + err = lockd_up(proto); + if (!err) { + err = svc_addsock(nfsd_serv, fd, buf, &proto); + if (err) + lockd_down(); } /* Decrease the count, but don't shutdown the * the service */ - lock_kernel(); nfsd_serv->sv_nrthreads--; - unlock_kernel(); } - return err < 0 ? err : 0; + return err; } if (buf[0] == '-') { char *toclose = kstrdup(buf+1, GFP_KERNEL); @@ -550,43 +545,12 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) if (nfsd_serv) len = svc_sock_names(buf, nfsd_serv, toclose); unlock_kernel(); - if (len >= 0) - lockd_down(); kfree(toclose); return len; } return -EINVAL; } -int nfsd_max_blksize; - -static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) -{ - char *mesg = buf; - if (size > 0) { - int bsize; - int rv = get_int(&mesg, &bsize); - if (rv) - return rv; - /* force bsize into allowed range and - * required alignment. - */ - if (bsize < 1024) - bsize = 1024; - if (bsize > NFSSVC_MAXBLKSIZE) - bsize = NFSSVC_MAXBLKSIZE; - bsize &= ~(1024-1); - lock_kernel(); - if (nfsd_serv && nfsd_serv->sv_nrthreads) { - unlock_kernel(); - return -EBUSY; - } - nfsd_max_blksize = bsize; - unlock_kernel(); - } - return sprintf(buf, "%d\n", nfsd_max_blksize); -} - #ifdef CONFIG_NFSD_V4 extern time_t nfs4_leasetime(void); @@ -652,7 +616,6 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, - [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, #ifdef CONFIG_NFSD_V4 [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, diff --git a/trunk/fs/nfsd/nfsproc.c b/trunk/fs/nfsd/nfsproc.c index 9ee1dab5d44a..06cd0db0f32b 100644 --- a/trunk/fs/nfsd/nfsproc.c +++ b/trunk/fs/nfsd/nfsproc.c @@ -146,20 +146,20 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp, * status, 17 words for fattr, and 1 word for the byte count. */ - if (NFSSVC_MAXBLKSIZE_V2 < argp->count) { + if (NFSSVC_MAXBLKSIZE < argp->count) { printk(KERN_NOTICE "oversized read request from %u.%u.%u.%u:%d (%d bytes)\n", NIPQUAD(rqstp->rq_addr.sin_addr.s_addr), ntohs(rqstp->rq_addr.sin_port), argp->count); - argp->count = NFSSVC_MAXBLKSIZE_V2; + argp->count = NFSSVC_MAXBLKSIZE; } svc_reserve(rqstp, (19<<2) + argp->count + 4); resp->count = argp->count; nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, argp->offset, - rqstp->rq_vec, argp->vlen, + argp->vec, argp->vlen, &resp->count); if (nfserr) return nfserr; @@ -185,7 +185,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp, nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh), NULL, argp->offset, - rqstp->rq_vec, argp->vlen, + argp->vec, argp->vlen, argp->len, &stable); return nfsd_return_attrs(nfserr, resp); @@ -225,7 +225,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, nfserr = nfserr_exist; if (isdotent(argp->name, argp->len)) goto done; - fh_lock_nested(dirfhp, I_MUTEX_PARENT); + fh_lock(dirfhp); dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len); if (IS_ERR(dchild)) { nfserr = nfserrno(PTR_ERR(dchild)); @@ -553,7 +553,7 @@ static struct svc_procedure nfsd_procedures2[18] = { PROC(none, void, void, none, RC_NOCACHE, ST), PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, ST+FH+AT), PROC(readlink, readlinkargs, readlinkres, none, RC_NOCACHE, ST+1+NFS_MAXPATHLEN/4), - PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4), + PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE/4), PROC(none, void, void, none, RC_NOCACHE, ST), PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, ST+AT), PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT), diff --git a/trunk/fs/nfsd/nfssvc.c b/trunk/fs/nfsd/nfssvc.c index 6fa6340a5fb8..19443056ec30 100644 --- a/trunk/fs/nfsd/nfssvc.c +++ b/trunk/fs/nfsd/nfssvc.c @@ -198,26 +198,9 @@ int nfsd_create_serv(void) unlock_kernel(); return 0; } - if (nfsd_max_blksize == 0) { - /* choose a suitable default */ - struct sysinfo i; - si_meminfo(&i); - /* Aim for 1/4096 of memory per thread - * This gives 1MB on 4Gig machines - * But only uses 32K on 128M machines. - * Bottom out at 8K on 32M and smaller. - * Of course, this is only a default. - */ - nfsd_max_blksize = NFSSVC_MAXBLKSIZE; - i.totalram <<= PAGE_SHIFT - 12; - while (nfsd_max_blksize > i.totalram && - nfsd_max_blksize >= 8*1024*2) - nfsd_max_blksize /= 2; - } atomic_set(&nfsd_busy, 0); - nfsd_serv = svc_create_pooled(&nfsd_program, - NFSD_BUFSIZE - NFSSVC_MAXBLKSIZE + nfsd_max_blksize, + nfsd_serv = svc_create_pooled(&nfsd_program, NFSD_BUFSIZE, nfsd_last_thread, nfsd, SIG_NOCLEAN, THIS_MODULE); if (nfsd_serv == NULL) diff --git a/trunk/fs/nfsd/nfsxdr.c b/trunk/fs/nfsd/nfsxdr.c index 1135c0d14557..3f14a17eaa6e 100644 --- a/trunk/fs/nfsd/nfsxdr.c +++ b/trunk/fs/nfsd/nfsxdr.c @@ -254,18 +254,19 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p, len = args->count = ntohl(*p++); p++; /* totalcount - unused */ - if (len > NFSSVC_MAXBLKSIZE_V2) - len = NFSSVC_MAXBLKSIZE_V2; + if (len > NFSSVC_MAXBLKSIZE) + len = NFSSVC_MAXBLKSIZE; /* set up somewhere to store response. * We take pages, put them on reslist and include in iovec */ v=0; while (len > 0) { - pn = rqstp->rq_resused++; - rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); - rqstp->rq_vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; - len -= rqstp->rq_vec[v].iov_len; + pn=rqstp->rq_resused; + svc_take_page(rqstp); + args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]); + args->vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; + len -= args->vec[v].iov_len; v++; } args->vlen = v; @@ -285,21 +286,21 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p, args->offset = ntohl(*p++); /* offset */ p++; /* totalcount */ len = args->len = ntohl(*p++); - rqstp->rq_vec[0].iov_base = (void*)p; - rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - + args->vec[0].iov_base = (void*)p; + args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - (((void*)p) - rqstp->rq_arg.head[0].iov_base); - if (len > NFSSVC_MAXBLKSIZE_V2) - len = NFSSVC_MAXBLKSIZE_V2; + if (len > NFSSVC_MAXBLKSIZE) + len = NFSSVC_MAXBLKSIZE; v = 0; - while (len > rqstp->rq_vec[v].iov_len) { - len -= rqstp->rq_vec[v].iov_len; + while (len > args->vec[v].iov_len) { + len -= args->vec[v].iov_len; v++; - rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]); - rqstp->rq_vec[v].iov_len = PAGE_SIZE; + args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]); + args->vec[v].iov_len = PAGE_SIZE; } - rqstp->rq_vec[v].iov_len = len; + args->vec[v].iov_len = len; args->vlen = v+1; - return rqstp->rq_vec[0].iov_len > 0; + return args->vec[0].iov_len > 0; } int @@ -332,7 +333,8 @@ nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p, struct nfsd_readlinka { if (!(p = decode_fh(p, &args->fh))) return 0; - args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); + svc_take_page(rqstp); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); return xdr_argsize_check(rqstp, p); } @@ -373,7 +375,8 @@ nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p, if (args->count > PAGE_SIZE) args->count = PAGE_SIZE; - args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); + svc_take_page(rqstp); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]); return xdr_argsize_check(rqstp, p); } @@ -413,6 +416,7 @@ nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->len; if (resp->len & 3) { /* need to pad the tail */ + rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3); @@ -432,6 +436,7 @@ nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p, rqstp->rq_res.page_len = resp->count; if (resp->count & 3) { /* need to pad the tail */ + rqstp->rq_restailpage = 0; rqstp->rq_res.tail[0].iov_base = p; *p = 0; rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3); @@ -458,7 +463,7 @@ nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p, { struct kstatfs *stat = &resp->stats; - *p++ = htonl(NFSSVC_MAXBLKSIZE_V2); /* max transfer size */ + *p++ = htonl(NFSSVC_MAXBLKSIZE); /* max transfer size */ *p++ = htonl(stat->f_bsize); *p++ = htonl(stat->f_blocks); *p++ = htonl(stat->f_bfree); diff --git a/trunk/fs/nfsd/vfs.c b/trunk/fs/nfsd/vfs.c index 1141bd29e4e3..443ebc52e382 100644 --- a/trunk/fs/nfsd/vfs.c +++ b/trunk/fs/nfsd/vfs.c @@ -54,7 +54,6 @@ #include #include #endif /* CONFIG_NFSD_V4 */ -#include #include @@ -82,19 +81,10 @@ struct raparms { dev_t p_dev; int p_set; struct file_ra_state p_ra; - unsigned int p_hindex; }; -struct raparm_hbucket { - struct raparms *pb_head; - spinlock_t pb_lock; -} ____cacheline_aligned_in_smp; - static struct raparms * raparml; -#define RAPARM_HASH_BITS 4 -#define RAPARM_HASH_SIZE (1<i_mode)) { + if (dpacl) { error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); if (error < 0) goto out_nfserr; @@ -751,20 +743,16 @@ nfsd_sync_dir(struct dentry *dp) * Obtain the readahead parameters for the file * specified by (dev, ino). */ +static DEFINE_SPINLOCK(ra_lock); static inline struct raparms * nfsd_get_raparms(dev_t dev, ino_t ino) { struct raparms *ra, **rap, **frap = NULL; int depth = 0; - unsigned int hash; - struct raparm_hbucket *rab; - hash = jhash_2words(dev, ino, 0xfeedbeef) & RAPARM_HASH_MASK; - rab = &raparm_hash[hash]; - - spin_lock(&rab->pb_lock); - for (rap = &rab->pb_head; (ra = *rap); rap = &ra->p_next) { + spin_lock(&ra_lock); + for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) { if (ra->p_ino == ino && ra->p_dev == dev) goto found; depth++; @@ -773,7 +761,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino) } depth = nfsdstats.ra_size*11/10; if (!frap) { - spin_unlock(&rab->pb_lock); + spin_unlock(&ra_lock); return NULL; } rap = frap; @@ -781,16 +769,15 @@ nfsd_get_raparms(dev_t dev, ino_t ino) ra->p_dev = dev; ra->p_ino = ino; ra->p_set = 0; - ra->p_hindex = hash; found: - if (rap != &rab->pb_head) { + if (rap != &raparm_cache) { *rap = ra->p_next; - ra->p_next = rab->pb_head; - rab->pb_head = ra; + ra->p_next = raparm_cache; + raparm_cache = ra; } ra->p_count++; nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++; - spin_unlock(&rab->pb_lock); + spin_unlock(&ra_lock); return ra; } @@ -804,26 +791,22 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset { unsigned long count = desc->count; struct svc_rqst *rqstp = desc->arg.data; - struct page **pp = rqstp->rq_respages + rqstp->rq_resused; if (size > count) size = count; if (rqstp->rq_res.page_len == 0) { get_page(page); - put_page(*pp); - *pp = page; - rqstp->rq_resused++; + rqstp->rq_respages[rqstp->rq_resused++] = page; rqstp->rq_res.page_base = offset; rqstp->rq_res.page_len = size; - } else if (page != pp[-1]) { + } else if (page != rqstp->rq_respages[rqstp->rq_resused-1]) { get_page(page); - put_page(*pp); - *pp = page; - rqstp->rq_resused++; + rqstp->rq_respages[rqstp->rq_resused++] = page; rqstp->rq_res.page_len += size; - } else + } else { rqstp->rq_res.page_len += size; + } desc->count = count - size; desc->written += size; @@ -854,7 +837,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, file->f_ra = ra->p_ra; if (file->f_op->sendfile && rqstp->rq_sendfile_ok) { - rqstp->rq_resused = 1; + svc_pushback_unused_pages(rqstp); err = file->f_op->sendfile(file, &offset, *count, nfsd_read_actor, rqstp); } else { @@ -866,12 +849,11 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, /* Write back readahead params */ if (ra) { - struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex]; - spin_lock(&rab->pb_lock); + spin_lock(&ra_lock); ra->p_ra = file->f_ra; ra->p_set = 1; ra->p_count--; - spin_unlock(&rab->pb_lock); + spin_unlock(&ra_lock); } if (err >= 0) { @@ -1847,11 +1829,11 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) void nfsd_racache_shutdown(void) { - if (!raparml) + if (!raparm_cache) return; dprintk("nfsd: freeing readahead buffers.\n"); kfree(raparml); - raparml = NULL; + raparm_cache = raparml = NULL; } /* * Initialize readahead param cache @@ -1860,31 +1842,19 @@ int nfsd_racache_init(int cache_size) { int i; - int j = 0; - int nperbucket; - - if (raparml) + if (raparm_cache) return 0; - if (cache_size < 2*RAPARM_HASH_SIZE) - cache_size = 2*RAPARM_HASH_SIZE; raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL); if (raparml != NULL) { dprintk("nfsd: allocating %d readahead buffers.\n", cache_size); - for (i = 0 ; i < RAPARM_HASH_SIZE ; i++) { - raparm_hash[i].pb_head = NULL; - spin_lock_init(&raparm_hash[i].pb_lock); - } - nperbucket = cache_size >> RAPARM_HASH_BITS; memset(raparml, 0, sizeof(struct raparms) * cache_size); for (i = 0; i < cache_size - 1; i++) { - if (i % nperbucket == 0) - raparm_hash[j++].pb_head = raparml + i; - if (i % nperbucket < nperbucket-1) - raparml[i].p_next = raparml + i + 1; + raparml[i].p_next = raparml + i + 1; } + raparm_cache = raparml; } else { printk(KERN_WARNING "nfsd: Could not allocate memory read-ahead cache.\n"); diff --git a/trunk/fs/reiserfs/file.c b/trunk/fs/reiserfs/file.c index c093642fb983..b67ce9354048 100644 --- a/trunk/fs/reiserfs/file.c +++ b/trunk/fs/reiserfs/file.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include diff --git a/trunk/fs/reiserfs/inode.c b/trunk/fs/reiserfs/inode.c index 9c69bcacad22..7e5a2f5ebeb0 100644 --- a/trunk/fs/reiserfs/inode.c +++ b/trunk/fs/reiserfs/inode.c @@ -1780,7 +1780,7 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th, err = -EDQUOT; goto out_end_trans; } - if (!dir->i_nlink) { + if (!dir || !dir->i_nlink) { err = -EPERM; goto out_bad_inode; } diff --git a/trunk/include/asm-arm/arch-lh7a40x/clocks.h b/trunk/include/asm-arm/arch-lh7a40x/clocks.h index bee02fd8dab1..7d0ba18ad578 100644 --- a/trunk/include/asm-arm/arch-lh7a40x/clocks.h +++ b/trunk/include/asm-arm/arch-lh7a40x/clocks.h @@ -8,8 +8,6 @@ * */ -#include - #ifndef __ASM_ARCH_CLOCKS_H #define __ASM_ARCH_CLOCKS_H diff --git a/trunk/include/asm-arm/pgtable-nommu.h b/trunk/include/asm-arm/pgtable-nommu.h index b13322dccf41..c1b264dff287 100644 --- a/trunk/include/asm-arm/pgtable-nommu.h +++ b/trunk/include/asm-arm/pgtable-nommu.h @@ -13,7 +13,6 @@ #ifndef __ASSEMBLY__ -#include #include #include #include diff --git a/trunk/include/asm-i386/alternative-asm.i b/trunk/include/asm-i386/alternative-asm.i index 6c47e3b9484b..f0510209ccbe 100644 --- a/trunk/include/asm-i386/alternative-asm.i +++ b/trunk/include/asm-i386/alternative-asm.i @@ -1,5 +1,3 @@ -#include - #ifdef CONFIG_SMP .macro LOCK_PREFIX 1: lock diff --git a/trunk/include/asm-i386/frame.i b/trunk/include/asm-i386/frame.i index 4d68ddce18b6..03620251ae17 100644 --- a/trunk/include/asm-i386/frame.i +++ b/trunk/include/asm-i386/frame.i @@ -1,4 +1,3 @@ -#include #include /* The annotation hides the frame from the unwinder and makes it look diff --git a/trunk/include/asm-i386/hw_irq.h b/trunk/include/asm-i386/hw_irq.h index 88f02a073561..87e5a351d881 100644 --- a/trunk/include/asm-i386/hw_irq.h +++ b/trunk/include/asm-i386/hw_irq.h @@ -17,6 +17,8 @@ #include #include +struct hw_interrupt_type; + #define NMI_VECTOR 0x02 /* @@ -28,6 +30,7 @@ extern u8 irq_vector[NR_IRQ_VECTORS]; #define IO_APIC_VECTOR(irq) (irq_vector[irq]) +#define AUTO_ASSIGN -1 extern void (*interrupt[NR_IRQS])(void); diff --git a/trunk/include/asm-i386/hypertransport.h b/trunk/include/asm-i386/hypertransport.h deleted file mode 100644 index c16c6ff4bdd7..000000000000 --- a/trunk/include/asm-i386/hypertransport.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef ASM_HYPERTRANSPORT_H -#define ASM_HYPERTRANSPORT_H - -/* - * Constants for x86 Hypertransport Interrupts. - */ - -#define HT_IRQ_LOW_BASE 0xf8000000 - -#define HT_IRQ_LOW_VECTOR_SHIFT 16 -#define HT_IRQ_LOW_VECTOR_MASK 0x00ff0000 -#define HT_IRQ_LOW_VECTOR(v) (((v) << HT_IRQ_LOW_VECTOR_SHIFT) & HT_IRQ_LOW_VECTOR_MASK) - -#define HT_IRQ_LOW_DEST_ID_SHIFT 8 -#define HT_IRQ_LOW_DEST_ID_MASK 0x0000ff00 -#define HT_IRQ_LOW_DEST_ID(v) (((v) << HT_IRQ_LOW_DEST_ID_SHIFT) & HT_IRQ_LOW_DEST_ID_MASK) - -#define HT_IRQ_LOW_DM_PHYSICAL 0x0000000 -#define HT_IRQ_LOW_DM_LOGICAL 0x0000040 - -#define HT_IRQ_LOW_RQEOI_EDGE 0x0000000 -#define HT_IRQ_LOW_RQEOI_LEVEL 0x0000020 - - -#define HT_IRQ_LOW_MT_FIXED 0x0000000 -#define HT_IRQ_LOW_MT_ARBITRATED 0x0000004 -#define HT_IRQ_LOW_MT_SMI 0x0000008 -#define HT_IRQ_LOW_MT_NMI 0x000000c -#define HT_IRQ_LOW_MT_INIT 0x0000010 -#define HT_IRQ_LOW_MT_STARTUP 0x0000014 -#define HT_IRQ_LOW_MT_EXTINT 0x0000018 -#define HT_IRQ_LOW_MT_LINT1 0x000008c -#define HT_IRQ_LOW_MT_LINT0 0x0000098 - -#define HT_IRQ_LOW_IRQ_MASKED 0x0000001 - - -#define HT_IRQ_HIGH_DEST_ID_SHIFT 0 -#define HT_IRQ_HIGH_DEST_ID_MASK 0x00ffffff -#define HT_IRQ_HIGH_DEST_ID(v) ((((v) >> 8) << HT_IRQ_HIGH_DEST_ID_SHIFT) & HT_IRQ_HIGH_DEST_ID_MASK) - -#endif /* ASM_HYPERTRANSPORT_H */ diff --git a/trunk/include/asm-i386/io_apic.h b/trunk/include/asm-i386/io_apic.h index 276ea7e8144a..5d309275a1dc 100644 --- a/trunk/include/asm-i386/io_apic.h +++ b/trunk/include/asm-i386/io_apic.h @@ -12,6 +12,46 @@ #ifdef CONFIG_X86_IO_APIC +#ifdef CONFIG_PCI_MSI +static inline int use_pci_vector(void) {return 1;} +static inline void disable_edge_ioapic_vector(unsigned int vector) { } +static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { } +static inline void end_edge_ioapic_vector (unsigned int vector) { } +#define startup_level_ioapic startup_level_ioapic_vector +#define shutdown_level_ioapic mask_IO_APIC_vector +#define enable_level_ioapic unmask_IO_APIC_vector +#define disable_level_ioapic mask_IO_APIC_vector +#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_vector +#define end_level_ioapic end_level_ioapic_vector +#define set_ioapic_affinity set_ioapic_affinity_vector + +#define startup_edge_ioapic startup_edge_ioapic_vector +#define shutdown_edge_ioapic disable_edge_ioapic_vector +#define enable_edge_ioapic unmask_IO_APIC_vector +#define disable_edge_ioapic disable_edge_ioapic_vector +#define ack_edge_ioapic ack_edge_ioapic_vector +#define end_edge_ioapic end_edge_ioapic_vector +#else +static inline int use_pci_vector(void) {return 0;} +static inline void disable_edge_ioapic_irq(unsigned int irq) { } +static inline void mask_and_ack_level_ioapic_irq(unsigned int irq) { } +static inline void end_edge_ioapic_irq (unsigned int irq) { } +#define startup_level_ioapic startup_level_ioapic_irq +#define shutdown_level_ioapic mask_IO_APIC_irq +#define enable_level_ioapic unmask_IO_APIC_irq +#define disable_level_ioapic mask_IO_APIC_irq +#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_irq +#define end_level_ioapic end_level_ioapic_irq +#define set_ioapic_affinity set_ioapic_affinity_irq + +#define startup_edge_ioapic startup_edge_ioapic_irq +#define shutdown_edge_ioapic disable_edge_ioapic_irq +#define enable_edge_ioapic unmask_IO_APIC_irq +#define disable_edge_ioapic disable_edge_ioapic_irq +#define ack_edge_ioapic ack_edge_ioapic_irq +#define end_edge_ioapic end_edge_ioapic_irq +#endif + #define IO_APIC_BASE(idx) \ ((volatile int *)(__fix_to_virt(FIX_IO_APIC_BASE_0 + idx) \ + (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK))) @@ -179,4 +219,6 @@ extern int (*ioapic_renumber_irq)(int ioapic, int irq); static inline void disable_ioapic_setup(void) { } #endif +extern int assign_irq_vector(int irq); + #endif diff --git a/trunk/include/asm-i386/mach-default/irq_vectors_limits.h b/trunk/include/asm-i386/mach-default/irq_vectors_limits.h index 7f161e760be6..b330026e6f7f 100644 --- a/trunk/include/asm-i386/mach-default/irq_vectors_limits.h +++ b/trunk/include/asm-i386/mach-default/irq_vectors_limits.h @@ -1,6 +1,10 @@ #ifndef _ASM_IRQ_VECTORS_LIMITS_H #define _ASM_IRQ_VECTORS_LIMITS_H +#ifdef CONFIG_PCI_MSI +#define NR_IRQS FIRST_SYSTEM_VECTOR +#define NR_IRQ_VECTORS NR_IRQS +#else #ifdef CONFIG_X86_IO_APIC #define NR_IRQS 224 # if (224 >= 32 * NR_CPUS) @@ -12,5 +16,6 @@ #define NR_IRQS 16 #define NR_IRQ_VECTORS NR_IRQS #endif +#endif #endif /* _ASM_IRQ_VECTORS_LIMITS_H */ diff --git a/trunk/include/asm-i386/msi.h b/trunk/include/asm-i386/msi.h new file mode 100644 index 000000000000..b11c4b7dfaef --- /dev/null +++ b/trunk/include/asm-i386/msi.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2003-2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#ifndef ASM_MSI_H +#define ASM_MSI_H + +#include +#include + +#define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) +#define MSI_TARGET_CPU_SHIFT 12 + +extern struct msi_ops msi_apic_ops; + +static inline int msi_arch_init(void) +{ + msi_register(&msi_apic_ops); + return 0; +} + +#endif /* ASM_MSI_H */ diff --git a/trunk/include/asm-i386/msidef.h b/trunk/include/asm-i386/msidef.h deleted file mode 100644 index 5b8acddb70fb..000000000000 --- a/trunk/include/asm-i386/msidef.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ASM_MSIDEF_H -#define ASM_MSIDEF_H - -/* - * Constants for Intel APIC based MSI messages. - */ - -/* - * Shifts for MSI data - */ - -#define MSI_DATA_VECTOR_SHIFT 0 -#define MSI_DATA_VECTOR_MASK 0x000000ff -#define MSI_DATA_VECTOR(v) (((v) << MSI_DATA_VECTOR_SHIFT) & MSI_DATA_VECTOR_MASK) - -#define MSI_DATA_DELIVERY_MODE_SHIFT 8 -#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_MODE_SHIFT) -#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_MODE_SHIFT) - -#define MSI_DATA_LEVEL_SHIFT 14 -#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) -#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) - -#define MSI_DATA_TRIGGER_SHIFT 15 -#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) -#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) - -/* - * Shift/mask fields for msi address - */ - -#define MSI_ADDR_BASE_HI 0 -#define MSI_ADDR_BASE_LO 0xfee00000 - -#define MSI_ADDR_DEST_MODE_SHIFT 2 -#define MSI_ADDR_DEST_MODE_PHYSICAL (0 << MSI_ADDR_DEST_MODE_SHIFT) -#define MSI_ADDR_DEST_MODE_LOGICAL (1 << MSI_ADDR_DEST_MODE_SHIFT) - -#define MSI_ADDR_REDIRECTION_SHIFT 3 -#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) /* dedicated cpu */ -#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) /* lowest priority */ - -#define MSI_ADDR_DEST_ID_SHIFT 12 -#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 -#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & MSI_ADDR_DEST_ID_MASK) - -#endif /* ASM_MSIDEF_H */ diff --git a/trunk/include/asm-ia64/machvec.h b/trunk/include/asm-ia64/machvec.h index 90cba967df35..15b545a897a4 100644 --- a/trunk/include/asm-ia64/machvec.h +++ b/trunk/include/asm-ia64/machvec.h @@ -20,7 +20,6 @@ struct page; struct mm_struct; struct pci_bus; struct task_struct; -struct pci_dev; typedef void ia64_mv_setup_t (char **); typedef void ia64_mv_cpu_init_t (void); @@ -76,9 +75,7 @@ typedef unsigned char ia64_mv_readb_relaxed_t (const volatile void __iomem *); typedef unsigned short ia64_mv_readw_relaxed_t (const volatile void __iomem *); typedef unsigned int ia64_mv_readl_relaxed_t (const volatile void __iomem *); typedef unsigned long ia64_mv_readq_relaxed_t (const volatile void __iomem *); - -typedef int ia64_mv_setup_msi_irq_t (unsigned int irq, struct pci_dev *pdev); -typedef void ia64_mv_teardown_msi_irq_t (unsigned int irq); +typedef int ia64_mv_msi_init_t (void); static inline void machvec_noop (void) @@ -157,8 +154,7 @@ extern void machvec_tlb_migrate_finish (struct mm_struct *); # define platform_readl_relaxed ia64_mv.readl_relaxed # define platform_readq_relaxed ia64_mv.readq_relaxed # define platform_migrate ia64_mv.migrate -# define platform_setup_msi_irq ia64_mv.setup_msi_irq -# define platform_teardown_msi_irq ia64_mv.teardown_msi_irq +# define platform_msi_init ia64_mv.msi_init # endif /* __attribute__((__aligned__(16))) is required to make size of the @@ -208,8 +204,7 @@ struct ia64_machine_vector { ia64_mv_readl_relaxed_t *readl_relaxed; ia64_mv_readq_relaxed_t *readq_relaxed; ia64_mv_migrate_t *migrate; - ia64_mv_setup_msi_irq_t *setup_msi_irq; - ia64_mv_teardown_msi_irq_t *teardown_msi_irq; + ia64_mv_msi_init_t *msi_init; } __attribute__((__aligned__(16))); /* align attrib? see above comment */ #define MACHVEC_INIT(name) \ @@ -255,8 +250,7 @@ struct ia64_machine_vector { platform_readl_relaxed, \ platform_readq_relaxed, \ platform_migrate, \ - platform_setup_msi_irq, \ - platform_teardown_msi_irq, \ + platform_msi_init, \ } extern struct ia64_machine_vector ia64_mv; @@ -410,11 +404,8 @@ extern int ia64_pci_legacy_write(struct pci_bus *bus, u16 port, u32 val, u8 size #ifndef platform_migrate # define platform_migrate machvec_noop_task #endif -#ifndef platform_setup_msi_irq -# define platform_setup_msi_irq ((ia64_mv_setup_msi_irq_t*)NULL) -#endif -#ifndef platform_teardown_msi_irq -# define platform_teardown_msi_irq ((ia64_mv_teardown_msi_irq_t*)NULL) +#ifndef platform_msi_init +# define platform_msi_init ((ia64_mv_msi_init_t*)NULL) #endif #endif /* _ASM_IA64_MACHVEC_H */ diff --git a/trunk/include/asm-ia64/machvec_sn2.h b/trunk/include/asm-ia64/machvec_sn2.h index c54b165b1c17..cf724dc79d8c 100644 --- a/trunk/include/asm-ia64/machvec_sn2.h +++ b/trunk/include/asm-ia64/machvec_sn2.h @@ -67,8 +67,7 @@ extern ia64_mv_dma_sync_sg_for_device sn_dma_sync_sg_for_device; extern ia64_mv_dma_mapping_error sn_dma_mapping_error; extern ia64_mv_dma_supported sn_dma_supported; extern ia64_mv_migrate_t sn_migrate; -extern ia64_mv_setup_msi_irq_t sn_setup_msi_irq; -extern ia64_mv_teardown_msi_irq_t sn_teardown_msi_irq; +extern ia64_mv_msi_init_t sn_msi_init; /* @@ -121,11 +120,9 @@ extern ia64_mv_teardown_msi_irq_t sn_teardown_msi_irq; #define platform_dma_supported sn_dma_supported #define platform_migrate sn_migrate #ifdef CONFIG_PCI_MSI -#define platform_setup_msi_irq sn_setup_msi_irq -#define platform_teardown_msi_irq sn_teardown_msi_irq +#define platform_msi_init sn_msi_init #else -#define platform_setup_msi_irq ((ia64_mv_setup_msi_irq_t*)NULL) -#define platform_teardown_msi_irq ((ia64_mv_teardown_msi_irq_t*)NULL) +#define platform_msi_init ((ia64_mv_msi_init_t*)NULL) #endif #include diff --git a/trunk/include/asm-ia64/msi.h b/trunk/include/asm-ia64/msi.h new file mode 100644 index 000000000000..bb92b0dbde2f --- /dev/null +++ b/trunk/include/asm-ia64/msi.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2003-2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#ifndef ASM_MSI_H +#define ASM_MSI_H + +#define NR_VECTORS NR_IRQS +#define FIRST_DEVICE_VECTOR IA64_FIRST_DEVICE_VECTOR +#define LAST_DEVICE_VECTOR IA64_LAST_DEVICE_VECTOR +static inline void set_intr_gate (int nr, void *func) {} +#define IO_APIC_VECTOR(irq) (irq) +#define ack_APIC_irq ia64_eoi +#define MSI_TARGET_CPU_SHIFT 4 + +extern struct msi_ops msi_apic_ops; + +static inline int msi_arch_init(void) +{ + if (platform_msi_init) + return platform_msi_init(); + + /* default ops for most ia64 platforms */ + msi_register(&msi_apic_ops); + return 0; +} + +#endif /* ASM_MSI_H */ diff --git a/trunk/include/asm-parisc/agp.h b/trunk/include/asm-parisc/agp.h deleted file mode 100644 index 9f61d4eb6c01..000000000000 --- a/trunk/include/asm-parisc/agp.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _ASM_PARISC_AGP_H -#define _ASM_PARISC_AGP_H - -/* - * PARISC specific AGP definitions. - * Copyright (c) 2006 Kyle McMartin - * - */ - -#define map_page_into_agp(page) /* nothing */ -#define unmap_page_from_agp(page) /* nothing */ -#define flush_agp_mappings() /* nothing */ -#define flush_agp_cache() mb() - -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - -/* GATT allocation. Returns/accepts GATT kernel virtual address. */ -#define alloc_gatt_pages(order) \ - ((char *)__get_free_pages(GFP_KERNEL, (order))) -#define free_gatt_pages(table, order) \ - free_pages((unsigned long)(table), (order)) - -#endif /* _ASM_PARISC_AGP_H */ diff --git a/trunk/include/asm-parisc/assembly.h b/trunk/include/asm-parisc/assembly.h index 5a1e0e8b1c32..1a7bfe699e0c 100644 --- a/trunk/include/asm-parisc/assembly.h +++ b/trunk/include/asm-parisc/assembly.h @@ -29,8 +29,7 @@ #define LDREGX ldd,s #define LDREGM ldd,mb #define STREGM std,ma -#define SHRREG shrd -#define SHLREG shld +#define SHRREG shrd #define RP_OFFSET 16 #define FRAME_SIZE 128 #define CALLEE_REG_FRAME_SIZE 144 @@ -40,8 +39,7 @@ #define LDREGX ldwx,s #define LDREGM ldwm #define STREGM stwm -#define SHRREG shr -#define SHLREG shlw +#define SHRREG shr #define RP_OFFSET 20 #define FRAME_SIZE 64 #define CALLEE_REG_FRAME_SIZE 128 diff --git a/trunk/include/asm-parisc/cacheflush.h b/trunk/include/asm-parisc/cacheflush.h index 2bc41f2e0271..0b459cdfbd6f 100644 --- a/trunk/include/asm-parisc/cacheflush.h +++ b/trunk/include/asm-parisc/cacheflush.h @@ -191,38 +191,16 @@ flush_anon_page(struct page *page, unsigned long vmaddr) } #define ARCH_HAS_FLUSH_ANON_PAGE -#define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE -void flush_kernel_dcache_page_addr(void *addr); -static inline void flush_kernel_dcache_page(struct page *page) +static inline void +flush_kernel_dcache_page(struct page *page) { - flush_kernel_dcache_page_addr(page_address(page)); + flush_kernel_dcache_page_asm(page_address(page)); } +#define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE #ifdef CONFIG_DEBUG_RODATA void mark_rodata_ro(void); #endif -#ifdef CONFIG_PA8X00 -/* Only pa8800, pa8900 needs this */ -#define ARCH_HAS_KMAP - -void kunmap_parisc(void *addr); - -static inline void *kmap(struct page *page) -{ - might_sleep(); - return page_address(page); -} - -#define kunmap(page) kunmap_parisc(page_address(page)) - -#define kmap_atomic(page, idx) page_address(page) - -#define kunmap_atomic(addr, idx) kunmap_parisc(addr) - -#define kmap_atomic_pfn(pfn, idx) page_address(pfn_to_page(pfn)) -#define kmap_atomic_to_page(ptr) virt_to_page(ptr) -#endif - #endif /* _PARISC_CACHEFLUSH_H */ diff --git a/trunk/include/asm-parisc/compat.h b/trunk/include/asm-parisc/compat.h index fe8579023531..71b4eeea205a 100644 --- a/trunk/include/asm-parisc/compat.h +++ b/trunk/include/asm-parisc/compat.h @@ -5,7 +5,7 @@ */ #include #include -#include +#include #define COMPAT_USER_HZ 100 @@ -152,7 +152,7 @@ static __inline__ void __user *compat_alloc_user_space(long len) static inline int __is_compat_task(struct task_struct *t) { - return test_ti_thread_flag(t->thread_info, TIF_32BIT); + return personality(t->personality) == PER_LINUX32; } static inline int is_compat_task(void) diff --git a/trunk/include/asm-parisc/dma.h b/trunk/include/asm-parisc/dma.h index da2cf373e31c..9979c3cb3745 100644 --- a/trunk/include/asm-parisc/dma.h +++ b/trunk/include/asm-parisc/dma.h @@ -72,13 +72,18 @@ #define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ #define DMA2_EXT_MODE_REG (0x400 | DMA2_MODE_REG) +extern spinlock_t dma_spin_lock; + static __inline__ unsigned long claim_dma_lock(void) { - return 0; + unsigned long flags; + spin_lock_irqsave(&dma_spin_lock, flags); + return flags; } static __inline__ void release_dma_lock(unsigned long flags) { + spin_unlock_irqrestore(&dma_spin_lock, flags); } diff --git a/trunk/include/asm-parisc/futex.h b/trunk/include/asm-parisc/futex.h index d84bbb283fd1..6a332a9f099c 100644 --- a/trunk/include/asm-parisc/futex.h +++ b/trunk/include/asm-parisc/futex.h @@ -1,71 +1,6 @@ -#ifndef _ASM_PARISC_FUTEX_H -#define _ASM_PARISC_FUTEX_H +#ifndef _ASM_FUTEX_H +#define _ASM_FUTEX_H -#ifdef __KERNEL__ +#include -#include -#include -#include - -static inline int -futex_atomic_op_inuser (int encoded_op, int __user *uaddr) -{ - int op = (encoded_op >> 28) & 7; - int cmp = (encoded_op >> 24) & 15; - int oparg = (encoded_op << 8) >> 20; - int cmparg = (encoded_op << 20) >> 20; - int oldval = 0, ret; - if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) - oparg = 1 << oparg; - - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) - return -EFAULT; - - inc_preempt_count(); - - switch (op) { - case FUTEX_OP_SET: - case FUTEX_OP_ADD: - case FUTEX_OP_OR: - case FUTEX_OP_ANDN: - case FUTEX_OP_XOR: - default: - ret = -ENOSYS; - } - - dec_preempt_count(); - - if (!ret) { - switch (cmp) { - case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; - case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; - case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break; - case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break; - case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break; - case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break; - default: ret = -ENOSYS; - } - } - return ret; -} - -/* Non-atomic version */ -static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) -{ - int err = 0; - int uval; - - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) - return -EFAULT; - - err = get_user(uval, uaddr); - if (err) return -EFAULT; - if (uval == oldval) - err = put_user(newval, uaddr); - if (err) return -EFAULT; - return uval; -} - -#endif #endif diff --git a/trunk/include/asm-parisc/io.h b/trunk/include/asm-parisc/io.h index c1963ce19dd2..b9eb245b8874 100644 --- a/trunk/include/asm-parisc/io.h +++ b/trunk/include/asm-parisc/io.h @@ -134,7 +134,7 @@ extern inline void __iomem * ioremap(unsigned long offset, unsigned long size) } #define ioremap_nocache(off, sz) ioremap((off), (sz)) -extern void iounmap(const volatile void __iomem *addr); +extern void iounmap(void __iomem *addr); static inline unsigned char __raw_readb(const volatile void __iomem *addr) { diff --git a/trunk/include/asm-parisc/iosapic.h b/trunk/include/asm-parisc/iosapic.h new file mode 100644 index 000000000000..613390e6805c --- /dev/null +++ b/trunk/include/asm-parisc/iosapic.h @@ -0,0 +1,53 @@ +/* +** This file is private to iosapic driver. +** If stuff needs to be used by another driver, move it to a common file. +** +** WARNING: fields most data structures here are ordered to make sure +** they pack nicely for 64-bit compilation. (ie sizeof(long) == 8) +*/ + + +/* +** I/O SAPIC init function +** Caller knows where an I/O SAPIC is. LBA has an integrated I/O SAPIC. +** Call setup as part of per instance initialization. +** (ie *not* init_module() function unless only one is present.) +** fixup_irq is to initialize PCI IRQ line support and +** virtualize pcidev->irq value. To be called by pci_fixup_bus(). +*/ +extern void *iosapic_register(unsigned long hpa); +extern int iosapic_fixup_irq(void *obj, struct pci_dev *pcidev); + + +#ifdef __IA64__ +/* +** PA: PIB (Processor Interrupt Block) is handled by Runway bus adapter. +** and is hardcoded to 0xfeeNNNN0 where NNNN is id_eid field. +** +** IA64: PIB is handled by "Local SAPIC" (integrated in the processor). +*/ +struct local_sapic_info { + struct local_sapic_info *lsi_next; /* point to next CPU info */ + int *lsi_cpu_id; /* point to logical CPU id */ + unsigned long *lsi_id_eid; /* point to IA-64 CPU id */ + int *lsi_status; /* point to CPU status */ + void *lsi_private; /* point to special info */ +}; + +/* +** "root" data structure which ties everything together. +** Should always be able to start with sapic_root and locate +** the desired information. +*/ +struct sapic_info { + struct sapic_info *si_next; /* info is per cell */ + int si_cellid; /* cell id */ + unsigned int si_status; /* status */ + char *si_pib_base; /* intr blk base address */ + local_sapic_info_t *si_local_info; + io_sapic_info_t *si_io_info; + extint_info_t *si_extint_info;/* External Intr info */ +}; + +#endif /* IA64 */ + diff --git a/trunk/include/asm-parisc/irq.h b/trunk/include/asm-parisc/irq.h index 399c81981ed5..5cae260615a2 100644 --- a/trunk/include/asm-parisc/irq.h +++ b/trunk/include/asm-parisc/irq.h @@ -31,7 +31,7 @@ static __inline__ int irq_canonicalize(int irq) return (irq == 2) ? 9 : irq; } -struct irq_chip; +struct hw_interrupt_type; /* * Some useful "we don't have to do anything here" handlers. Should @@ -39,8 +39,6 @@ struct irq_chip; */ void no_ack_irq(unsigned int irq); void no_end_irq(unsigned int irq); -void cpu_ack_irq(unsigned int irq); -void cpu_end_irq(unsigned int irq); extern int txn_alloc_irq(unsigned int nbits); extern int txn_claim_irq(int); @@ -48,7 +46,7 @@ extern unsigned int txn_alloc_data(unsigned int); extern unsigned long txn_alloc_addr(unsigned int); extern unsigned long txn_affinity_addr(unsigned int irq, int cpu); -extern int cpu_claim_irq(unsigned int irq, struct irq_chip *, void *); +extern int cpu_claim_irq(unsigned int irq, struct hw_interrupt_type *, void *); extern int cpu_check_affinity(unsigned int irq, cpumask_t *dest); /* soft power switch support (power.c) */ diff --git a/trunk/include/asm-parisc/mckinley.h b/trunk/include/asm-parisc/mckinley.h deleted file mode 100644 index d1ea6f12915e..000000000000 --- a/trunk/include/asm-parisc/mckinley.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef ASM_PARISC_MCKINLEY_H -#define ASM_PARISC_MCKINLEY_H -#ifdef __KERNEL__ - -/* declared in arch/parisc/kernel/setup.c */ -extern struct proc_dir_entry * proc_mckinley_root; - -#endif /*__KERNEL__*/ -#endif /*ASM_PARISC_MCKINLEY_H*/ diff --git a/trunk/include/asm-parisc/page.h b/trunk/include/asm-parisc/page.h index 3567208191e3..57d6d82756dd 100644 --- a/trunk/include/asm-parisc/page.h +++ b/trunk/include/asm-parisc/page.h @@ -26,10 +26,24 @@ struct page; -void copy_user_page_asm(void *to, void *from); -void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *pg); -void clear_user_page(void *page, unsigned long vaddr, struct page *pg); +extern void purge_kernel_dcache_page(unsigned long); +extern void copy_user_page_asm(void *to, void *from); +extern void clear_user_page_asm(void *page, unsigned long vaddr); + +static inline void +copy_user_page(void *vto, void *vfrom, unsigned long vaddr, struct page *pg) +{ + copy_user_page_asm(vto, vfrom); + flush_kernel_dcache_page_asm(vto); + /* XXX: ppc flushes icache too, should we? */ +} + +static inline void +clear_user_page(void *page, unsigned long vaddr, struct page *pg) +{ + purge_kernel_dcache_page((unsigned long)page); + clear_user_page_asm(page, vaddr); +} /* * These are used to make use of C type-checking.. diff --git a/trunk/include/asm-parisc/param.h b/trunk/include/asm-parisc/param.h index 32e03d877858..07cb9b93cfe2 100644 --- a/trunk/include/asm-parisc/param.h +++ b/trunk/include/asm-parisc/param.h @@ -2,9 +2,13 @@ #define _ASMPARISC_PARAM_H #ifdef __KERNEL__ -#define HZ CONFIG_HZ -#define USER_HZ 100 /* some user API use "ticks" */ -#define CLOCKS_PER_SEC (USER_HZ) /* like times() */ +# ifdef CONFIG_PA20 +# define HZ 1000 /* Faster machines */ +# else +# define HZ 100 /* Internal kernel timer frequency */ +# endif +# define USER_HZ 100 /* .. some user interfaces are in "ticks" */ +# define CLOCKS_PER_SEC (USER_HZ) /* like times() */ #endif #ifndef HZ diff --git a/trunk/include/asm-parisc/parisc-device.h b/trunk/include/asm-parisc/parisc-device.h index e12624d8941d..1d247e32a608 100644 --- a/trunk/include/asm-parisc/parisc-device.h +++ b/trunk/include/asm-parisc/parisc-device.h @@ -1,6 +1,3 @@ -#ifndef _ASM_PARISC_PARISC_DEVICE_H_ -#define _ASM_PARISC_PARISC_DEVICE_H_ - #include struct parisc_device { @@ -60,5 +57,3 @@ parisc_get_drvdata(struct parisc_device *d) } extern struct bus_type parisc_bus_type; - -#endif /*_ASM_PARISC_PARISC_DEVICE_H_*/ diff --git a/trunk/include/asm-parisc/pci.h b/trunk/include/asm-parisc/pci.h index 7b8ad118d2fe..8b631f47eb25 100644 --- a/trunk/include/asm-parisc/pci.h +++ b/trunk/include/asm-parisc/pci.h @@ -293,9 +293,4 @@ static inline void pcibios_penalize_isa_irq(int irq, int active) /* We don't need to penalize isa irq's */ } -static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) -{ - return channel ? 15 : 14; -} - #endif /* __ASM_PARISC_PCI_H */ diff --git a/trunk/include/asm-parisc/prefetch.h b/trunk/include/asm-parisc/prefetch.h deleted file mode 100644 index 5d021726fa33..000000000000 --- a/trunk/include/asm-parisc/prefetch.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * include/asm-parisc/prefetch.h - * - * PA 2.0 defines data prefetch instructions on page 6-11 of the Kane book. - * In addition, many implementations do hardware prefetching of both - * instructions and data. - * - * PA7300LC (page 14-4 of the ERS) also implements prefetching by a load - * to gr0 but not in a way that Linux can use. If the load would cause an - * interruption (eg due to prefetching 0), it is suppressed on PA2.0 - * processors, but not on 7300LC. - * - */ - -#ifndef __ASM_PARISC_PREFETCH_H -#define __ASM_PARISC_PREFETCH_H - -#ifndef __ASSEMBLY__ -#ifdef CONFIG_PREFETCH - -#define ARCH_HAS_PREFETCH -extern inline void prefetch(const void *addr) -{ - __asm__("ldw 0(%0), %%r0" : : "r" (addr)); -} - -/* LDD is a PA2.0 addition. */ -#ifdef CONFIG_PA20 -#define ARCH_HAS_PREFETCHW -extern inline void prefetchw(const void *addr) -{ - __asm__("ldd 0(%0), %%r0" : : "r" (addr)); -} -#endif /* CONFIG_PA20 */ - -#endif /* CONFIG_PREFETCH */ -#endif /* __ASSEMBLY__ */ - -#endif /* __ASM_PARISC_PROCESSOR_H */ diff --git a/trunk/include/asm-parisc/processor.h b/trunk/include/asm-parisc/processor.h index fd7866dc8c83..b73626f040da 100644 --- a/trunk/include/asm-parisc/processor.h +++ b/trunk/include/asm-parisc/processor.h @@ -9,8 +9,6 @@ #define __ASM_PARISC_PROCESSOR_H #ifndef __ASSEMBLY__ -#include /* lockdep.h needs */ - #include #include @@ -278,7 +276,7 @@ on downward growing arches, it looks like this: */ #ifdef __LP64__ -#define USER_WIDE_MODE (!test_thread_flag(TIF_32BIT)) +#define USER_WIDE_MODE (personality(current->personality) == PER_LINUX) #else #define USER_WIDE_MODE 0 #endif @@ -330,20 +328,33 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) ((tsk)->thread.regs.iaoq[0]) #define KSTK_ESP(tsk) ((tsk)->thread.regs.gr[30]) -#define cpu_relax() barrier() -/* Used as a macro to identify the combined VIPT/PIPT cached - * CPUs which require a guarantee of coherency (no inequivalent - * aliases with different data, whether clean or not) to operate */ -static inline int parisc_requires_coherency(void) +/* + * PA 2.0 defines data prefetch instructions on page 6-11 of the Kane book. + * In addition, many implementations do hardware prefetching of both + * instructions and data. + * + * PA7300LC (page 14-4 of the ERS) also implements prefetching by a load + * to gr0 but not in a way that Linux can use. If the load would cause an + * interruption (eg due to prefetching 0), it is suppressed on PA2.0 + * processors, but not on 7300LC. + */ +#ifdef CONFIG_PREFETCH +#define ARCH_HAS_PREFETCH +#define ARCH_HAS_PREFETCHW + +extern inline void prefetch(const void *addr) { -#ifdef CONFIG_PA8X00 - /* FIXME: also pa8900 - when we see one */ - return boot_cpu_data.cpu_type == mako; -#else - return 0; -#endif + __asm__("ldw 0(%0), %%r0" : : "r" (addr)); +} + +extern inline void prefetchw(const void *addr) +{ + __asm__("ldd 0(%0), %%r0" : : "r" (addr)); } +#endif + +#define cpu_relax() barrier() #endif /* __ASSEMBLY__ */ diff --git a/trunk/include/asm-parisc/ropes.h b/trunk/include/asm-parisc/ropes.h deleted file mode 100644 index 5542dd00472b..000000000000 --- a/trunk/include/asm-parisc/ropes.h +++ /dev/null @@ -1,322 +0,0 @@ -#ifndef _ASM_PARISC_ROPES_H_ -#define _ASM_PARISC_ROPES_H_ - -#include - -#ifdef CONFIG_64BIT -/* "low end" PA8800 machines use ZX1 chipset: PAT PDC and only run 64-bit */ -#define ZX1_SUPPORT -#endif - -#ifdef CONFIG_PROC_FS -/* depends on proc fs support. But costs CPU performance */ -#undef SBA_COLLECT_STATS -#endif - -/* -** The number of pdir entries to "free" before issueing -** a read to PCOM register to flush out PCOM writes. -** Interacts with allocation granularity (ie 4 or 8 entries -** allocated and free'd/purged at a time might make this -** less interesting). -*/ -#define DELAYED_RESOURCE_CNT 16 - -#define MAX_IOC 2 /* per Ike. Pluto/Astro only have 1. */ -#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ - -struct ioc { - void __iomem *ioc_hpa; /* I/O MMU base address */ - char *res_map; /* resource map, bit == pdir entry */ - u64 *pdir_base; /* physical base address */ - unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */ - unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */ -#ifdef ZX1_SUPPORT - unsigned long iovp_mask; /* help convert IOVA to IOVP */ -#endif - unsigned long *res_hint; /* next avail IOVP - circular search */ - spinlock_t res_lock; - unsigned int res_bitshift; /* from the LEFT! */ - unsigned int res_size; /* size of resource map in bytes */ -#ifdef SBA_HINT_SUPPORT -/* FIXME : DMA HINTs not used */ - unsigned long hint_mask_pdir; /* bits used for DMA hints */ - unsigned int hint_shift_pdir; -#endif -#if DELAYED_RESOURCE_CNT > 0 - int saved_cnt; - struct sba_dma_pair { - dma_addr_t iova; - size_t size; - } saved[DELAYED_RESOURCE_CNT]; -#endif - -#ifdef SBA_COLLECT_STATS -#define SBA_SEARCH_SAMPLE 0x100 - unsigned long avg_search[SBA_SEARCH_SAMPLE]; - unsigned long avg_idx; /* current index into avg_search */ - unsigned long used_pages; - unsigned long msingle_calls; - unsigned long msingle_pages; - unsigned long msg_calls; - unsigned long msg_pages; - unsigned long usingle_calls; - unsigned long usingle_pages; - unsigned long usg_calls; - unsigned long usg_pages; -#endif - /* STUFF We don't need in performance path */ - unsigned int pdir_size; /* in bytes, determined by IOV Space size */ -}; - -struct sba_device { - struct sba_device *next; /* list of SBA's in system */ - struct parisc_device *dev; /* dev found in bus walk */ - const char *name; - void __iomem *sba_hpa; /* base address */ - spinlock_t sba_lock; - unsigned int flags; /* state/functionality enabled */ - unsigned int hw_rev; /* HW revision of chip */ - - struct resource chip_resv; /* MMIO reserved for chip */ - struct resource iommu_resv; /* MMIO reserved for iommu */ - - unsigned int num_ioc; /* number of on-board IOC's */ - struct ioc ioc[MAX_IOC]; -}; - -#define ASTRO_RUNWAY_PORT 0x582 -#define IKE_MERCED_PORT 0x803 -#define REO_MERCED_PORT 0x804 -#define REOG_MERCED_PORT 0x805 -#define PLUTO_MCKINLEY_PORT 0x880 - -static inline int IS_ASTRO(struct parisc_device *d) { - return d->id.hversion == ASTRO_RUNWAY_PORT; -} - -static inline int IS_IKE(struct parisc_device *d) { - return d->id.hversion == IKE_MERCED_PORT; -} - -static inline int IS_PLUTO(struct parisc_device *d) { - return d->id.hversion == PLUTO_MCKINLEY_PORT; -} - -#define PLUTO_IOVA_BASE (1UL*1024*1024*1024) /* 1GB */ -#define PLUTO_IOVA_SIZE (1UL*1024*1024*1024) /* 1GB */ -#define PLUTO_GART_SIZE (PLUTO_IOVA_SIZE / 2) - -#define SBA_PDIR_VALID_BIT 0x8000000000000000ULL - -#define SBA_AGPGART_COOKIE 0x0000badbadc0ffeeULL - -#define SBA_FUNC_ID 0x0000 /* function id */ -#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ - -#define SBA_FUNC_SIZE 4096 /* SBA configuration function reg set */ - -#define ASTRO_IOC_OFFSET (32 * SBA_FUNC_SIZE) -#define PLUTO_IOC_OFFSET (1 * SBA_FUNC_SIZE) -/* Ike's IOC's occupy functions 2 and 3 */ -#define IKE_IOC_OFFSET(p) ((p+2) * SBA_FUNC_SIZE) - -#define IOC_CTRL 0x8 /* IOC_CTRL offset */ -#define IOC_CTRL_TC (1 << 0) /* TOC Enable */ -#define IOC_CTRL_CE (1 << 1) /* Coalesce Enable */ -#define IOC_CTRL_DE (1 << 2) /* Dillon Enable */ -#define IOC_CTRL_RM (1 << 8) /* Real Mode */ -#define IOC_CTRL_NC (1 << 9) /* Non Coherent Mode */ -#define IOC_CTRL_D4 (1 << 11) /* Disable 4-byte coalescing */ -#define IOC_CTRL_DD (1 << 13) /* Disable distr. LMMIO range coalescing */ - -/* -** Offsets into MBIB (Function 0 on Ike and hopefully Astro) -** Firmware programs this stuff. Don't touch it. -*/ -#define LMMIO_DIRECT0_BASE 0x300 -#define LMMIO_DIRECT0_MASK 0x308 -#define LMMIO_DIRECT0_ROUTE 0x310 - -#define LMMIO_DIST_BASE 0x360 -#define LMMIO_DIST_MASK 0x368 -#define LMMIO_DIST_ROUTE 0x370 - -#define IOS_DIST_BASE 0x390 -#define IOS_DIST_MASK 0x398 -#define IOS_DIST_ROUTE 0x3A0 - -#define IOS_DIRECT_BASE 0x3C0 -#define IOS_DIRECT_MASK 0x3C8 -#define IOS_DIRECT_ROUTE 0x3D0 - -/* -** Offsets into I/O TLB (Function 2 and 3 on Ike) -*/ -#define ROPE0_CTL 0x200 /* "regbus pci0" */ -#define ROPE1_CTL 0x208 -#define ROPE2_CTL 0x210 -#define ROPE3_CTL 0x218 -#define ROPE4_CTL 0x220 -#define ROPE5_CTL 0x228 -#define ROPE6_CTL 0x230 -#define ROPE7_CTL 0x238 - -#define IOC_ROPE0_CFG 0x500 /* pluto only */ -#define IOC_ROPE_AO 0x10 /* Allow "Relaxed Ordering" */ - -#define HF_ENABLE 0x40 - -#define IOC_IBASE 0x300 /* IO TLB */ -#define IOC_IMASK 0x308 -#define IOC_PCOM 0x310 -#define IOC_TCNFG 0x318 -#define IOC_PDIR_BASE 0x320 - -/* -** IOC supports 4/8/16/64KB page sizes (see TCNFG register) -** It's safer (avoid memory corruption) to keep DMA page mappings -** equivalently sized to VM PAGE_SIZE. -** -** We really can't avoid generating a new mapping for each -** page since the Virtual Coherence Index has to be generated -** and updated for each page. -** -** PAGE_SIZE could be greater than IOVP_SIZE. But not the inverse. -*/ -#define IOVP_SIZE PAGE_SIZE -#define IOVP_SHIFT PAGE_SHIFT -#define IOVP_MASK PAGE_MASK - -#define SBA_PERF_CFG 0x708 /* Performance Counter stuff */ -#define SBA_PERF_MASK1 0x718 -#define SBA_PERF_MASK2 0x730 - -/* -** Offsets into PCI Performance Counters (functions 12 and 13) -** Controlled by PERF registers in function 2 & 3 respectively. -*/ -#define SBA_PERF_CNT1 0x200 -#define SBA_PERF_CNT2 0x208 -#define SBA_PERF_CNT3 0x210 - -/* -** lba_device: Per instance Elroy data structure -*/ -struct lba_device { - struct pci_hba_data hba; - - spinlock_t lba_lock; - void *iosapic_obj; - -#ifdef CONFIG_64BIT - void __iomem *iop_base; /* PA_VIEW - for IO port accessor funcs */ -#endif - - int flags; /* state/functionality enabled */ - int hw_rev; /* HW revision of chip */ -}; - -#define ELROY_HVERS 0x782 -#define MERCURY_HVERS 0x783 -#define QUICKSILVER_HVERS 0x784 - -static inline int IS_ELROY(struct parisc_device *d) { - return (d->id.hversion == ELROY_HVERS); -} - -static inline int IS_MERCURY(struct parisc_device *d) { - return (d->id.hversion == MERCURY_HVERS); -} - -static inline int IS_QUICKSILVER(struct parisc_device *d) { - return (d->id.hversion == QUICKSILVER_HVERS); -} - -static inline int agp_mode_mercury(void __iomem *hpa) { - u64 bus_mode; - - bus_mode = readl(hpa + 0x0620); - if (bus_mode & 1) - return 1; - - return 0; -} - -/* -** I/O SAPIC init function -** Caller knows where an I/O SAPIC is. LBA has an integrated I/O SAPIC. -** Call setup as part of per instance initialization. -** (ie *not* init_module() function unless only one is present.) -** fixup_irq is to initialize PCI IRQ line support and -** virtualize pcidev->irq value. To be called by pci_fixup_bus(). -*/ -extern void *iosapic_register(unsigned long hpa); -extern int iosapic_fixup_irq(void *obj, struct pci_dev *pcidev); - -#define LBA_FUNC_ID 0x0000 /* function id */ -#define LBA_FCLASS 0x0008 /* function class, bist, header, rev... */ -#define LBA_CAPABLE 0x0030 /* capabilities register */ - -#define LBA_PCI_CFG_ADDR 0x0040 /* poke CFG address here */ -#define LBA_PCI_CFG_DATA 0x0048 /* read or write data here */ - -#define LBA_PMC_MTLT 0x0050 /* Firmware sets this - read only. */ -#define LBA_FW_SCRATCH 0x0058 /* Firmware writes the PCI bus number here. */ -#define LBA_ERROR_ADDR 0x0070 /* On error, address gets logged here */ - -#define LBA_ARB_MASK 0x0080 /* bit 0 enable arbitration. PAT/PDC enables */ -#define LBA_ARB_PRI 0x0088 /* firmware sets this. */ -#define LBA_ARB_MODE 0x0090 /* firmware sets this. */ -#define LBA_ARB_MTLT 0x0098 /* firmware sets this. */ - -#define LBA_MOD_ID 0x0100 /* Module ID. PDC_PAT_CELL reports 4 */ - -#define LBA_STAT_CTL 0x0108 /* Status & Control */ -#define LBA_BUS_RESET 0x01 /* Deassert PCI Bus Reset Signal */ -#define CLEAR_ERRLOG 0x10 /* "Clear Error Log" cmd */ -#define CLEAR_ERRLOG_ENABLE 0x20 /* "Clear Error Log" Enable */ -#define HF_ENABLE 0x40 /* enable HF mode (default is -1 mode) */ - -#define LBA_LMMIO_BASE 0x0200 /* < 4GB I/O address range */ -#define LBA_LMMIO_MASK 0x0208 - -#define LBA_GMMIO_BASE 0x0210 /* > 4GB I/O address range */ -#define LBA_GMMIO_MASK 0x0218 - -#define LBA_WLMMIO_BASE 0x0220 /* All < 4GB ranges under the same *SBA* */ -#define LBA_WLMMIO_MASK 0x0228 - -#define LBA_WGMMIO_BASE 0x0230 /* All > 4GB ranges under the same *SBA* */ -#define LBA_WGMMIO_MASK 0x0238 - -#define LBA_IOS_BASE 0x0240 /* I/O port space for this LBA */ -#define LBA_IOS_MASK 0x0248 - -#define LBA_ELMMIO_BASE 0x0250 /* Extra LMMIO range */ -#define LBA_ELMMIO_MASK 0x0258 - -#define LBA_EIOS_BASE 0x0260 /* Extra I/O port space */ -#define LBA_EIOS_MASK 0x0268 - -#define LBA_GLOBAL_MASK 0x0270 /* Mercury only: Global Address Mask */ -#define LBA_DMA_CTL 0x0278 /* firmware sets this */ - -#define LBA_IBASE 0x0300 /* SBA DMA support */ -#define LBA_IMASK 0x0308 - -/* FIXME: ignore DMA Hint stuff until we can measure performance */ -#define LBA_HINT_CFG 0x0310 -#define LBA_HINT_BASE 0x0380 /* 14 registers at every 8 bytes. */ - -#define LBA_BUS_MODE 0x0620 - -/* ERROR regs are needed for config cycle kluges */ -#define LBA_ERROR_CONFIG 0x0680 -#define LBA_SMART_MODE 0x20 -#define LBA_ERROR_STATUS 0x0688 -#define LBA_ROPE_CTL 0x06A0 - -#define LBA_IOSAPIC_BASE 0x800 /* Offset of IRQ logic */ - -#endif /*_ASM_PARISC_ROPES_H_*/ diff --git a/trunk/include/asm-parisc/serial.h b/trunk/include/asm-parisc/serial.h index d7e3cc60dbc3..82fd820d684f 100644 --- a/trunk/include/asm-parisc/serial.h +++ b/trunk/include/asm-parisc/serial.h @@ -3,8 +3,20 @@ */ /* - * This is used for 16550-compatible UARTs + * This assumes you have a 7.272727 MHz clock for your UART. + * The documentation implies a 40Mhz clock, and elsewhere a 7Mhz clock + * Clarified: 7.2727MHz on LASI. Not yet clarified for DINO */ -#define BASE_BAUD ( 1843200 / 16 ) +#define LASI_BASE_BAUD ( 7272727 / 16 ) +#define BASE_BAUD LASI_BASE_BAUD + +/* + * We don't use the ISA probing code, so these entries are just to reserve + * space. Some example (maximal) configurations: + * - 712 w/ additional Lasi & RJ16 ports: 4 + * - J5k w/ PCI serial cards: 2 + 4 * card ~= 34 + * A500 w/ PCI serial cards: 5 + 4 * card ~= 17 + */ + #define SERIAL_PORT_DFNS diff --git a/trunk/include/asm-parisc/spinlock.h b/trunk/include/asm-parisc/spinlock.h index f3d2090a18dc..e1825530365d 100644 --- a/trunk/include/asm-parisc/spinlock.h +++ b/trunk/include/asm-parisc/spinlock.h @@ -56,79 +56,50 @@ static inline int __raw_spin_trylock(raw_spinlock_t *x) } /* - * Read-write spinlocks, allowing multiple readers but only one writer. - * Linux rwlocks are unfair to writers; they can be starved for an indefinite - * time by readers. With care, they can also be taken in interrupt context. - * - * In the PA-RISC implementation, we have a spinlock and a counter. - * Readers use the lock to serialise their access to the counter (which - * records how many readers currently hold the lock). - * Writers hold the spinlock, preventing any readers or other writers from - * grabbing the rwlock. + * Read-write spinlocks, allowing multiple readers + * but only one writer. */ -/* Note that we have to ensure interrupts are disabled in case we're - * interrupted by some other code that wants to grab the same read lock */ +#define __raw_read_trylock(lock) generic__raw_read_trylock(lock) + +/* read_lock, read_unlock are pretty straightforward. Of course it somehow + * sucks we end up saving/restoring flags twice for read_lock_irqsave aso. */ + static __inline__ void __raw_read_lock(raw_rwlock_t *rw) { - unsigned long flags; - local_irq_save(flags); - __raw_spin_lock_flags(&rw->lock, flags); + __raw_spin_lock(&rw->lock); + rw->counter++; + __raw_spin_unlock(&rw->lock); - local_irq_restore(flags); } -/* Note that we have to ensure interrupts are disabled in case we're - * interrupted by some other code that wants to grab the same read lock */ static __inline__ void __raw_read_unlock(raw_rwlock_t *rw) { - unsigned long flags; - local_irq_save(flags); - __raw_spin_lock_flags(&rw->lock, flags); + __raw_spin_lock(&rw->lock); + rw->counter--; + __raw_spin_unlock(&rw->lock); - local_irq_restore(flags); } -/* Note that we have to ensure interrupts are disabled in case we're - * interrupted by some other code that wants to grab the same read lock */ -static __inline__ int __raw_read_trylock(raw_rwlock_t *rw) -{ - unsigned long flags; - retry: - local_irq_save(flags); - if (__raw_spin_trylock(&rw->lock)) { - rw->counter++; - __raw_spin_unlock(&rw->lock); - local_irq_restore(flags); - return 1; - } - - local_irq_restore(flags); - /* If write-locked, we fail to acquire the lock */ - if (rw->counter < 0) - return 0; - - /* Wait until we have a realistic chance at the lock */ - while (__raw_spin_is_locked(&rw->lock) && rw->counter >= 0) - cpu_relax(); - - goto retry; -} +/* write_lock is less trivial. We optimistically grab the lock and check + * if we surprised any readers. If so we release the lock and wait till + * they're all gone before trying again + * + * Also note that we don't use the _irqsave / _irqrestore suffixes here. + * If we're called with interrupts enabled and we've got readers (or other + * writers) in interrupt handlers someone fucked up and we'd dead-lock + * sooner or later anyway. prumpf */ -/* Note that we have to ensure interrupts are disabled in case we're - * interrupted by some other code that wants to read_trylock() this lock */ -static __inline__ void __raw_write_lock(raw_rwlock_t *rw) +static __inline__ void __raw_write_lock(raw_rwlock_t *rw) { - unsigned long flags; retry: - local_irq_save(flags); - __raw_spin_lock_flags(&rw->lock, flags); + __raw_spin_lock(&rw->lock); - if (rw->counter != 0) { + if(rw->counter != 0) { + /* this basically never happens */ __raw_spin_unlock(&rw->lock); - local_irq_restore(flags); while (rw->counter != 0) cpu_relax(); @@ -136,37 +107,31 @@ static __inline__ void __raw_write_lock(raw_rwlock_t *rw) goto retry; } - rw->counter = -1; /* mark as write-locked */ - mb(); - local_irq_restore(flags); + /* got it. now leave without unlocking */ + rw->counter = -1; /* remember we are locked */ } -static __inline__ void __raw_write_unlock(raw_rwlock_t *rw) +/* write_unlock is absolutely trivial - we don't have to wait for anything */ + +static __inline__ void __raw_write_unlock(raw_rwlock_t *rw) { rw->counter = 0; __raw_spin_unlock(&rw->lock); } -/* Note that we have to ensure interrupts are disabled in case we're - * interrupted by some other code that wants to read_trylock() this lock */ -static __inline__ int __raw_write_trylock(raw_rwlock_t *rw) +static __inline__ int __raw_write_trylock(raw_rwlock_t *rw) { - unsigned long flags; - int result = 0; - - local_irq_save(flags); - if (__raw_spin_trylock(&rw->lock)) { - if (rw->counter == 0) { - rw->counter = -1; - result = 1; - } else { - /* Read-locked. Oh well. */ - __raw_spin_unlock(&rw->lock); - } + __raw_spin_lock(&rw->lock); + if (rw->counter != 0) { + /* this basically never happens */ + __raw_spin_unlock(&rw->lock); + + return 0; } - local_irq_restore(flags); - return result; + /* got it. now leave without unlocking */ + rw->counter = -1; /* remember we are locked */ + return 1; } /* diff --git a/trunk/include/asm-powerpc/firmware.h b/trunk/include/asm-powerpc/firmware.h index 1022737f4f34..77069df92bf8 100644 --- a/trunk/include/asm-powerpc/firmware.h +++ b/trunk/include/asm-powerpc/firmware.h @@ -14,36 +14,34 @@ #ifdef __KERNEL__ -#include +#ifndef __ASSEMBLY__ /* firmware feature bitmask values */ #define FIRMWARE_MAX_FEATURES 63 -#define FW_FEATURE_PFT ASM_CONST(0x0000000000000001) -#define FW_FEATURE_TCE ASM_CONST(0x0000000000000002) -#define FW_FEATURE_SPRG0 ASM_CONST(0x0000000000000004) -#define FW_FEATURE_DABR ASM_CONST(0x0000000000000008) -#define FW_FEATURE_COPY ASM_CONST(0x0000000000000010) -#define FW_FEATURE_ASR ASM_CONST(0x0000000000000020) -#define FW_FEATURE_DEBUG ASM_CONST(0x0000000000000040) -#define FW_FEATURE_TERM ASM_CONST(0x0000000000000080) -#define FW_FEATURE_PERF ASM_CONST(0x0000000000000100) -#define FW_FEATURE_DUMP ASM_CONST(0x0000000000000200) -#define FW_FEATURE_INTERRUPT ASM_CONST(0x0000000000000400) -#define FW_FEATURE_MIGRATE ASM_CONST(0x0000000000000800) -#define FW_FEATURE_PERFMON ASM_CONST(0x0000000000001000) -#define FW_FEATURE_CRQ ASM_CONST(0x0000000000002000) -#define FW_FEATURE_VIO ASM_CONST(0x0000000000004000) -#define FW_FEATURE_RDMA ASM_CONST(0x0000000000008000) -#define FW_FEATURE_LLAN ASM_CONST(0x0000000000010000) -#define FW_FEATURE_BULK ASM_CONST(0x0000000000020000) -#define FW_FEATURE_XDABR ASM_CONST(0x0000000000040000) -#define FW_FEATURE_MULTITCE ASM_CONST(0x0000000000080000) -#define FW_FEATURE_SPLPAR ASM_CONST(0x0000000000100000) -#define FW_FEATURE_ISERIES ASM_CONST(0x0000000000200000) -#define FW_FEATURE_LPAR ASM_CONST(0x0000000000400000) - -#ifndef __ASSEMBLY__ +#define FW_FEATURE_PFT (1UL<<0) +#define FW_FEATURE_TCE (1UL<<1) +#define FW_FEATURE_SPRG0 (1UL<<2) +#define FW_FEATURE_DABR (1UL<<3) +#define FW_FEATURE_COPY (1UL<<4) +#define FW_FEATURE_ASR (1UL<<5) +#define FW_FEATURE_DEBUG (1UL<<6) +#define FW_FEATURE_TERM (1UL<<7) +#define FW_FEATURE_PERF (1UL<<8) +#define FW_FEATURE_DUMP (1UL<<9) +#define FW_FEATURE_INTERRUPT (1UL<<10) +#define FW_FEATURE_MIGRATE (1UL<<11) +#define FW_FEATURE_PERFMON (1UL<<12) +#define FW_FEATURE_CRQ (1UL<<13) +#define FW_FEATURE_VIO (1UL<<14) +#define FW_FEATURE_RDMA (1UL<<15) +#define FW_FEATURE_LLAN (1UL<<16) +#define FW_FEATURE_BULK (1UL<<17) +#define FW_FEATURE_XDABR (1UL<<18) +#define FW_FEATURE_MULTITCE (1UL<<19) +#define FW_FEATURE_SPLPAR (1UL<<20) +#define FW_FEATURE_ISERIES (1UL<<21) +#define FW_FEATURE_LPAR (1UL<<22) enum { #ifdef CONFIG_PPC64 @@ -96,23 +94,6 @@ extern void machine_check_fwnmi(void); /* This is true if we are using the firmware NMI handler (typically LPAR) */ extern int fwnmi_active; -#else /* __ASSEMBLY__ */ - -#define BEGIN_FW_FTR_SECTION 96: - -#define END_FW_FTR_SECTION(msk, val) \ -97: \ - .section __fw_ftr_fixup,"a"; \ - .align 3; \ - .llong msk; \ - .llong val; \ - .llong 96b; \ - .llong 97b; \ - .previous - -#define END_FW_FTR_SECTION_IFSET(msk) END_FW_FTR_SECTION((msk), (msk)) -#define END_FW_FTR_SECTION_IFCLR(msk) END_FW_FTR_SECTION((msk), 0) - #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* __ASM_POWERPC_FIRMWARE_H */ diff --git a/trunk/include/asm-powerpc/immap_qe.h b/trunk/include/asm-powerpc/immap_qe.h deleted file mode 100644 index ce12f85fff9b..000000000000 --- a/trunk/include/asm-powerpc/immap_qe.h +++ /dev/null @@ -1,477 +0,0 @@ -/* - * include/asm-powerpc/immap_qe.h - * - * QUICC Engine (QE) Internal Memory Map. - * The Internal Memory Map for devices with QE on them. This - * is the superset of all QE devices (8360, etc.). - - * Copyright (C) 2006. Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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. - */ -#ifndef _ASM_POWERPC_IMMAP_QE_H -#define _ASM_POWERPC_IMMAP_QE_H -#ifdef __KERNEL__ - -#include - -#define QE_IMMAP_SIZE (1024 * 1024) /* 1MB from 1MB+IMMR */ - -/* QE I-RAM */ -struct qe_iram { - __be32 iadd; /* I-RAM Address Register */ - __be32 idata; /* I-RAM Data Register */ - u8 res0[0x78]; -} __attribute__ ((packed)); - -/* QE Interrupt Controller */ -struct qe_ic_regs { - __be32 qicr; - __be32 qivec; - __be32 qripnr; - __be32 qipnr; - __be32 qipxcc; - __be32 qipycc; - __be32 qipwcc; - __be32 qipzcc; - __be32 qimr; - __be32 qrimr; - __be32 qicnr; - u8 res0[0x4]; - __be32 qiprta; - __be32 qiprtb; - u8 res1[0x4]; - __be32 qricr; - u8 res2[0x20]; - __be32 qhivec; - u8 res3[0x1C]; -} __attribute__ ((packed)); - -/* Communications Processor */ -struct cp_qe { - __be32 cecr; /* QE command register */ - __be32 ceccr; /* QE controller configuration register */ - __be32 cecdr; /* QE command data register */ - u8 res0[0xA]; - __be16 ceter; /* QE timer event register */ - u8 res1[0x2]; - __be16 cetmr; /* QE timers mask register */ - __be32 cetscr; /* QE time-stamp timer control register */ - __be32 cetsr1; /* QE time-stamp register 1 */ - __be32 cetsr2; /* QE time-stamp register 2 */ - u8 res2[0x8]; - __be32 cevter; /* QE virtual tasks event register */ - __be32 cevtmr; /* QE virtual tasks mask register */ - __be16 cercr; /* QE RAM control register */ - u8 res3[0x2]; - u8 res4[0x24]; - __be16 ceexe1; /* QE external request 1 event register */ - u8 res5[0x2]; - __be16 ceexm1; /* QE external request 1 mask register */ - u8 res6[0x2]; - __be16 ceexe2; /* QE external request 2 event register */ - u8 res7[0x2]; - __be16 ceexm2; /* QE external request 2 mask register */ - u8 res8[0x2]; - __be16 ceexe3; /* QE external request 3 event register */ - u8 res9[0x2]; - __be16 ceexm3; /* QE external request 3 mask register */ - u8 res10[0x2]; - __be16 ceexe4; /* QE external request 4 event register */ - u8 res11[0x2]; - __be16 ceexm4; /* QE external request 4 mask register */ - u8 res12[0x2]; - u8 res13[0x280]; -} __attribute__ ((packed)); - -/* QE Multiplexer */ -struct qe_mux { - __be32 cmxgcr; /* CMX general clock route register */ - __be32 cmxsi1cr_l; /* CMX SI1 clock route low register */ - __be32 cmxsi1cr_h; /* CMX SI1 clock route high register */ - __be32 cmxsi1syr; /* CMX SI1 SYNC route register */ - __be32 cmxucr1; /* CMX UCC1, UCC3 clock route register */ - __be32 cmxucr2; /* CMX UCC5, UCC7 clock route register */ - __be32 cmxucr3; /* CMX UCC2, UCC4 clock route register */ - __be32 cmxucr4; /* CMX UCC6, UCC8 clock route register */ - __be32 cmxupcr; /* CMX UPC clock route register */ - u8 res0[0x1C]; -} __attribute__ ((packed)); - -/* QE Timers */ -struct qe_timers { - u8 gtcfr1; /* Timer 1 and Timer 2 global config register*/ - u8 res0[0x3]; - u8 gtcfr2; /* Timer 3 and timer 4 global config register*/ - u8 res1[0xB]; - __be16 gtmdr1; /* Timer 1 mode register */ - __be16 gtmdr2; /* Timer 2 mode register */ - __be16 gtrfr1; /* Timer 1 reference register */ - __be16 gtrfr2; /* Timer 2 reference register */ - __be16 gtcpr1; /* Timer 1 capture register */ - __be16 gtcpr2; /* Timer 2 capture register */ - __be16 gtcnr1; /* Timer 1 counter */ - __be16 gtcnr2; /* Timer 2 counter */ - __be16 gtmdr3; /* Timer 3 mode register */ - __be16 gtmdr4; /* Timer 4 mode register */ - __be16 gtrfr3; /* Timer 3 reference register */ - __be16 gtrfr4; /* Timer 4 reference register */ - __be16 gtcpr3; /* Timer 3 capture register */ - __be16 gtcpr4; /* Timer 4 capture register */ - __be16 gtcnr3; /* Timer 3 counter */ - __be16 gtcnr4; /* Timer 4 counter */ - __be16 gtevr1; /* Timer 1 event register */ - __be16 gtevr2; /* Timer 2 event register */ - __be16 gtevr3; /* Timer 3 event register */ - __be16 gtevr4; /* Timer 4 event register */ - __be16 gtps; /* Timer 1 prescale register */ - u8 res2[0x46]; -} __attribute__ ((packed)); - -/* BRG */ -struct qe_brg { - __be32 brgc1; /* BRG1 configuration register */ - __be32 brgc2; /* BRG2 configuration register */ - __be32 brgc3; /* BRG3 configuration register */ - __be32 brgc4; /* BRG4 configuration register */ - __be32 brgc5; /* BRG5 configuration register */ - __be32 brgc6; /* BRG6 configuration register */ - __be32 brgc7; /* BRG7 configuration register */ - __be32 brgc8; /* BRG8 configuration register */ - __be32 brgc9; /* BRG9 configuration register */ - __be32 brgc10; /* BRG10 configuration register */ - __be32 brgc11; /* BRG11 configuration register */ - __be32 brgc12; /* BRG12 configuration register */ - __be32 brgc13; /* BRG13 configuration register */ - __be32 brgc14; /* BRG14 configuration register */ - __be32 brgc15; /* BRG15 configuration register */ - __be32 brgc16; /* BRG16 configuration register */ - u8 res0[0x40]; -} __attribute__ ((packed)); - -/* SPI */ -struct spi { - u8 res0[0x20]; - __be32 spmode; /* SPI mode register */ - u8 res1[0x2]; - u8 spie; /* SPI event register */ - u8 res2[0x1]; - u8 res3[0x2]; - u8 spim; /* SPI mask register */ - u8 res4[0x1]; - u8 res5[0x1]; - u8 spcom; /* SPI command register */ - u8 res6[0x2]; - __be32 spitd; /* SPI transmit data register (cpu mode) */ - __be32 spird; /* SPI receive data register (cpu mode) */ - u8 res7[0x8]; -} __attribute__ ((packed)); - -/* SI */ -struct si1 { - __be16 siamr1; /* SI1 TDMA mode register */ - __be16 sibmr1; /* SI1 TDMB mode register */ - __be16 sicmr1; /* SI1 TDMC mode register */ - __be16 sidmr1; /* SI1 TDMD mode register */ - u8 siglmr1_h; /* SI1 global mode register high */ - u8 res0[0x1]; - u8 sicmdr1_h; /* SI1 command register high */ - u8 res2[0x1]; - u8 sistr1_h; /* SI1 status register high */ - u8 res3[0x1]; - __be16 sirsr1_h; /* SI1 RAM shadow address register high */ - u8 sitarc1; /* SI1 RAM counter Tx TDMA */ - u8 sitbrc1; /* SI1 RAM counter Tx TDMB */ - u8 sitcrc1; /* SI1 RAM counter Tx TDMC */ - u8 sitdrc1; /* SI1 RAM counter Tx TDMD */ - u8 sirarc1; /* SI1 RAM counter Rx TDMA */ - u8 sirbrc1; /* SI1 RAM counter Rx TDMB */ - u8 sircrc1; /* SI1 RAM counter Rx TDMC */ - u8 sirdrc1; /* SI1 RAM counter Rx TDMD */ - u8 res4[0x8]; - __be16 siemr1; /* SI1 TDME mode register 16 bits */ - __be16 sifmr1; /* SI1 TDMF mode register 16 bits */ - __be16 sigmr1; /* SI1 TDMG mode register 16 bits */ - __be16 sihmr1; /* SI1 TDMH mode register 16 bits */ - u8 siglmg1_l; /* SI1 global mode register low 8 bits */ - u8 res5[0x1]; - u8 sicmdr1_l; /* SI1 command register low 8 bits */ - u8 res6[0x1]; - u8 sistr1_l; /* SI1 status register low 8 bits */ - u8 res7[0x1]; - __be16 sirsr1_l; /* SI1 RAM shadow address register low 16 bits*/ - u8 siterc1; /* SI1 RAM counter Tx TDME 8 bits */ - u8 sitfrc1; /* SI1 RAM counter Tx TDMF 8 bits */ - u8 sitgrc1; /* SI1 RAM counter Tx TDMG 8 bits */ - u8 sithrc1; /* SI1 RAM counter Tx TDMH 8 bits */ - u8 sirerc1; /* SI1 RAM counter Rx TDME 8 bits */ - u8 sirfrc1; /* SI1 RAM counter Rx TDMF 8 bits */ - u8 sirgrc1; /* SI1 RAM counter Rx TDMG 8 bits */ - u8 sirhrc1; /* SI1 RAM counter Rx TDMH 8 bits */ - u8 res8[0x8]; - __be32 siml1; /* SI1 multiframe limit register */ - u8 siedm1; /* SI1 extended diagnostic mode register */ - u8 res9[0xBB]; -} __attribute__ ((packed)); - -/* SI Routing Tables */ -struct sir { - u8 tx[0x400]; - u8 rx[0x400]; - u8 res0[0x800]; -} __attribute__ ((packed)); - -/* USB Controller */ -struct usb_ctlr { - u8 usb_usmod; - u8 usb_usadr; - u8 usb_uscom; - u8 res1[1]; - __be16 usb_usep1; - __be16 usb_usep2; - __be16 usb_usep3; - __be16 usb_usep4; - u8 res2[4]; - __be16 usb_usber; - u8 res3[2]; - __be16 usb_usbmr; - u8 res4[1]; - u8 usb_usbs; - __be16 usb_ussft; - u8 res5[2]; - __be16 usb_usfrn; - u8 res6[0x22]; -} __attribute__ ((packed)); - -/* MCC */ -struct mcc { - __be32 mcce; /* MCC event register */ - __be32 mccm; /* MCC mask register */ - __be32 mccf; /* MCC configuration register */ - __be32 merl; /* MCC emergency request level register */ - u8 res0[0xF0]; -} __attribute__ ((packed)); - -/* QE UCC Slow */ -struct ucc_slow { - __be32 gumr_l; /* UCCx general mode register (low) */ - __be32 gumr_h; /* UCCx general mode register (high) */ - __be16 upsmr; /* UCCx protocol-specific mode register */ - u8 res0[0x2]; - __be16 utodr; /* UCCx transmit on demand register */ - __be16 udsr; /* UCCx data synchronization register */ - __be16 ucce; /* UCCx event register */ - u8 res1[0x2]; - __be16 uccm; /* UCCx mask register */ - u8 res2[0x1]; - u8 uccs; /* UCCx status register */ - u8 res3[0x24]; - __be16 utpt; - u8 guemr; /* UCC general extended mode register */ - u8 res4[0x200 - 0x091]; -} __attribute__ ((packed)); - -/* QE UCC Fast */ -struct ucc_fast { - __be32 gumr; /* UCCx general mode register */ - __be32 upsmr; /* UCCx protocol-specific mode register */ - __be16 utodr; /* UCCx transmit on demand register */ - u8 res0[0x2]; - __be16 udsr; /* UCCx data synchronization register */ - u8 res1[0x2]; - __be32 ucce; /* UCCx event register */ - __be32 uccm; /* UCCx mask register */ - u8 uccs; /* UCCx status register */ - u8 res2[0x7]; - __be32 urfb; /* UCC receive FIFO base */ - __be16 urfs; /* UCC receive FIFO size */ - u8 res3[0x2]; - __be16 urfet; /* UCC receive FIFO emergency threshold */ - __be16 urfset; /* UCC receive FIFO special emergency - threshold */ - __be32 utfb; /* UCC transmit FIFO base */ - __be16 utfs; /* UCC transmit FIFO size */ - u8 res4[0x2]; - __be16 utfet; /* UCC transmit FIFO emergency threshold */ - u8 res5[0x2]; - __be16 utftt; /* UCC transmit FIFO transmit threshold */ - u8 res6[0x2]; - __be16 utpt; /* UCC transmit polling timer */ - u8 res7[0x2]; - __be32 urtry; /* UCC retry counter register */ - u8 res8[0x4C]; - u8 guemr; /* UCC general extended mode register */ - u8 res9[0x100 - 0x091]; -} __attribute__ ((packed)); - -/* QE UCC */ -struct ucc_common { - u8 res1[0x90]; - u8 guemr; - u8 res2[0x200 - 0x091]; -} __attribute__ ((packed)); - -struct ucc { - union { - struct ucc_slow slow; - struct ucc_fast fast; - struct ucc_common common; - }; -} __attribute__ ((packed)); - -/* MultiPHY UTOPIA POS Controllers (UPC) */ -struct upc { - __be32 upgcr; /* UTOPIA/POS general configuration register */ - __be32 uplpa; /* UTOPIA/POS last PHY address */ - __be32 uphec; /* ATM HEC register */ - __be32 upuc; /* UTOPIA/POS UCC configuration */ - __be32 updc1; /* UTOPIA/POS device 1 configuration */ - __be32 updc2; /* UTOPIA/POS device 2 configuration */ - __be32 updc3; /* UTOPIA/POS device 3 configuration */ - __be32 updc4; /* UTOPIA/POS device 4 configuration */ - __be32 upstpa; /* UTOPIA/POS STPA threshold */ - u8 res0[0xC]; - __be32 updrs1_h; /* UTOPIA/POS device 1 rate select */ - __be32 updrs1_l; /* UTOPIA/POS device 1 rate select */ - __be32 updrs2_h; /* UTOPIA/POS device 2 rate select */ - __be32 updrs2_l; /* UTOPIA/POS device 2 rate select */ - __be32 updrs3_h; /* UTOPIA/POS device 3 rate select */ - __be32 updrs3_l; /* UTOPIA/POS device 3 rate select */ - __be32 updrs4_h; /* UTOPIA/POS device 4 rate select */ - __be32 updrs4_l; /* UTOPIA/POS device 4 rate select */ - __be32 updrp1; /* UTOPIA/POS device 1 receive priority low */ - __be32 updrp2; /* UTOPIA/POS device 2 receive priority low */ - __be32 updrp3; /* UTOPIA/POS device 3 receive priority low */ - __be32 updrp4; /* UTOPIA/POS device 4 receive priority low */ - __be32 upde1; /* UTOPIA/POS device 1 event */ - __be32 upde2; /* UTOPIA/POS device 2 event */ - __be32 upde3; /* UTOPIA/POS device 3 event */ - __be32 upde4; /* UTOPIA/POS device 4 event */ - __be16 uprp1; - __be16 uprp2; - __be16 uprp3; - __be16 uprp4; - u8 res1[0x8]; - __be16 uptirr1_0; /* Device 1 transmit internal rate 0 */ - __be16 uptirr1_1; /* Device 1 transmit internal rate 1 */ - __be16 uptirr1_2; /* Device 1 transmit internal rate 2 */ - __be16 uptirr1_3; /* Device 1 transmit internal rate 3 */ - __be16 uptirr2_0; /* Device 2 transmit internal rate 0 */ - __be16 uptirr2_1; /* Device 2 transmit internal rate 1 */ - __be16 uptirr2_2; /* Device 2 transmit internal rate 2 */ - __be16 uptirr2_3; /* Device 2 transmit internal rate 3 */ - __be16 uptirr3_0; /* Device 3 transmit internal rate 0 */ - __be16 uptirr3_1; /* Device 3 transmit internal rate 1 */ - __be16 uptirr3_2; /* Device 3 transmit internal rate 2 */ - __be16 uptirr3_3; /* Device 3 transmit internal rate 3 */ - __be16 uptirr4_0; /* Device 4 transmit internal rate 0 */ - __be16 uptirr4_1; /* Device 4 transmit internal rate 1 */ - __be16 uptirr4_2; /* Device 4 transmit internal rate 2 */ - __be16 uptirr4_3; /* Device 4 transmit internal rate 3 */ - __be32 uper1; /* Device 1 port enable register */ - __be32 uper2; /* Device 2 port enable register */ - __be32 uper3; /* Device 3 port enable register */ - __be32 uper4; /* Device 4 port enable register */ - u8 res2[0x150]; -} __attribute__ ((packed)); - -/* SDMA */ -struct sdma { - __be32 sdsr; /* Serial DMA status register */ - __be32 sdmr; /* Serial DMA mode register */ - __be32 sdtr1; /* SDMA system bus threshold register */ - __be32 sdtr2; /* SDMA secondary bus threshold register */ - __be32 sdhy1; /* SDMA system bus hysteresis register */ - __be32 sdhy2; /* SDMA secondary bus hysteresis register */ - __be32 sdta1; /* SDMA system bus address register */ - __be32 sdta2; /* SDMA secondary bus address register */ - __be32 sdtm1; /* SDMA system bus MSNUM register */ - __be32 sdtm2; /* SDMA secondary bus MSNUM register */ - u8 res0[0x10]; - __be32 sdaqr; /* SDMA address bus qualify register */ - __be32 sdaqmr; /* SDMA address bus qualify mask register */ - u8 res1[0x4]; - __be32 sdebcr; /* SDMA CAM entries base register */ - u8 res2[0x38]; -} __attribute__ ((packed)); - -/* Debug Space */ -struct dbg { - __be32 bpdcr; /* Breakpoint debug command register */ - __be32 bpdsr; /* Breakpoint debug status register */ - __be32 bpdmr; /* Breakpoint debug mask register */ - __be32 bprmrr0; /* Breakpoint request mode risc register 0 */ - __be32 bprmrr1; /* Breakpoint request mode risc register 1 */ - u8 res0[0x8]; - __be32 bprmtr0; /* Breakpoint request mode trb register 0 */ - __be32 bprmtr1; /* Breakpoint request mode trb register 1 */ - u8 res1[0x8]; - __be32 bprmir; /* Breakpoint request mode immediate register */ - __be32 bprmsr; /* Breakpoint request mode serial register */ - __be32 bpemr; /* Breakpoint exit mode register */ - u8 res2[0x48]; -} __attribute__ ((packed)); - -/* RISC Special Registers (Trap and Breakpoint) */ -struct rsp { - u8 fixme[0x100]; -} __attribute__ ((packed)); - -struct qe_immap { - struct qe_iram iram; /* I-RAM */ - struct qe_ic_regs ic; /* Interrupt Controller */ - struct cp_qe cp; /* Communications Processor */ - struct qe_mux qmx; /* QE Multiplexer */ - struct qe_timers qet; /* QE Timers */ - struct spi spi[0x2]; /* spi */ - struct mcc mcc; /* mcc */ - struct qe_brg brg; /* brg */ - struct usb_ctlr usb; /* USB */ - struct si1 si1; /* SI */ - u8 res11[0x800]; - struct sir sir; /* SI Routing Tables */ - struct ucc ucc1; /* ucc1 */ - struct ucc ucc3; /* ucc3 */ - struct ucc ucc5; /* ucc5 */ - struct ucc ucc7; /* ucc7 */ - u8 res12[0x600]; - struct upc upc1; /* MultiPHY UTOPIA POS Ctrlr 1*/ - struct ucc ucc2; /* ucc2 */ - struct ucc ucc4; /* ucc4 */ - struct ucc ucc6; /* ucc6 */ - struct ucc ucc8; /* ucc8 */ - u8 res13[0x600]; - struct upc upc2; /* MultiPHY UTOPIA POS Ctrlr 2*/ - struct sdma sdma; /* SDMA */ - struct dbg dbg; /* Debug Space */ - struct rsp rsp[0x2]; /* RISC Special Registers - (Trap and Breakpoint) */ - u8 res14[0x300]; - u8 res15[0x3A00]; - u8 res16[0x8000]; /* 0x108000 - 0x110000 */ - u8 muram[0xC000]; /* 0x110000 - 0x11C000 - Multi-user RAM */ - u8 res17[0x24000]; /* 0x11C000 - 0x140000 */ - u8 res18[0xC0000]; /* 0x140000 - 0x200000 */ -} __attribute__ ((packed)); - -extern struct qe_immap *qe_immr; -extern phys_addr_t get_qe_base(void); - -static inline unsigned long immrbar_virt_to_phys(volatile void * address) -{ - if ( ((u32)address >= (u32)qe_immr) && - ((u32)address < ((u32)qe_immr + QE_IMMAP_SIZE)) ) - return (unsigned long)(address - (u32)qe_immr + - (u32)get_qe_base()); - return (unsigned long)virt_to_phys(address); -} - -#endif /* __KERNEL__ */ -#endif /* _ASM_POWERPC_IMMAP_QE_H */ diff --git a/trunk/include/asm-powerpc/irq.h b/trunk/include/asm-powerpc/irq.h index 4da41efb1319..89ed545b446b 100644 --- a/trunk/include/asm-powerpc/irq.h +++ b/trunk/include/asm-powerpc/irq.h @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/trunk/include/asm-powerpc/pci-bridge.h b/trunk/include/asm-powerpc/pci-bridge.h index 4f55573762bb..86ee46b09b8a 100644 --- a/trunk/include/asm-powerpc/pci-bridge.h +++ b/trunk/include/asm-powerpc/pci-bridge.h @@ -6,7 +6,6 @@ #include #else -#include #include #include diff --git a/trunk/include/asm-powerpc/qe.h b/trunk/include/asm-powerpc/qe.h deleted file mode 100644 index a62168ec535f..000000000000 --- a/trunk/include/asm-powerpc/qe.h +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QUICC Engine (QE) external definitions and structure. - * - * 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. - */ -#ifndef _ASM_POWERPC_QE_H -#define _ASM_POWERPC_QE_H -#ifdef __KERNEL__ - -#include - -#define QE_NUM_OF_SNUM 28 -#define QE_NUM_OF_BRGS 16 -#define QE_NUM_OF_PORTS 1024 - -/* Memory partitions -*/ -#define MEM_PART_SYSTEM 0 -#define MEM_PART_SECONDARY 1 -#define MEM_PART_MURAM 2 - -/* Export QE common operations */ -extern void qe_reset(void); -extern int par_io_init(struct device_node *np); -extern int par_io_of_config(struct device_node *np); - -/* QE internal API */ -int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input); -void qe_setbrg(u32 brg, u32 rate); -int qe_get_snum(void); -void qe_put_snum(u8 snum); -u32 qe_muram_alloc(u32 size, u32 align); -int qe_muram_free(u32 offset); -u32 qe_muram_alloc_fixed(u32 offset, u32 size); -void qe_muram_dump(void); -void *qe_muram_addr(u32 offset); - -/* Buffer descriptors */ -struct qe_bd { - u16 status; - u16 length; - u32 buf; -} __attribute__ ((packed)); - -#define BD_STATUS_MASK 0xffff0000 -#define BD_LENGTH_MASK 0x0000ffff - -/* Alignment */ -#define QE_INTR_TABLE_ALIGN 16 /* ??? */ -#define QE_ALIGNMENT_OF_BD 8 -#define QE_ALIGNMENT_OF_PRAM 64 - -/* RISC allocation */ -enum qe_risc_allocation { - QE_RISC_ALLOCATION_RISC1 = 1, /* RISC 1 */ - QE_RISC_ALLOCATION_RISC2 = 2, /* RISC 2 */ - QE_RISC_ALLOCATION_RISC1_AND_RISC2 = 3 /* Dynamically choose - RISC 1 or RISC 2 */ -}; - -/* QE extended filtering Table Lookup Key Size */ -enum qe_fltr_tbl_lookup_key_size { - QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES - = 0x3f, /* LookupKey parsed by the Generate LookupKey - CMD is truncated to 8 bytes */ - QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES - = 0x5f, /* LookupKey parsed by the Generate LookupKey - CMD is truncated to 16 bytes */ -}; - -/* QE FLTR extended filtering Largest External Table Lookup Key Size */ -enum qe_fltr_largest_external_tbl_lookup_key_size { - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE - = 0x0,/* not used */ - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES - = QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES, /* 8 bytes */ - QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES - = QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES, /* 16 bytes */ -}; - -/* structure representing QE parameter RAM */ -struct qe_timer_tables { - u16 tm_base; /* QE timer table base adr */ - u16 tm_ptr; /* QE timer table pointer */ - u16 r_tmr; /* QE timer mode register */ - u16 r_tmv; /* QE timer valid register */ - u32 tm_cmd; /* QE timer cmd register */ - u32 tm_cnt; /* QE timer internal cnt */ -} __attribute__ ((packed)); - -#define QE_FLTR_TAD_SIZE 8 - -/* QE extended filtering Termination Action Descriptor (TAD) */ -struct qe_fltr_tad { - u8 serialized[QE_FLTR_TAD_SIZE]; -} __attribute__ ((packed)); - -/* Communication Direction */ -enum comm_dir { - COMM_DIR_NONE = 0, - COMM_DIR_RX = 1, - COMM_DIR_TX = 2, - COMM_DIR_RX_AND_TX = 3 -}; - -/* Clocks and BRGs */ -enum qe_clock { - QE_CLK_NONE = 0, - QE_BRG1, /* Baud Rate Generator 1 */ - QE_BRG2, /* Baud Rate Generator 2 */ - QE_BRG3, /* Baud Rate Generator 3 */ - QE_BRG4, /* Baud Rate Generator 4 */ - QE_BRG5, /* Baud Rate Generator 5 */ - QE_BRG6, /* Baud Rate Generator 6 */ - QE_BRG7, /* Baud Rate Generator 7 */ - QE_BRG8, /* Baud Rate Generator 8 */ - QE_BRG9, /* Baud Rate Generator 9 */ - QE_BRG10, /* Baud Rate Generator 10 */ - QE_BRG11, /* Baud Rate Generator 11 */ - QE_BRG12, /* Baud Rate Generator 12 */ - QE_BRG13, /* Baud Rate Generator 13 */ - QE_BRG14, /* Baud Rate Generator 14 */ - QE_BRG15, /* Baud Rate Generator 15 */ - QE_BRG16, /* Baud Rate Generator 16 */ - QE_CLK1, /* Clock 1 */ - QE_CLK2, /* Clock 2 */ - QE_CLK3, /* Clock 3 */ - QE_CLK4, /* Clock 4 */ - QE_CLK5, /* Clock 5 */ - QE_CLK6, /* Clock 6 */ - QE_CLK7, /* Clock 7 */ - QE_CLK8, /* Clock 8 */ - QE_CLK9, /* Clock 9 */ - QE_CLK10, /* Clock 10 */ - QE_CLK11, /* Clock 11 */ - QE_CLK12, /* Clock 12 */ - QE_CLK13, /* Clock 13 */ - QE_CLK14, /* Clock 14 */ - QE_CLK15, /* Clock 15 */ - QE_CLK16, /* Clock 16 */ - QE_CLK17, /* Clock 17 */ - QE_CLK18, /* Clock 18 */ - QE_CLK19, /* Clock 19 */ - QE_CLK20, /* Clock 20 */ - QE_CLK21, /* Clock 21 */ - QE_CLK22, /* Clock 22 */ - QE_CLK23, /* Clock 23 */ - QE_CLK24, /* Clock 24 */ - QE_CLK_DUMMY, -}; - -/* QE CMXUCR Registers. - * There are two UCCs represented in each of the four CMXUCR registers. - * These values are for the UCC in the LSBs - */ -#define QE_CMXUCR_MII_ENET_MNG 0x00007000 -#define QE_CMXUCR_MII_ENET_MNG_SHIFT 12 -#define QE_CMXUCR_GRANT 0x00008000 -#define QE_CMXUCR_TSA 0x00004000 -#define QE_CMXUCR_BKPT 0x00000100 -#define QE_CMXUCR_TX_CLK_SRC_MASK 0x0000000F - -/* QE CMXGCR Registers. -*/ -#define QE_CMXGCR_MII_ENET_MNG 0x00007000 -#define QE_CMXGCR_MII_ENET_MNG_SHIFT 12 -#define QE_CMXGCR_USBCS 0x0000000f - -/* QE CECR Commands. -*/ -#define QE_CR_FLG 0x00010000 -#define QE_RESET 0x80000000 -#define QE_INIT_TX_RX 0x00000000 -#define QE_INIT_RX 0x00000001 -#define QE_INIT_TX 0x00000002 -#define QE_ENTER_HUNT_MODE 0x00000003 -#define QE_STOP_TX 0x00000004 -#define QE_GRACEFUL_STOP_TX 0x00000005 -#define QE_RESTART_TX 0x00000006 -#define QE_CLOSE_RX_BD 0x00000007 -#define QE_SWITCH_COMMAND 0x00000007 -#define QE_SET_GROUP_ADDRESS 0x00000008 -#define QE_START_IDMA 0x00000009 -#define QE_MCC_STOP_RX 0x00000009 -#define QE_ATM_TRANSMIT 0x0000000a -#define QE_HPAC_CLEAR_ALL 0x0000000b -#define QE_GRACEFUL_STOP_RX 0x0000001a -#define QE_RESTART_RX 0x0000001b -#define QE_HPAC_SET_PRIORITY 0x0000010b -#define QE_HPAC_STOP_TX 0x0000020b -#define QE_HPAC_STOP_RX 0x0000030b -#define QE_HPAC_GRACEFUL_STOP_TX 0x0000040b -#define QE_HPAC_GRACEFUL_STOP_RX 0x0000050b -#define QE_HPAC_START_TX 0x0000060b -#define QE_HPAC_START_RX 0x0000070b -#define QE_USB_STOP_TX 0x0000000a -#define QE_USB_RESTART_TX 0x0000000b -#define QE_QMC_STOP_TX 0x0000000c -#define QE_QMC_STOP_RX 0x0000000d -#define QE_SS7_SU_FIL_RESET 0x0000000e -/* jonathbr added from here down for 83xx */ -#define QE_RESET_BCS 0x0000000a -#define QE_MCC_INIT_TX_RX_16 0x00000003 -#define QE_MCC_STOP_TX 0x00000004 -#define QE_MCC_INIT_TX_1 0x00000005 -#define QE_MCC_INIT_RX_1 0x00000006 -#define QE_MCC_RESET 0x00000007 -#define QE_SET_TIMER 0x00000008 -#define QE_RANDOM_NUMBER 0x0000000c -#define QE_ATM_MULTI_THREAD_INIT 0x00000011 -#define QE_ASSIGN_PAGE 0x00000012 -#define QE_ADD_REMOVE_HASH_ENTRY 0x00000013 -#define QE_START_FLOW_CONTROL 0x00000014 -#define QE_STOP_FLOW_CONTROL 0x00000015 -#define QE_ASSIGN_PAGE_TO_DEVICE 0x00000016 - -#define QE_ASSIGN_RISC 0x00000010 -#define QE_CR_MCN_NORMAL_SHIFT 6 -#define QE_CR_MCN_USB_SHIFT 4 -#define QE_CR_MCN_RISC_ASSIGN_SHIFT 8 -#define QE_CR_SNUM_SHIFT 17 - -/* QE CECR Sub Block - sub block of QE command. -*/ -#define QE_CR_SUBBLOCK_INVALID 0x00000000 -#define QE_CR_SUBBLOCK_USB 0x03200000 -#define QE_CR_SUBBLOCK_UCCFAST1 0x02000000 -#define QE_CR_SUBBLOCK_UCCFAST2 0x02200000 -#define QE_CR_SUBBLOCK_UCCFAST3 0x02400000 -#define QE_CR_SUBBLOCK_UCCFAST4 0x02600000 -#define QE_CR_SUBBLOCK_UCCFAST5 0x02800000 -#define QE_CR_SUBBLOCK_UCCFAST6 0x02a00000 -#define QE_CR_SUBBLOCK_UCCFAST7 0x02c00000 -#define QE_CR_SUBBLOCK_UCCFAST8 0x02e00000 -#define QE_CR_SUBBLOCK_UCCSLOW1 0x00000000 -#define QE_CR_SUBBLOCK_UCCSLOW2 0x00200000 -#define QE_CR_SUBBLOCK_UCCSLOW3 0x00400000 -#define QE_CR_SUBBLOCK_UCCSLOW4 0x00600000 -#define QE_CR_SUBBLOCK_UCCSLOW5 0x00800000 -#define QE_CR_SUBBLOCK_UCCSLOW6 0x00a00000 -#define QE_CR_SUBBLOCK_UCCSLOW7 0x00c00000 -#define QE_CR_SUBBLOCK_UCCSLOW8 0x00e00000 -#define QE_CR_SUBBLOCK_MCC1 0x03800000 -#define QE_CR_SUBBLOCK_MCC2 0x03a00000 -#define QE_CR_SUBBLOCK_MCC3 0x03000000 -#define QE_CR_SUBBLOCK_IDMA1 0x02800000 -#define QE_CR_SUBBLOCK_IDMA2 0x02a00000 -#define QE_CR_SUBBLOCK_IDMA3 0x02c00000 -#define QE_CR_SUBBLOCK_IDMA4 0x02e00000 -#define QE_CR_SUBBLOCK_HPAC 0x01e00000 -#define QE_CR_SUBBLOCK_SPI1 0x01400000 -#define QE_CR_SUBBLOCK_SPI2 0x01600000 -#define QE_CR_SUBBLOCK_RAND 0x01c00000 -#define QE_CR_SUBBLOCK_TIMER 0x01e00000 -#define QE_CR_SUBBLOCK_GENERAL 0x03c00000 - -/* QE CECR Protocol - For non-MCC, specifies mode for QE CECR command */ -#define QE_CR_PROTOCOL_UNSPECIFIED 0x00 /* For all other protocols */ -#define QE_CR_PROTOCOL_HDLC_TRANSPARENT 0x00 -#define QE_CR_PROTOCOL_ATM_POS 0x0A -#define QE_CR_PROTOCOL_ETHERNET 0x0C -#define QE_CR_PROTOCOL_L2_SWITCH 0x0D - -/* BMR byte order */ -#define QE_BMR_BYTE_ORDER_BO_PPC 0x08 /* powerpc little endian */ -#define QE_BMR_BYTE_ORDER_BO_MOT 0x10 /* motorola big endian */ -#define QE_BMR_BYTE_ORDER_BO_MAX 0x18 - -/* BRG configuration register */ -#define QE_BRGC_ENABLE 0x00010000 -#define QE_BRGC_DIVISOR_SHIFT 1 -#define QE_BRGC_DIVISOR_MAX 0xFFF -#define QE_BRGC_DIV16 1 - -/* QE Timers registers */ -#define QE_GTCFR1_PCAS 0x80 -#define QE_GTCFR1_STP2 0x20 -#define QE_GTCFR1_RST2 0x10 -#define QE_GTCFR1_GM2 0x08 -#define QE_GTCFR1_GM1 0x04 -#define QE_GTCFR1_STP1 0x02 -#define QE_GTCFR1_RST1 0x01 - -/* SDMA registers */ -#define QE_SDSR_BER1 0x02000000 -#define QE_SDSR_BER2 0x01000000 - -#define QE_SDMR_GLB_1_MSK 0x80000000 -#define QE_SDMR_ADR_SEL 0x20000000 -#define QE_SDMR_BER1_MSK 0x02000000 -#define QE_SDMR_BER2_MSK 0x01000000 -#define QE_SDMR_EB1_MSK 0x00800000 -#define QE_SDMR_ER1_MSK 0x00080000 -#define QE_SDMR_ER2_MSK 0x00040000 -#define QE_SDMR_CEN_MASK 0x0000E000 -#define QE_SDMR_SBER_1 0x00000200 -#define QE_SDMR_SBER_2 0x00000200 -#define QE_SDMR_EB1_PR_MASK 0x000000C0 -#define QE_SDMR_ER1_PR 0x00000008 - -#define QE_SDMR_CEN_SHIFT 13 -#define QE_SDMR_EB1_PR_SHIFT 6 - -#define QE_SDTM_MSNUM_SHIFT 24 - -#define QE_SDEBCR_BA_MASK 0x01FFFFFF - -/* UPC */ -#define UPGCR_PROTOCOL 0x80000000 /* protocol ul2 or pl2 */ -#define UPGCR_TMS 0x40000000 /* Transmit master/slave mode */ -#define UPGCR_RMS 0x20000000 /* Receive master/slave mode */ -#define UPGCR_ADDR 0x10000000 /* Master MPHY Addr multiplexing */ -#define UPGCR_DIAG 0x01000000 /* Diagnostic mode */ - -/* UCC */ -#define UCC_GUEMR_MODE_MASK_RX 0x02 -#define UCC_GUEMR_MODE_MASK_TX 0x01 -#define UCC_GUEMR_MODE_FAST_RX 0x02 -#define UCC_GUEMR_MODE_FAST_TX 0x01 -#define UCC_GUEMR_MODE_SLOW_RX 0x00 -#define UCC_GUEMR_MODE_SLOW_TX 0x00 -#define UCC_GUEMR_SET_RESERVED3 0x10 /* Bit 3 in the guemr is reserved but - must be set 1 */ - -/* structure representing UCC SLOW parameter RAM */ -struct ucc_slow_pram { - u16 rbase; /* RX BD base address */ - u16 tbase; /* TX BD base address */ - u8 rfcr; /* Rx function code */ - u8 tfcr; /* Tx function code */ - u16 mrblr; /* Rx buffer length */ - u32 rstate; /* Rx internal state */ - u32 rptr; /* Rx internal data pointer */ - u16 rbptr; /* rb BD Pointer */ - u16 rcount; /* Rx internal byte count */ - u32 rtemp; /* Rx temp */ - u32 tstate; /* Tx internal state */ - u32 tptr; /* Tx internal data pointer */ - u16 tbptr; /* Tx BD pointer */ - u16 tcount; /* Tx byte count */ - u32 ttemp; /* Tx temp */ - u32 rcrc; /* temp receive CRC */ - u32 tcrc; /* temp transmit CRC */ -} __attribute__ ((packed)); - -/* General UCC SLOW Mode Register (GUMRH & GUMRL) */ -#define UCC_SLOW_GUMR_H_CRC16 0x00004000 -#define UCC_SLOW_GUMR_H_CRC16CCITT 0x00000000 -#define UCC_SLOW_GUMR_H_CRC32CCITT 0x00008000 -#define UCC_SLOW_GUMR_H_REVD 0x00002000 -#define UCC_SLOW_GUMR_H_TRX 0x00001000 -#define UCC_SLOW_GUMR_H_TTX 0x00000800 -#define UCC_SLOW_GUMR_H_CDP 0x00000400 -#define UCC_SLOW_GUMR_H_CTSP 0x00000200 -#define UCC_SLOW_GUMR_H_CDS 0x00000100 -#define UCC_SLOW_GUMR_H_CTSS 0x00000080 -#define UCC_SLOW_GUMR_H_TFL 0x00000040 -#define UCC_SLOW_GUMR_H_RFW 0x00000020 -#define UCC_SLOW_GUMR_H_TXSY 0x00000010 -#define UCC_SLOW_GUMR_H_4SYNC 0x00000004 -#define UCC_SLOW_GUMR_H_8SYNC 0x00000008 -#define UCC_SLOW_GUMR_H_16SYNC 0x0000000c -#define UCC_SLOW_GUMR_H_RTSM 0x00000002 -#define UCC_SLOW_GUMR_H_RSYN 0x00000001 - -#define UCC_SLOW_GUMR_L_TCI 0x10000000 -#define UCC_SLOW_GUMR_L_RINV 0x02000000 -#define UCC_SLOW_GUMR_L_TINV 0x01000000 -#define UCC_SLOW_GUMR_L_TEND 0x00020000 -#define UCC_SLOW_GUMR_L_ENR 0x00000020 -#define UCC_SLOW_GUMR_L_ENT 0x00000010 - -/* General UCC FAST Mode Register */ -#define UCC_FAST_GUMR_TCI 0x20000000 -#define UCC_FAST_GUMR_TRX 0x10000000 -#define UCC_FAST_GUMR_TTX 0x08000000 -#define UCC_FAST_GUMR_CDP 0x04000000 -#define UCC_FAST_GUMR_CTSP 0x02000000 -#define UCC_FAST_GUMR_CDS 0x01000000 -#define UCC_FAST_GUMR_CTSS 0x00800000 -#define UCC_FAST_GUMR_TXSY 0x00020000 -#define UCC_FAST_GUMR_RSYN 0x00010000 -#define UCC_FAST_GUMR_RTSM 0x00002000 -#define UCC_FAST_GUMR_REVD 0x00000400 -#define UCC_FAST_GUMR_ENR 0x00000020 -#define UCC_FAST_GUMR_ENT 0x00000010 - -/* Slow UCC Event Register (UCCE) */ -#define UCC_SLOW_UCCE_GLR 0x1000 -#define UCC_SLOW_UCCE_GLT 0x0800 -#define UCC_SLOW_UCCE_DCC 0x0400 -#define UCC_SLOW_UCCE_FLG 0x0200 -#define UCC_SLOW_UCCE_AB 0x0200 -#define UCC_SLOW_UCCE_IDLE 0x0100 -#define UCC_SLOW_UCCE_GRA 0x0080 -#define UCC_SLOW_UCCE_TXE 0x0010 -#define UCC_SLOW_UCCE_RXF 0x0008 -#define UCC_SLOW_UCCE_CCR 0x0008 -#define UCC_SLOW_UCCE_RCH 0x0008 -#define UCC_SLOW_UCCE_BSY 0x0004 -#define UCC_SLOW_UCCE_TXB 0x0002 -#define UCC_SLOW_UCCE_TX 0x0002 -#define UCC_SLOW_UCCE_RX 0x0001 -#define UCC_SLOW_UCCE_GOV 0x0001 -#define UCC_SLOW_UCCE_GUN 0x0002 -#define UCC_SLOW_UCCE_GINT 0x0004 -#define UCC_SLOW_UCCE_IQOV 0x0008 - -#define UCC_SLOW_UCCE_HDLC_SET (UCC_SLOW_UCCE_TXE | UCC_SLOW_UCCE_BSY | \ - UCC_SLOW_UCCE_GRA | UCC_SLOW_UCCE_TXB | UCC_SLOW_UCCE_RXF | \ - UCC_SLOW_UCCE_DCC | UCC_SLOW_UCCE_GLT | UCC_SLOW_UCCE_GLR) -#define UCC_SLOW_UCCE_ENET_SET (UCC_SLOW_UCCE_TXE | UCC_SLOW_UCCE_BSY | \ - UCC_SLOW_UCCE_GRA | UCC_SLOW_UCCE_TXB | UCC_SLOW_UCCE_RXF) -#define UCC_SLOW_UCCE_TRANS_SET (UCC_SLOW_UCCE_TXE | UCC_SLOW_UCCE_BSY | \ - UCC_SLOW_UCCE_GRA | UCC_SLOW_UCCE_TX | UCC_SLOW_UCCE_RX | \ - UCC_SLOW_UCCE_DCC | UCC_SLOW_UCCE_GLT | UCC_SLOW_UCCE_GLR) -#define UCC_SLOW_UCCE_UART_SET (UCC_SLOW_UCCE_BSY | UCC_SLOW_UCCE_GRA | \ - UCC_SLOW_UCCE_TXB | UCC_SLOW_UCCE_TX | UCC_SLOW_UCCE_RX | \ - UCC_SLOW_UCCE_GLT | UCC_SLOW_UCCE_GLR) -#define UCC_SLOW_UCCE_QMC_SET (UCC_SLOW_UCCE_IQOV | UCC_SLOW_UCCE_GINT | \ - UCC_SLOW_UCCE_GUN | UCC_SLOW_UCCE_GOV) - -#define UCC_SLOW_UCCE_OTHER (UCC_SLOW_UCCE_TXE | UCC_SLOW_UCCE_BSY | \ - UCC_SLOW_UCCE_GRA | UCC_SLOW_UCCE_DCC | UCC_SLOW_UCCE_GLT | \ - UCC_SLOW_UCCE_GLR) - -#define UCC_SLOW_INTR_TX UCC_SLOW_UCCE_TXB -#define UCC_SLOW_INTR_RX (UCC_SLOW_UCCE_RXF | UCC_SLOW_UCCE_RX) -#define UCC_SLOW_INTR (UCC_SLOW_INTR_TX | UCC_SLOW_INTR_RX) - -/* UCC Transmit On Demand Register (UTODR) */ -#define UCC_SLOW_TOD 0x8000 -#define UCC_FAST_TOD 0x8000 - -/* Function code masks */ -#define FC_GBL 0x20 -#define FC_DTB_LCL 0x02 -#define UCC_FAST_FUNCTION_CODE_GBL 0x20 -#define UCC_FAST_FUNCTION_CODE_DTB_LCL 0x02 -#define UCC_FAST_FUNCTION_CODE_BDB_LCL 0x01 - -static inline long IS_MURAM_ERR(const u32 offset) -{ - return offset > (u32) - 1000L; -} - -#endif /* __KERNEL__ */ -#endif /* _ASM_POWERPC_QE_H */ diff --git a/trunk/include/asm-powerpc/qe_ic.h b/trunk/include/asm-powerpc/qe_ic.h deleted file mode 100644 index e386fb7e44b0..000000000000 --- a/trunk/include/asm-powerpc/qe_ic.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * include/asm-powerpc/qe_ic.h - * - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * QE IC external definitions and structure. - * - * 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. - */ -#ifndef _ASM_POWERPC_QE_IC_H -#define _ASM_POWERPC_QE_IC_H - -#include - -#define NUM_OF_QE_IC_GROUPS 6 - -/* Flags when we init the QE IC */ -#define QE_IC_SPREADMODE_GRP_W 0x00000001 -#define QE_IC_SPREADMODE_GRP_X 0x00000002 -#define QE_IC_SPREADMODE_GRP_Y 0x00000004 -#define QE_IC_SPREADMODE_GRP_Z 0x00000008 -#define QE_IC_SPREADMODE_GRP_RISCA 0x00000010 -#define QE_IC_SPREADMODE_GRP_RISCB 0x00000020 - -#define QE_IC_LOW_SIGNAL 0x00000100 -#define QE_IC_HIGH_SIGNAL 0x00000200 - -#define QE_IC_GRP_W_PRI0_DEST_SIGNAL_HIGH 0x00001000 -#define QE_IC_GRP_W_PRI1_DEST_SIGNAL_HIGH 0x00002000 -#define QE_IC_GRP_X_PRI0_DEST_SIGNAL_HIGH 0x00004000 -#define QE_IC_GRP_X_PRI1_DEST_SIGNAL_HIGH 0x00008000 -#define QE_IC_GRP_Y_PRI0_DEST_SIGNAL_HIGH 0x00010000 -#define QE_IC_GRP_Y_PRI1_DEST_SIGNAL_HIGH 0x00020000 -#define QE_IC_GRP_Z_PRI0_DEST_SIGNAL_HIGH 0x00040000 -#define QE_IC_GRP_Z_PRI1_DEST_SIGNAL_HIGH 0x00080000 -#define QE_IC_GRP_RISCA_PRI0_DEST_SIGNAL_HIGH 0x00100000 -#define QE_IC_GRP_RISCA_PRI1_DEST_SIGNAL_HIGH 0x00200000 -#define QE_IC_GRP_RISCB_PRI0_DEST_SIGNAL_HIGH 0x00400000 -#define QE_IC_GRP_RISCB_PRI1_DEST_SIGNAL_HIGH 0x00800000 -#define QE_IC_GRP_W_DEST_SIGNAL_SHIFT (12) - -/* QE interrupt sources groups */ -enum qe_ic_grp_id { - QE_IC_GRP_W = 0, /* QE interrupt controller group W */ - QE_IC_GRP_X, /* QE interrupt controller group X */ - QE_IC_GRP_Y, /* QE interrupt controller group Y */ - QE_IC_GRP_Z, /* QE interrupt controller group Z */ - QE_IC_GRP_RISCA, /* QE interrupt controller RISC group A */ - QE_IC_GRP_RISCB /* QE interrupt controller RISC group B */ -}; - -void qe_ic_init(struct device_node *node, unsigned int flags); -void qe_ic_set_highest_priority(unsigned int virq, int high); -int qe_ic_set_priority(unsigned int virq, unsigned int priority); -int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high); - -#endif /* _ASM_POWERPC_QE_IC_H */ diff --git a/trunk/include/asm-powerpc/system.h b/trunk/include/asm-powerpc/system.h index 43627596003b..4b41deaa8d8d 100644 --- a/trunk/include/asm-powerpc/system.h +++ b/trunk/include/asm-powerpc/system.h @@ -91,6 +91,10 @@ DEBUGGER_BOILERPLATE(debugger_iabr_match) DEBUGGER_BOILERPLATE(debugger_dabr_match) DEBUGGER_BOILERPLATE(debugger_fault_handler) +#ifdef CONFIG_XMON +extern void xmon_init(int enable); +#endif + #else static inline int debugger(struct pt_regs *regs) { return 0; } static inline int debugger_ipi(struct pt_regs *regs) { return 0; } diff --git a/trunk/include/asm-powerpc/ucc.h b/trunk/include/asm-powerpc/ucc.h deleted file mode 100644 index afe3076bdc03..000000000000 --- a/trunk/include/asm-powerpc/ucc.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * Internal header file for UCC unit 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. - */ -#ifndef __UCC_H__ -#define __UCC_H__ - -#include -#include - -#define STATISTICS - -#define UCC_MAX_NUM 8 - -/* Slow or fast type for UCCs. -*/ -enum ucc_speed_type { - UCC_SPEED_TYPE_FAST, UCC_SPEED_TYPE_SLOW -}; - -/* Initial UCCs Parameter RAM address relative to: MEM_MAP_BASE (IMMR). -*/ -enum ucc_pram_initial_offset { - UCC_PRAM_OFFSET_UCC1 = 0x8400, - UCC_PRAM_OFFSET_UCC2 = 0x8500, - UCC_PRAM_OFFSET_UCC3 = 0x8600, - UCC_PRAM_OFFSET_UCC4 = 0x9000, - UCC_PRAM_OFFSET_UCC5 = 0x8000, - UCC_PRAM_OFFSET_UCC6 = 0x8100, - UCC_PRAM_OFFSET_UCC7 = 0x8200, - UCC_PRAM_OFFSET_UCC8 = 0x8300 -}; - -/* ucc_set_type - * Sets UCC to slow or fast mode. - * - * ucc_num - (In) number of UCC (0-7). - * regs - (In) pointer to registers base for the UCC. - * speed - (In) slow or fast mode for UCC. - */ -int ucc_set_type(int ucc_num, struct ucc_common *regs, - enum ucc_speed_type speed); - -/* ucc_init_guemr - * Init the Guemr register. - * - * regs - (In) pointer to registers base for the UCC. - */ -int ucc_init_guemr(struct ucc_common *regs); - -int ucc_set_qe_mux_mii_mng(int ucc_num); - -int ucc_set_qe_mux_rxtx(int ucc_num, enum qe_clock clock, enum comm_dir mode); - -int ucc_mux_set_grant_tsa_bkpt(int ucc_num, int set, u32 mask); - -/* QE MUX clock routing for UCC -*/ -static inline int ucc_set_qe_mux_grant(int ucc_num, int set) -{ - return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_GRANT); -} - -static inline int ucc_set_qe_mux_tsa(int ucc_num, int set) -{ - return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_TSA); -} - -static inline int ucc_set_qe_mux_bkpt(int ucc_num, int set) -{ - return ucc_mux_set_grant_tsa_bkpt(ucc_num, set, QE_CMXUCR_BKPT); -} - -#endif /* __UCC_H__ */ diff --git a/trunk/include/asm-powerpc/ucc_fast.h b/trunk/include/asm-powerpc/ucc_fast.h deleted file mode 100644 index 39d1c90fd2ca..000000000000 --- a/trunk/include/asm-powerpc/ucc_fast.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * include/asm-powerpc/ucc_fast.h - * - * Internal header file for UCC FAST unit routines. - * - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * 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. - */ -#ifndef __UCC_FAST_H__ -#define __UCC_FAST_H__ - -#include - -#include -#include - -#include "ucc.h" - -/* Receive BD's status */ -#define R_E 0x80000000 /* buffer empty */ -#define R_W 0x20000000 /* wrap bit */ -#define R_I 0x10000000 /* interrupt on reception */ -#define R_L 0x08000000 /* last */ -#define R_F 0x04000000 /* first */ - -/* transmit BD's status */ -#define T_R 0x80000000 /* ready bit */ -#define T_W 0x20000000 /* wrap bit */ -#define T_I 0x10000000 /* interrupt on completion */ -#define T_L 0x08000000 /* last */ - -/* Rx Data buffer must be 4 bytes aligned in most cases */ -#define UCC_FAST_RX_ALIGN 4 -#define UCC_FAST_MRBLR_ALIGNMENT 4 -#define UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT 8 - -/* Sizes */ -#define UCC_FAST_URFS_MIN_VAL 0x88 -#define UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR 8 - -/* ucc_fast_channel_protocol_mode - UCC FAST mode */ -enum ucc_fast_channel_protocol_mode { - UCC_FAST_PROTOCOL_MODE_HDLC = 0x00000000, - UCC_FAST_PROTOCOL_MODE_RESERVED01 = 0x00000001, - UCC_FAST_PROTOCOL_MODE_RESERVED_QMC = 0x00000002, - UCC_FAST_PROTOCOL_MODE_RESERVED02 = 0x00000003, - UCC_FAST_PROTOCOL_MODE_RESERVED_UART = 0x00000004, - UCC_FAST_PROTOCOL_MODE_RESERVED03 = 0x00000005, - UCC_FAST_PROTOCOL_MODE_RESERVED_EX_MAC_1 = 0x00000006, - UCC_FAST_PROTOCOL_MODE_RESERVED_EX_MAC_2 = 0x00000007, - UCC_FAST_PROTOCOL_MODE_RESERVED_BISYNC = 0x00000008, - UCC_FAST_PROTOCOL_MODE_RESERVED04 = 0x00000009, - UCC_FAST_PROTOCOL_MODE_ATM = 0x0000000A, - UCC_FAST_PROTOCOL_MODE_RESERVED05 = 0x0000000B, - UCC_FAST_PROTOCOL_MODE_ETHERNET = 0x0000000C, - UCC_FAST_PROTOCOL_MODE_RESERVED06 = 0x0000000D, - UCC_FAST_PROTOCOL_MODE_POS = 0x0000000E, - UCC_FAST_PROTOCOL_MODE_RESERVED07 = 0x0000000F -}; - -/* ucc_fast_transparent_txrx - UCC Fast Transparent TX & RX */ -enum ucc_fast_transparent_txrx { - UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL = 0x00000000, - UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_TRANSPARENT = 0x18000000 -}; - -/* UCC fast diagnostic mode */ -enum ucc_fast_diag_mode { - UCC_FAST_DIAGNOSTIC_NORMAL = 0x0, - UCC_FAST_DIAGNOSTIC_LOCAL_LOOP_BACK = 0x40000000, - UCC_FAST_DIAGNOSTIC_AUTO_ECHO = 0x80000000, - UCC_FAST_DIAGNOSTIC_LOOP_BACK_AND_ECHO = 0xC0000000 -}; - -/* UCC fast Sync length (transparent mode only) */ -enum ucc_fast_sync_len { - UCC_FAST_SYNC_LEN_NOT_USED = 0x0, - UCC_FAST_SYNC_LEN_AUTOMATIC = 0x00004000, - UCC_FAST_SYNC_LEN_8_BIT = 0x00008000, - UCC_FAST_SYNC_LEN_16_BIT = 0x0000C000 -}; - -/* UCC fast RTS mode */ -enum ucc_fast_ready_to_send { - UCC_FAST_SEND_IDLES_BETWEEN_FRAMES = 0x00000000, - UCC_FAST_SEND_FLAGS_BETWEEN_FRAMES = 0x00002000 -}; - -/* UCC fast receiver decoding mode */ -enum ucc_fast_rx_decoding_method { - UCC_FAST_RX_ENCODING_NRZ = 0x00000000, - UCC_FAST_RX_ENCODING_NRZI = 0x00000800, - UCC_FAST_RX_ENCODING_RESERVED0 = 0x00001000, - UCC_FAST_RX_ENCODING_RESERVED1 = 0x00001800 -}; - -/* UCC fast transmitter encoding mode */ -enum ucc_fast_tx_encoding_method { - UCC_FAST_TX_ENCODING_NRZ = 0x00000000, - UCC_FAST_TX_ENCODING_NRZI = 0x00000100, - UCC_FAST_TX_ENCODING_RESERVED0 = 0x00000200, - UCC_FAST_TX_ENCODING_RESERVED1 = 0x00000300 -}; - -/* UCC fast CRC length */ -enum ucc_fast_transparent_tcrc { - UCC_FAST_16_BIT_CRC = 0x00000000, - UCC_FAST_CRC_RESERVED0 = 0x00000040, - UCC_FAST_32_BIT_CRC = 0x00000080, - UCC_FAST_CRC_RESERVED1 = 0x000000C0 -}; - -/* Fast UCC initialization structure */ -struct ucc_fast_info { - int ucc_num; - enum qe_clock rx_clock; - enum qe_clock tx_clock; - u32 regs; - int irq; - u32 uccm_mask; - int bd_mem_part; - int brkpt_support; - int grant_support; - int tsa; - int cdp; - int cds; - int ctsp; - int ctss; - int tci; - int txsy; - int rtsm; - int revd; - int rsyn; - u16 max_rx_buf_length; - u16 urfs; - u16 urfet; - u16 urfset; - u16 utfs; - u16 utfet; - u16 utftt; - u16 ufpt; - enum ucc_fast_channel_protocol_mode mode; - enum ucc_fast_transparent_txrx ttx_trx; - enum ucc_fast_tx_encoding_method tenc; - enum ucc_fast_rx_decoding_method renc; - enum ucc_fast_transparent_tcrc tcrc; - enum ucc_fast_sync_len synl; -}; - -struct ucc_fast_private { - struct ucc_fast_info *uf_info; - struct ucc_fast *uf_regs; /* a pointer to memory map of UCC regs. */ - u32 *p_ucce; /* a pointer to the event register in memory. */ - u32 *p_uccm; /* a pointer to the mask register in memory. */ - int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ - int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ - int stopped_tx; /* Whether channel has been stopped for Tx - (STOP_TX, etc.) */ - int stopped_rx; /* Whether channel has been stopped for Rx */ - u32 ucc_fast_tx_virtual_fifo_base_offset;/* pointer to base of Tx - virtual fifo */ - u32 ucc_fast_rx_virtual_fifo_base_offset;/* pointer to base of Rx - virtual fifo */ -#ifdef STATISTICS - u32 tx_frames; /* Transmitted frames counter. */ - u32 rx_frames; /* Received frames counter (only frames - passed to application). */ - u32 tx_discarded; /* Discarded tx frames counter (frames that - were discarded by the driver due to errors). - */ - u32 rx_discarded; /* Discarded rx frames counter (frames that - were discarded by the driver due to errors). - */ -#endif /* STATISTICS */ - u16 mrblr; /* maximum receive buffer length */ -}; - -/* ucc_fast_init - * Initializes Fast UCC according to user provided parameters. - * - * uf_info - (In) pointer to the fast UCC info structure. - * uccf_ret - (Out) pointer to the fast UCC structure. - */ -int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret); - -/* ucc_fast_free - * Frees all resources for fast UCC. - * - * uccf - (In) pointer to the fast UCC structure. - */ -void ucc_fast_free(struct ucc_fast_private * uccf); - -/* ucc_fast_enable - * Enables a fast UCC port. - * This routine enables Tx and/or Rx through the General UCC Mode Register. - * - * uccf - (In) pointer to the fast UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode); - -/* ucc_fast_disable - * Disables a fast UCC port. - * This routine disables Tx and/or Rx through the General UCC Mode Register. - * - * uccf - (In) pointer to the fast UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode); - -/* ucc_fast_irq - * Handles interrupts on fast UCC. - * Called from the general interrupt routine to handle interrupts on fast UCC. - * - * uccf - (In) pointer to the fast UCC structure. - */ -void ucc_fast_irq(struct ucc_fast_private * uccf); - -/* ucc_fast_transmit_on_demand - * Immediately forces a poll of the transmitter for data to be sent. - * Typically, the hardware performs a periodic poll for data that the - * transmit routine has set up to be transmitted. In cases where - * this polling cycle is not soon enough, this optional routine can - * be invoked to force a poll right away, instead. Proper use for - * each transmission for which this functionality is desired is to - * call the transmit routine and then this routine right after. - * - * uccf - (In) pointer to the fast UCC structure. - */ -void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf); - -u32 ucc_fast_get_qe_cr_subblock(int uccf_num); - -void ucc_fast_dump_regs(struct ucc_fast_private * uccf); - -#endif /* __UCC_FAST_H__ */ diff --git a/trunk/include/asm-powerpc/ucc_slow.h b/trunk/include/asm-powerpc/ucc_slow.h deleted file mode 100644 index ca93bc99237e..000000000000 --- a/trunk/include/asm-powerpc/ucc_slow.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. - * - * Authors: Shlomi Gridish - * Li Yang - * - * Description: - * Internal header file for UCC SLOW unit 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. - */ -#ifndef __UCC_SLOW_H__ -#define __UCC_SLOW_H__ - -#include - -#include -#include - -#include "ucc.h" - -/* transmit BD's status */ -#define T_R 0x80000000 /* ready bit */ -#define T_PAD 0x40000000 /* add pads to short frames */ -#define T_W 0x20000000 /* wrap bit */ -#define T_I 0x10000000 /* interrupt on completion */ -#define T_L 0x08000000 /* last */ - -#define T_A 0x04000000 /* Address - the data transmitted as address - chars */ -#define T_TC 0x04000000 /* transmit CRC */ -#define T_CM 0x02000000 /* continuous mode */ -#define T_DEF 0x02000000 /* collision on previous attempt to transmit */ -#define T_P 0x01000000 /* Preamble - send Preamble sequence before - data */ -#define T_HB 0x01000000 /* heartbeat */ -#define T_NS 0x00800000 /* No Stop */ -#define T_LC 0x00800000 /* late collision */ -#define T_RL 0x00400000 /* retransmission limit */ -#define T_UN 0x00020000 /* underrun */ -#define T_CT 0x00010000 /* CTS lost */ -#define T_CSL 0x00010000 /* carrier sense lost */ -#define T_RC 0x003c0000 /* retry count */ - -/* Receive BD's status */ -#define R_E 0x80000000 /* buffer empty */ -#define R_W 0x20000000 /* wrap bit */ -#define R_I 0x10000000 /* interrupt on reception */ -#define R_L 0x08000000 /* last */ -#define R_C 0x08000000 /* the last byte in this buffer is a cntl - char */ -#define R_F 0x04000000 /* first */ -#define R_A 0x04000000 /* the first byte in this buffer is address - byte */ -#define R_CM 0x02000000 /* continuous mode */ -#define R_ID 0x01000000 /* buffer close on reception of idles */ -#define R_M 0x01000000 /* Frame received because of promiscuous - mode */ -#define R_AM 0x00800000 /* Address match */ -#define R_DE 0x00800000 /* Address match */ -#define R_LG 0x00200000 /* Break received */ -#define R_BR 0x00200000 /* Frame length violation */ -#define R_NO 0x00100000 /* Rx Non Octet Aligned Packet */ -#define R_FR 0x00100000 /* Framing Error (no stop bit) character - received */ -#define R_PR 0x00080000 /* Parity Error character received */ -#define R_AB 0x00080000 /* Frame Aborted */ -#define R_SH 0x00080000 /* frame is too short */ -#define R_CR 0x00040000 /* CRC Error */ -#define R_OV 0x00020000 /* Overrun */ -#define R_CD 0x00010000 /* CD lost */ -#define R_CL 0x00010000 /* this frame is closed because of a - collision */ - -/* Rx Data buffer must be 4 bytes aligned in most cases.*/ -#define UCC_SLOW_RX_ALIGN 4 -#define UCC_SLOW_MRBLR_ALIGNMENT 4 -#define UCC_SLOW_PRAM_SIZE 0x100 -#define ALIGNMENT_OF_UCC_SLOW_PRAM 64 - -/* UCC Slow Channel Protocol Mode */ -enum ucc_slow_channel_protocol_mode { - UCC_SLOW_CHANNEL_PROTOCOL_MODE_QMC = 0x00000002, - UCC_SLOW_CHANNEL_PROTOCOL_MODE_UART = 0x00000004, - UCC_SLOW_CHANNEL_PROTOCOL_MODE_BISYNC = 0x00000008, -}; - -/* UCC Slow Transparent Transmit CRC (TCRC) */ -enum ucc_slow_transparent_tcrc { - /* 16-bit CCITT CRC (HDLC). (X16 + X12 + X5 + 1) */ - UCC_SLOW_TRANSPARENT_TCRC_CCITT_CRC16 = 0x00000000, - /* CRC16 (BISYNC). (X16 + X15 + X2 + 1) */ - UCC_SLOW_TRANSPARENT_TCRC_CRC16 = 0x00004000, - /* 32-bit CCITT CRC (Ethernet and HDLC) */ - UCC_SLOW_TRANSPARENT_TCRC_CCITT_CRC32 = 0x00008000, -}; - -/* UCC Slow oversampling rate for transmitter (TDCR) */ -enum ucc_slow_tx_oversampling_rate { - /* 1x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_1 = 0x00000000, - /* 8x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_8 = 0x00010000, - /* 16x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_16 = 0x00020000, - /* 32x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_TX_TDCR_32 = 0x00030000, -}; - -/* UCC Slow Oversampling rate for receiver (RDCR) -*/ -enum ucc_slow_rx_oversampling_rate { - /* 1x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_1 = 0x00000000, - /* 8x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_8 = 0x00004000, - /* 16x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_16 = 0x00008000, - /* 32x clock mode */ - UCC_SLOW_OVERSAMPLING_RATE_RX_RDCR_32 = 0x0000c000, -}; - -/* UCC Slow Transmitter encoding method (TENC) -*/ -enum ucc_slow_tx_encoding_method { - UCC_SLOW_TRANSMITTER_ENCODING_METHOD_TENC_NRZ = 0x00000000, - UCC_SLOW_TRANSMITTER_ENCODING_METHOD_TENC_NRZI = 0x00000100 -}; - -/* UCC Slow Receiver decoding method (RENC) -*/ -enum ucc_slow_rx_decoding_method { - UCC_SLOW_RECEIVER_DECODING_METHOD_RENC_NRZ = 0x00000000, - UCC_SLOW_RECEIVER_DECODING_METHOD_RENC_NRZI = 0x00000800 -}; - -/* UCC Slow Diagnostic mode (DIAG) -*/ -enum ucc_slow_diag_mode { - UCC_SLOW_DIAG_MODE_NORMAL = 0x00000000, - UCC_SLOW_DIAG_MODE_LOOPBACK = 0x00000040, - UCC_SLOW_DIAG_MODE_ECHO = 0x00000080, - UCC_SLOW_DIAG_MODE_LOOPBACK_ECHO = 0x000000c0 -}; - -struct ucc_slow_info { - int ucc_num; - enum qe_clock rx_clock; - enum qe_clock tx_clock; - struct ucc_slow *us_regs; - int irq; - u16 uccm_mask; - int data_mem_part; - int init_tx; - int init_rx; - u32 tx_bd_ring_len; - u32 rx_bd_ring_len; - int rx_interrupts; - int brkpt_support; - int grant_support; - int tsa; - int cdp; - int cds; - int ctsp; - int ctss; - int rinv; - int tinv; - int rtsm; - int rfw; - int tci; - int tend; - int tfl; - int txsy; - u16 max_rx_buf_length; - enum ucc_slow_transparent_tcrc tcrc; - enum ucc_slow_channel_protocol_mode mode; - enum ucc_slow_diag_mode diag; - enum ucc_slow_tx_oversampling_rate tdcr; - enum ucc_slow_rx_oversampling_rate rdcr; - enum ucc_slow_tx_encoding_method tenc; - enum ucc_slow_rx_decoding_method renc; -}; - -struct ucc_slow_private { - struct ucc_slow_info *us_info; - struct ucc_slow *us_regs; /* a pointer to memory map of UCC regs */ - struct ucc_slow_pram *us_pram; /* a pointer to the parameter RAM */ - u32 us_pram_offset; - int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ - int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ - int stopped_tx; /* Whether channel has been stopped for Tx - (STOP_TX, etc.) */ - int stopped_rx; /* Whether channel has been stopped for Rx */ - struct list_head confQ; /* frames passed to chip waiting for tx */ - u32 first_tx_bd_mask; /* mask is used in Tx routine to save status - and length for first BD in a frame */ - u32 tx_base_offset; /* first BD in Tx BD table offset (In MURAM) */ - u32 rx_base_offset; /* first BD in Rx BD table offset (In MURAM) */ - u8 *confBd; /* next BD for confirm after Tx */ - u8 *tx_bd; /* next BD for new Tx request */ - u8 *rx_bd; /* next BD to collect after Rx */ - void *p_rx_frame; /* accumulating receive frame */ - u16 *p_ucce; /* a pointer to the event register in memory. - */ - u16 *p_uccm; /* a pointer to the mask register in memory */ - u16 saved_uccm; /* a saved mask for the RX Interrupt bits */ -#ifdef STATISTICS - u32 tx_frames; /* Transmitted frames counters */ - u32 rx_frames; /* Received frames counters (only frames - passed to application) */ - u32 rx_discarded; /* Discarded frames counters (frames that - were discarded by the driver due to - errors) */ -#endif /* STATISTICS */ -}; - -/* ucc_slow_init - * Initializes Slow UCC according to provided parameters. - * - * us_info - (In) pointer to the slow UCC info structure. - * uccs_ret - (Out) pointer to the slow UCC structure. - */ -int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret); - -/* ucc_slow_free - * Frees all resources for slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_free(struct ucc_slow_private * uccs); - -/* ucc_slow_enable - * Enables a fast UCC port. - * This routine enables Tx and/or Rx through the General UCC Mode Register. - * - * uccs - (In) pointer to the slow UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode); - -/* ucc_slow_disable - * Disables a fast UCC port. - * This routine disables Tx and/or Rx through the General UCC Mode Register. - * - * uccs - (In) pointer to the slow UCC structure. - * mode - (In) TX, RX, or both. - */ -void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode); - -/* ucc_slow_poll_transmitter_now - * Immediately forces a poll of the transmitter for data to be sent. - * Typically, the hardware performs a periodic poll for data that the - * transmit routine has set up to be transmitted. In cases where - * this polling cycle is not soon enough, this optional routine can - * be invoked to force a poll right away, instead. Proper use for - * each transmission for which this functionality is desired is to - * call the transmit routine and then this routine right after. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs); - -/* ucc_slow_graceful_stop_tx - * Smoothly stops transmission on a specified slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs); - -/* ucc_slow_stop_tx - * Stops transmission on a specified slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_stop_tx(struct ucc_slow_private * uccs); - -/* ucc_slow_restart_x - * Restarts transmitting on a specified slow UCC. - * - * uccs - (In) pointer to the slow UCC structure. - */ -void ucc_slow_restart_x(struct ucc_slow_private * uccs); - -u32 ucc_slow_get_qe_cr_subblock(int uccs_num); - -#endif /* __UCC_SLOW_H__ */ diff --git a/trunk/include/asm-powerpc/xmon.h b/trunk/include/asm-powerpc/xmon.h index f1d337ed68d5..43f7129984c7 100644 --- a/trunk/include/asm-powerpc/xmon.h +++ b/trunk/include/asm-powerpc/xmon.h @@ -1,22 +1,12 @@ -#ifndef __ASM_POWERPC_XMON_H -#define __ASM_POWERPC_XMON_H +#ifndef __PPC_XMON_H +#define __PPC_XMON_H +#ifdef __KERNEL__ -/* - * Copyrignt (C) 2006 IBM Corp - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ +struct pt_regs; -#ifdef __KERNEL__ +extern int xmon(struct pt_regs *excp); +extern void xmon_printf(const char *fmt, ...); +extern void xmon_init(int); -#ifdef CONFIG_XMON -extern void xmon_setup(void); -#else -static inline void xmon_setup(void) { }; #endif - -#endif /* __KERNEL __ */ -#endif /* __ASM_POWERPC_XMON_H */ +#endif diff --git a/trunk/include/asm-sparc64/compat_signal.h b/trunk/include/asm-sparc64/compat_signal.h index 7aefa301321e..b759eab9b51c 100644 --- a/trunk/include/asm-sparc64/compat_signal.h +++ b/trunk/include/asm-sparc64/compat_signal.h @@ -1,7 +1,6 @@ #ifndef _COMPAT_SIGNAL_H #define _COMPAT_SIGNAL_H -#include #include #include diff --git a/trunk/include/asm-x86_64/alternative-asm.i b/trunk/include/asm-x86_64/alternative-asm.i index e4041f4fa4dc..0b3f1a2bb2cb 100644 --- a/trunk/include/asm-x86_64/alternative-asm.i +++ b/trunk/include/asm-x86_64/alternative-asm.i @@ -1,5 +1,3 @@ -#include - #ifdef CONFIG_SMP .macro LOCK_PREFIX 1: lock diff --git a/trunk/include/asm-x86_64/hardirq.h b/trunk/include/asm-x86_64/hardirq.h index 95d5e090ed89..64a65ce2f41f 100644 --- a/trunk/include/asm-x86_64/hardirq.h +++ b/trunk/include/asm-x86_64/hardirq.h @@ -6,9 +6,6 @@ #include #include -/* We can have at most NR_VECTORS irqs routed to a cpu at a time */ -#define MAX_HARDIRQS_PER_CPU NR_VECTORS - #define __ARCH_IRQ_STAT 1 #define local_softirq_pending() read_pda(__softirq_pending) diff --git a/trunk/include/asm-x86_64/hw_irq.h b/trunk/include/asm-x86_64/hw_irq.h index 53d0d9fd10d6..48a4a5364e85 100644 --- a/trunk/include/asm-x86_64/hw_irq.h +++ b/trunk/include/asm-x86_64/hw_irq.h @@ -19,7 +19,8 @@ #include #include #include -#include + +struct hw_interrupt_type; #endif #define NMI_VECTOR 0x02 @@ -74,10 +75,9 @@ #ifndef __ASSEMBLY__ -extern unsigned int irq_vector[NR_IRQ_VECTORS]; -typedef int vector_irq_t[NR_VECTORS]; -DECLARE_PER_CPU(vector_irq_t, vector_irq); +extern u8 irq_vector[NR_IRQ_VECTORS]; #define IO_APIC_VECTOR(irq) (irq_vector[irq]) +#define AUTO_ASSIGN -1 /* * Various low-level irq details needed by irq.c, process.c, diff --git a/trunk/include/asm-x86_64/hypertransport.h b/trunk/include/asm-x86_64/hypertransport.h deleted file mode 100644 index c16c6ff4bdd7..000000000000 --- a/trunk/include/asm-x86_64/hypertransport.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef ASM_HYPERTRANSPORT_H -#define ASM_HYPERTRANSPORT_H - -/* - * Constants for x86 Hypertransport Interrupts. - */ - -#define HT_IRQ_LOW_BASE 0xf8000000 - -#define HT_IRQ_LOW_VECTOR_SHIFT 16 -#define HT_IRQ_LOW_VECTOR_MASK 0x00ff0000 -#define HT_IRQ_LOW_VECTOR(v) (((v) << HT_IRQ_LOW_VECTOR_SHIFT) & HT_IRQ_LOW_VECTOR_MASK) - -#define HT_IRQ_LOW_DEST_ID_SHIFT 8 -#define HT_IRQ_LOW_DEST_ID_MASK 0x0000ff00 -#define HT_IRQ_LOW_DEST_ID(v) (((v) << HT_IRQ_LOW_DEST_ID_SHIFT) & HT_IRQ_LOW_DEST_ID_MASK) - -#define HT_IRQ_LOW_DM_PHYSICAL 0x0000000 -#define HT_IRQ_LOW_DM_LOGICAL 0x0000040 - -#define HT_IRQ_LOW_RQEOI_EDGE 0x0000000 -#define HT_IRQ_LOW_RQEOI_LEVEL 0x0000020 - - -#define HT_IRQ_LOW_MT_FIXED 0x0000000 -#define HT_IRQ_LOW_MT_ARBITRATED 0x0000004 -#define HT_IRQ_LOW_MT_SMI 0x0000008 -#define HT_IRQ_LOW_MT_NMI 0x000000c -#define HT_IRQ_LOW_MT_INIT 0x0000010 -#define HT_IRQ_LOW_MT_STARTUP 0x0000014 -#define HT_IRQ_LOW_MT_EXTINT 0x0000018 -#define HT_IRQ_LOW_MT_LINT1 0x000008c -#define HT_IRQ_LOW_MT_LINT0 0x0000098 - -#define HT_IRQ_LOW_IRQ_MASKED 0x0000001 - - -#define HT_IRQ_HIGH_DEST_ID_SHIFT 0 -#define HT_IRQ_HIGH_DEST_ID_MASK 0x00ffffff -#define HT_IRQ_HIGH_DEST_ID(v) ((((v) >> 8) << HT_IRQ_HIGH_DEST_ID_SHIFT) & HT_IRQ_HIGH_DEST_ID_MASK) - -#endif /* ASM_HYPERTRANSPORT_H */ diff --git a/trunk/include/asm-x86_64/io_apic.h b/trunk/include/asm-x86_64/io_apic.h index 171ec2dc8c04..5d1b5c68e36e 100644 --- a/trunk/include/asm-x86_64/io_apic.h +++ b/trunk/include/asm-x86_64/io_apic.h @@ -10,6 +10,46 @@ * Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar */ +#ifdef CONFIG_PCI_MSI +static inline int use_pci_vector(void) {return 1;} +static inline void disable_edge_ioapic_vector(unsigned int vector) { } +static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { } +static inline void end_edge_ioapic_vector (unsigned int vector) { } +#define startup_level_ioapic startup_level_ioapic_vector +#define shutdown_level_ioapic mask_IO_APIC_vector +#define enable_level_ioapic unmask_IO_APIC_vector +#define disable_level_ioapic mask_IO_APIC_vector +#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_vector +#define end_level_ioapic end_level_ioapic_vector +#define set_ioapic_affinity set_ioapic_affinity_vector + +#define startup_edge_ioapic startup_edge_ioapic_vector +#define shutdown_edge_ioapic disable_edge_ioapic_vector +#define enable_edge_ioapic unmask_IO_APIC_vector +#define disable_edge_ioapic disable_edge_ioapic_vector +#define ack_edge_ioapic ack_edge_ioapic_vector +#define end_edge_ioapic end_edge_ioapic_vector +#else +static inline int use_pci_vector(void) {return 0;} +static inline void disable_edge_ioapic_irq(unsigned int irq) { } +static inline void mask_and_ack_level_ioapic_irq(unsigned int irq) { } +static inline void end_edge_ioapic_irq (unsigned int irq) { } +#define startup_level_ioapic startup_level_ioapic_irq +#define shutdown_level_ioapic mask_IO_APIC_irq +#define enable_level_ioapic unmask_IO_APIC_irq +#define disable_level_ioapic mask_IO_APIC_irq +#define mask_and_ack_level_ioapic mask_and_ack_level_ioapic_irq +#define end_level_ioapic end_level_ioapic_irq +#define set_ioapic_affinity set_ioapic_affinity_irq + +#define startup_edge_ioapic startup_edge_ioapic_irq +#define shutdown_edge_ioapic disable_edge_ioapic_irq +#define enable_edge_ioapic unmask_IO_APIC_irq +#define disable_edge_ioapic disable_edge_ioapic_irq +#define ack_edge_ioapic ack_edge_ioapic_irq +#define end_edge_ioapic end_edge_ioapic_irq +#endif + #define APIC_MISMATCH_DEBUG #define IO_APIC_BASE(idx) \ @@ -162,10 +202,13 @@ extern int skip_ioapic_setup; extern int io_apic_get_version (int ioapic); extern int io_apic_get_redir_entries (int ioapic); extern int io_apic_set_pci_routing (int ioapic, int pin, int irq, int, int); +extern int timer_uses_ioapic_pin_0; #endif extern int sis_apic_bug; /* dummy */ +extern int assign_irq_vector(int irq); + void enable_NMI_through_LVT0 (void * dummy); extern spinlock_t i8259A_lock; diff --git a/trunk/include/asm-x86_64/irq.h b/trunk/include/asm-x86_64/irq.h index 5006c6e75656..43469d8ab71a 100644 --- a/trunk/include/asm-x86_64/irq.h +++ b/trunk/include/asm-x86_64/irq.h @@ -31,8 +31,13 @@ #define FIRST_SYSTEM_VECTOR 0xef /* duplicated in hw_irq.h */ -#define NR_IRQS (NR_VECTORS + (32 *NR_CPUS)) +#ifdef CONFIG_PCI_MSI +#define NR_IRQS FIRST_SYSTEM_VECTOR #define NR_IRQ_VECTORS NR_IRQS +#else +#define NR_IRQS 224 +#define NR_IRQ_VECTORS (32 * NR_CPUS) +#endif static __inline__ int irq_canonicalize(int irq) { diff --git a/trunk/include/asm-x86_64/msi.h b/trunk/include/asm-x86_64/msi.h new file mode 100644 index 000000000000..3ad2346624b2 --- /dev/null +++ b/trunk/include/asm-x86_64/msi.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#ifndef ASM_MSI_H +#define ASM_MSI_H + +#include +#include +#include + +#define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) +#define MSI_TARGET_CPU_SHIFT 12 + +extern struct msi_ops msi_apic_ops; + +static inline int msi_arch_init(void) +{ + msi_register(&msi_apic_ops); + return 0; +} + +#endif /* ASM_MSI_H */ diff --git a/trunk/include/asm-x86_64/msidef.h b/trunk/include/asm-x86_64/msidef.h deleted file mode 100644 index 5b8acddb70fb..000000000000 --- a/trunk/include/asm-x86_64/msidef.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ASM_MSIDEF_H -#define ASM_MSIDEF_H - -/* - * Constants for Intel APIC based MSI messages. - */ - -/* - * Shifts for MSI data - */ - -#define MSI_DATA_VECTOR_SHIFT 0 -#define MSI_DATA_VECTOR_MASK 0x000000ff -#define MSI_DATA_VECTOR(v) (((v) << MSI_DATA_VECTOR_SHIFT) & MSI_DATA_VECTOR_MASK) - -#define MSI_DATA_DELIVERY_MODE_SHIFT 8 -#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_MODE_SHIFT) -#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_MODE_SHIFT) - -#define MSI_DATA_LEVEL_SHIFT 14 -#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) -#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) - -#define MSI_DATA_TRIGGER_SHIFT 15 -#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) -#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) - -/* - * Shift/mask fields for msi address - */ - -#define MSI_ADDR_BASE_HI 0 -#define MSI_ADDR_BASE_LO 0xfee00000 - -#define MSI_ADDR_DEST_MODE_SHIFT 2 -#define MSI_ADDR_DEST_MODE_PHYSICAL (0 << MSI_ADDR_DEST_MODE_SHIFT) -#define MSI_ADDR_DEST_MODE_LOGICAL (1 << MSI_ADDR_DEST_MODE_SHIFT) - -#define MSI_ADDR_REDIRECTION_SHIFT 3 -#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) /* dedicated cpu */ -#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) /* lowest priority */ - -#define MSI_ADDR_DEST_ID_SHIFT 12 -#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 -#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & MSI_ADDR_DEST_ID_MASK) - -#endif /* ASM_MSIDEF_H */ diff --git a/trunk/include/linux/Kbuild b/trunk/include/linux/Kbuild index ea005c0a79fd..f7a52e19b4be 100644 --- a/trunk/include/linux/Kbuild +++ b/trunk/include/linux/Kbuild @@ -46,7 +46,6 @@ header-y += coff.h header-y += comstats.h header-y += consolemap.h header-y += cycx_cfm.h -header-y += dlm_device.h header-y += dm-ioctl.h header-y += dn.h header-y += dqblk_v1.h @@ -105,7 +104,6 @@ header-y += ixjuser.h header-y += jffs2.h header-y += keyctl.h header-y += limits.h -header-y += lock_dlm_plock.h header-y += magic.h header-y += major.h header-y += matroxfb.h @@ -158,10 +156,12 @@ header-y += toshiba.h header-y += ultrasound.h header-y += un.h header-y += utime.h +header-y += utsname.h header-y += video_decoder.h header-y += video_encoder.h header-y += videotext.h header-y += vt.h +header-y += wavefront.h header-y += wireless.h header-y += xattr.h header-y += x25.h @@ -194,7 +194,6 @@ unifdef-y += cyclades.h unifdef-y += dccp.h unifdef-y += dirent.h unifdef-y += divert.h -unifdef-y += dlm.h unifdef-y += elfcore.h unifdef-y += errno.h unifdef-y += errqueue.h @@ -211,7 +210,6 @@ unifdef-y += ftape.h unifdef-y += gameport.h unifdef-y += generic_serial.h unifdef-y += genhd.h -unifdef-y += gfs2_ondisk.h unifdef-y += hayesesp.h unifdef-y += hdlcdrv.h unifdef-y += hdlc.h @@ -335,7 +333,6 @@ unifdef-y += unistd.h unifdef-y += usb_ch9.h unifdef-y += usbdevice_fs.h unifdef-y += user.h -unifdef-y += utsname.h unifdef-y += videodev2.h unifdef-y += videodev.h unifdef-y += wait.h diff --git a/trunk/include/linux/ac97_codec.h b/trunk/include/linux/ac97_codec.h index 22eb9367235a..2ed2fd855133 100644 --- a/trunk/include/linux/ac97_codec.h +++ b/trunk/include/linux/ac97_codec.h @@ -331,6 +331,8 @@ extern int ac97_read_proc (char *page_out, char **start, off_t off, extern int ac97_probe_codec(struct ac97_codec *); extern unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate); extern unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate); +extern int ac97_save_state(struct ac97_codec *codec); +extern int ac97_restore_state(struct ac97_codec *codec); extern struct ac97_codec *ac97_alloc_codec(void); extern void ac97_release_codec(struct ac97_codec *codec); @@ -344,6 +346,9 @@ struct ac97_driver { void (*remove) (struct ac97_codec *codec, struct ac97_driver *driver); }; +extern int ac97_register_driver(struct ac97_driver *driver); +extern void ac97_unregister_driver(struct ac97_driver *driver); + /* quirk types */ enum { AC97_TUNE_DEFAULT = -1, /* use default from quirk list (not valid in list) */ diff --git a/trunk/include/linux/audit.h b/trunk/include/linux/audit.h index b2ca666d9997..c3aa09751814 100644 --- a/trunk/include/linux/audit.h +++ b/trunk/include/linux/audit.h @@ -75,7 +75,7 @@ #define AUDIT_DAEMON_CONFIG 1203 /* Daemon config change */ #define AUDIT_SYSCALL 1300 /* Syscall event */ -/* #define AUDIT_FS_WATCH 1301 * Deprecated */ +#define AUDIT_FS_WATCH 1301 /* Filesystem watch event */ #define AUDIT_PATH 1302 /* Filename path information */ #define AUDIT_IPC 1303 /* IPC record */ #define AUDIT_SOCKETCALL 1304 /* sys_socketcall arguments */ @@ -88,7 +88,6 @@ #define AUDIT_MQ_SENDRECV 1313 /* POSIX MQ send/receive record type */ #define AUDIT_MQ_NOTIFY 1314 /* POSIX MQ notify record type */ #define AUDIT_MQ_GETSETATTR 1315 /* POSIX MQ get/set attribute record type */ -#define AUDIT_KERNEL_OTHER 1316 /* For use by 3rd party modules */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/trunk/include/linux/config.h b/trunk/include/linux/config.h index a91f5e55b525..479ffb0a22d8 100644 --- a/trunk/include/linux/config.h +++ b/trunk/include/linux/config.h @@ -3,6 +3,7 @@ /* This file is no longer in use and kept only for backward compatibility. * autoconf.h is now included via -imacros on the commandline */ +#warning Including config.h is deprecated. #include #endif diff --git a/trunk/include/linux/debug_locks.h b/trunk/include/linux/debug_locks.h index 952bee79a8f3..88dafa246d87 100644 --- a/trunk/include/linux/debug_locks.h +++ b/trunk/include/linux/debug_locks.h @@ -43,8 +43,6 @@ extern int debug_locks_off(void); # define locking_selftest() do { } while (0) #endif -struct task_struct; - #ifdef CONFIG_LOCKDEP extern void debug_show_all_locks(void); extern void debug_show_held_locks(struct task_struct *task); diff --git a/trunk/include/linux/dlm.h b/trunk/include/linux/dlm.h deleted file mode 100644 index 1b1dcb9a40bb..000000000000 --- a/trunk/include/linux/dlm.h +++ /dev/null @@ -1,302 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -#ifndef __DLM_DOT_H__ -#define __DLM_DOT_H__ - -/* - * Interface to Distributed Lock Manager (DLM) - * routines and structures to use DLM lockspaces - */ - -/* - * Lock Modes - */ - -#define DLM_LOCK_IV -1 /* invalid */ -#define DLM_LOCK_NL 0 /* null */ -#define DLM_LOCK_CR 1 /* concurrent read */ -#define DLM_LOCK_CW 2 /* concurrent write */ -#define DLM_LOCK_PR 3 /* protected read */ -#define DLM_LOCK_PW 4 /* protected write */ -#define DLM_LOCK_EX 5 /* exclusive */ - -/* - * Maximum size in bytes of a dlm_lock name - */ - -#define DLM_RESNAME_MAXLEN 64 - -/* - * Flags to dlm_lock - * - * DLM_LKF_NOQUEUE - * - * Do not queue the lock request on the wait queue if it cannot be granted - * immediately. If the lock cannot be granted because of this flag, DLM will - * either return -EAGAIN from the dlm_lock call or will return 0 from - * dlm_lock and -EAGAIN in the lock status block when the AST is executed. - * - * DLM_LKF_CANCEL - * - * Used to cancel a pending lock request or conversion. A converting lock is - * returned to its previously granted mode. - * - * DLM_LKF_CONVERT - * - * Indicates a lock conversion request. For conversions the name and namelen - * are ignored and the lock ID in the LKSB is used to identify the lock. - * - * DLM_LKF_VALBLK - * - * Requests DLM to return the current contents of the lock value block in the - * lock status block. When this flag is set in a lock conversion from PW or EX - * modes, DLM assigns the value specified in the lock status block to the lock - * value block of the lock resource. The LVB is a DLM_LVB_LEN size array - * containing application-specific information. - * - * DLM_LKF_QUECVT - * - * Force a conversion request to be queued, even if it is compatible with - * the granted modes of other locks on the same resource. - * - * DLM_LKF_IVVALBLK - * - * Invalidate the lock value block. - * - * DLM_LKF_CONVDEADLK - * - * Allows the dlm to resolve conversion deadlocks internally by demoting the - * granted mode of a converting lock to NL. The DLM_SBF_DEMOTED flag is - * returned for a conversion that's been effected by this. - * - * DLM_LKF_PERSISTENT - * - * Only relevant to locks originating in userspace. A persistent lock will not - * be removed if the process holding the lock exits. - * - * DLM_LKF_NODLKWT - * DLM_LKF_NODLCKBLK - * - * net yet implemented - * - * DLM_LKF_EXPEDITE - * - * Used only with new requests for NL mode locks. Tells the lock manager - * to grant the lock, ignoring other locks in convert and wait queues. - * - * DLM_LKF_NOQUEUEBAST - * - * Send blocking AST's before returning -EAGAIN to the caller. It is only - * used along with the NOQUEUE flag. Blocking AST's are not sent for failed - * NOQUEUE requests otherwise. - * - * DLM_LKF_HEADQUE - * - * Add a lock to the head of the convert or wait queue rather than the tail. - * - * DLM_LKF_NOORDER - * - * Disregard the standard grant order rules and grant a lock as soon as it - * is compatible with other granted locks. - * - * DLM_LKF_ORPHAN - * - * not yet implemented - * - * DLM_LKF_ALTPR - * - * If the requested mode cannot be granted immediately, try to grant the lock - * in PR mode instead. If this alternate mode is granted instead of the - * requested mode, DLM_SBF_ALTMODE is returned in the lksb. - * - * DLM_LKF_ALTCW - * - * The same as ALTPR, but the alternate mode is CW. - * - * DLM_LKF_FORCEUNLOCK - * - * Unlock the lock even if it is converting or waiting or has sublocks. - * Only really for use by the userland device.c code. - * - */ - -#define DLM_LKF_NOQUEUE 0x00000001 -#define DLM_LKF_CANCEL 0x00000002 -#define DLM_LKF_CONVERT 0x00000004 -#define DLM_LKF_VALBLK 0x00000008 -#define DLM_LKF_QUECVT 0x00000010 -#define DLM_LKF_IVVALBLK 0x00000020 -#define DLM_LKF_CONVDEADLK 0x00000040 -#define DLM_LKF_PERSISTENT 0x00000080 -#define DLM_LKF_NODLCKWT 0x00000100 -#define DLM_LKF_NODLCKBLK 0x00000200 -#define DLM_LKF_EXPEDITE 0x00000400 -#define DLM_LKF_NOQUEUEBAST 0x00000800 -#define DLM_LKF_HEADQUE 0x00001000 -#define DLM_LKF_NOORDER 0x00002000 -#define DLM_LKF_ORPHAN 0x00004000 -#define DLM_LKF_ALTPR 0x00008000 -#define DLM_LKF_ALTCW 0x00010000 -#define DLM_LKF_FORCEUNLOCK 0x00020000 - -/* - * Some return codes that are not in errno.h - */ - -#define DLM_ECANCEL 0x10001 -#define DLM_EUNLOCK 0x10002 - -typedef void dlm_lockspace_t; - -/* - * Lock status block - * - * Use this structure to specify the contents of the lock value block. For a - * conversion request, this structure is used to specify the lock ID of the - * lock. DLM writes the status of the lock request and the lock ID assigned - * to the request in the lock status block. - * - * sb_lkid: the returned lock ID. It is set on new (non-conversion) requests. - * It is available when dlm_lock returns. - * - * sb_lvbptr: saves or returns the contents of the lock's LVB according to rules - * shown for the DLM_LKF_VALBLK flag. - * - * sb_flags: DLM_SBF_DEMOTED is returned if in the process of promoting a lock, - * it was first demoted to NL to avoid conversion deadlock. - * DLM_SBF_VALNOTVALID is returned if the resource's LVB is marked invalid. - * - * sb_status: the returned status of the lock request set prior to AST - * execution. Possible return values: - * - * 0 if lock request was successful - * -EAGAIN if request would block and is flagged DLM_LKF_NOQUEUE - * -ENOMEM if there is no memory to process request - * -EINVAL if there are invalid parameters - * -DLM_EUNLOCK if unlock request was successful - * -DLM_ECANCEL if a cancel completed successfully - */ - -#define DLM_SBF_DEMOTED 0x01 -#define DLM_SBF_VALNOTVALID 0x02 -#define DLM_SBF_ALTMODE 0x04 - -struct dlm_lksb { - int sb_status; - uint32_t sb_lkid; - char sb_flags; - char * sb_lvbptr; -}; - - -#ifdef __KERNEL__ - -#define DLM_LSFL_NODIR 0x00000001 - -/* - * dlm_new_lockspace - * - * Starts a lockspace with the given name. If the named lockspace exists in - * the cluster, the calling node joins it. - */ - -int dlm_new_lockspace(char *name, int namelen, dlm_lockspace_t **lockspace, - uint32_t flags, int lvblen); - -/* - * dlm_release_lockspace - * - * Stop a lockspace. - */ - -int dlm_release_lockspace(dlm_lockspace_t *lockspace, int force); - -/* - * dlm_lock - * - * Make an asyncronous request to acquire or convert a lock on a named - * resource. - * - * lockspace: context for the request - * mode: the requested mode of the lock (DLM_LOCK_) - * lksb: lock status block for input and async return values - * flags: input flags (DLM_LKF_) - * name: name of the resource to lock, can be binary - * namelen: the length in bytes of the resource name (MAX_RESNAME_LEN) - * parent: the lock ID of a parent lock or 0 if none - * lockast: function DLM executes when it completes processing the request - * astarg: argument passed to lockast and bast functions - * bast: function DLM executes when this lock later blocks another request - * - * Returns: - * 0 if request is successfully queued for processing - * -EINVAL if any input parameters are invalid - * -EAGAIN if request would block and is flagged DLM_LKF_NOQUEUE - * -ENOMEM if there is no memory to process request - * -ENOTCONN if there is a communication error - * - * If the call to dlm_lock returns an error then the operation has failed and - * the AST routine will not be called. If dlm_lock returns 0 it is still - * possible that the lock operation will fail. The AST routine will be called - * when the locking is complete and the status is returned in the lksb. - * - * If the AST routines or parameter are passed to a conversion operation then - * they will overwrite those values that were passed to a previous dlm_lock - * call. - * - * AST routines should not block (at least not for long), but may make - * any locking calls they please. - */ - -int dlm_lock(dlm_lockspace_t *lockspace, - int mode, - struct dlm_lksb *lksb, - uint32_t flags, - void *name, - unsigned int namelen, - uint32_t parent_lkid, - void (*lockast) (void *astarg), - void *astarg, - void (*bast) (void *astarg, int mode)); - -/* - * dlm_unlock - * - * Asynchronously release a lock on a resource. The AST routine is called - * when the resource is successfully unlocked. - * - * lockspace: context for the request - * lkid: the lock ID as returned in the lksb - * flags: input flags (DLM_LKF_) - * lksb: if NULL the lksb parameter passed to last lock request is used - * astarg: the arg used with the completion ast for the unlock - * - * Returns: - * 0 if request is successfully queued for processing - * -EINVAL if any input parameters are invalid - * -ENOTEMPTY if the lock still has sublocks - * -EBUSY if the lock is waiting for a remote lock operation - * -ENOTCONN if there is a communication error - */ - -int dlm_unlock(dlm_lockspace_t *lockspace, - uint32_t lkid, - uint32_t flags, - struct dlm_lksb *lksb, - void *astarg); - -#endif /* __KERNEL__ */ - -#endif /* __DLM_DOT_H__ */ - diff --git a/trunk/include/linux/dlm_device.h b/trunk/include/linux/dlm_device.h deleted file mode 100644 index 2a2dd189b9fd..000000000000 --- a/trunk/include/linux/dlm_device.h +++ /dev/null @@ -1,86 +0,0 @@ -/****************************************************************************** -******************************************************************************* -** -** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. -** Copyright (C) 2004-2005 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. -** -******************************************************************************* -******************************************************************************/ - -/* This is the device interface for dlm, most users will use a library - * interface. - */ - -#define DLM_USER_LVB_LEN 32 - -/* Version of the device interface */ -#define DLM_DEVICE_VERSION_MAJOR 5 -#define DLM_DEVICE_VERSION_MINOR 0 -#define DLM_DEVICE_VERSION_PATCH 0 - -/* struct passed to the lock write */ -struct dlm_lock_params { - __u8 mode; - __u8 namelen; - __u16 flags; - __u32 lkid; - __u32 parent; - void __user *castparam; - void __user *castaddr; - void __user *bastparam; - void __user *bastaddr; - struct dlm_lksb __user *lksb; - char lvb[DLM_USER_LVB_LEN]; - char name[0]; -}; - -struct dlm_lspace_params { - __u32 flags; - __u32 minor; - char name[0]; -}; - -struct dlm_write_request { - __u32 version[3]; - __u8 cmd; - __u8 is64bit; - __u8 unused[2]; - - union { - struct dlm_lock_params lock; - struct dlm_lspace_params lspace; - } i; -}; - -/* struct read from the "device" fd, - consists mainly of userspace pointers for the library to use */ -struct dlm_lock_result { - __u32 length; - void __user * user_astaddr; - void __user * user_astparam; - struct dlm_lksb __user * user_lksb; - struct dlm_lksb lksb; - __u8 bast_mode; - __u8 unused[3]; - /* Offsets may be zero if no data is present */ - __u32 lvb_offset; -}; - -/* Commands passed to the device */ -#define DLM_USER_LOCK 1 -#define DLM_USER_UNLOCK 2 -#define DLM_USER_QUERY 3 -#define DLM_USER_CREATE_LOCKSPACE 4 -#define DLM_USER_REMOVE_LOCKSPACE 5 - -/* Arbitrary length restriction */ -#define MAX_LS_NAME_LEN 64 - -/* Lockspace flags */ -#define DLM_USER_LSFLG_AUTOFREE 1 -#define DLM_USER_LSFLG_FORCEFREE 2 - diff --git a/trunk/include/linux/fs.h b/trunk/include/linux/fs.h index 34406ed467c3..f53bf4ff1955 100644 --- a/trunk/include/linux/fs.h +++ b/trunk/include/linux/fs.h @@ -250,8 +250,6 @@ extern int dir_notify_enable; #define FS_NOTAIL_FL 0x00008000 /* file tail should not be merged */ #define FS_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ #define FS_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ -#define FS_EXTENT_FL 0x00080000 /* Extents */ -#define FS_DIRECTIO_FL 0x00100000 /* Use direct i/o */ #define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ diff --git a/trunk/include/linux/fsl_devices.h b/trunk/include/linux/fsl_devices.h index 3da29e2d524a..16fbe59edeb1 100644 --- a/trunk/include/linux/fsl_devices.h +++ b/trunk/include/linux/fsl_devices.h @@ -46,17 +46,18 @@ struct gianfar_platform_data { /* device specific information */ - u32 device_flags; + u32 device_flags; + /* board specific information */ - u32 board_flags; - u32 bus_id; - u32 phy_id; - u8 mac_addr[6]; + u32 board_flags; + u32 bus_id; + u32 phy_id; + u8 mac_addr[6]; }; struct gianfar_mdio_data { /* board specific information */ - int irq[32]; + int irq[32]; }; /* Flags related to gianfar device features */ @@ -75,13 +76,14 @@ struct gianfar_mdio_data { struct fsl_i2c_platform_data { /* device specific information */ - u32 device_flags; + u32 device_flags; }; /* Flags related to I2C device features */ #define FSL_I2C_DEV_SEPARATE_DFSRR 0x00000001 #define FSL_I2C_DEV_CLOCK_5200 0x00000002 + enum fsl_usb2_operating_modes { FSL_USB2_MPH_HOST, FSL_USB2_DR_HOST, @@ -99,9 +101,9 @@ enum fsl_usb2_phy_modes { struct fsl_usb2_platform_data { /* board specific information */ - enum fsl_usb2_operating_modes operating_mode; - enum fsl_usb2_phy_modes phy_mode; - unsigned int port_enables; + enum fsl_usb2_operating_modes operating_mode; + enum fsl_usb2_phy_modes phy_mode; + unsigned int port_enables; }; /* Flags in fsl_usb2_mph_platform_data */ @@ -119,44 +121,5 @@ struct fsl_spi_platform_data { u32 sysclk; }; -/* Ethernet interface (phy management and speed) -*/ -enum enet_interface { - ENET_10_MII, /* 10 Base T, MII interface */ - ENET_10_RMII, /* 10 Base T, RMII interface */ - ENET_10_RGMII, /* 10 Base T, RGMII interface */ - ENET_100_MII, /* 100 Base T, MII interface */ - ENET_100_RMII, /* 100 Base T, RMII interface */ - ENET_100_RGMII, /* 100 Base T, RGMII interface */ - ENET_1000_GMII, /* 1000 Base T, GMII interface */ - ENET_1000_RGMII, /* 1000 Base T, RGMII interface */ - ENET_1000_TBI, /* 1000 Base T, TBI interface */ - ENET_1000_RTBI /* 1000 Base T, RTBI interface */ -}; - -struct ucc_geth_platform_data { - /* device specific information */ - u32 device_flags; - u32 phy_reg_addr; - - /* board specific information */ - u32 board_flags; - u8 rx_clock; - u8 tx_clock; - u32 phy_id; - enum enet_interface phy_interface; - u32 phy_interrupt; - u8 mac_addr[6]; -}; - -/* Flags related to UCC Gigabit Ethernet device features */ -#define FSL_UGETH_DEV_HAS_GIGABIT 0x00000001 -#define FSL_UGETH_DEV_HAS_COALESCE 0x00000002 -#define FSL_UGETH_DEV_HAS_RMON 0x00000004 - -/* Flags in ucc_geth_platform_data */ -#define FSL_UGETH_BRD_HAS_PHY_INTR 0x00000001 - /* if not set use a timer */ - -#endif /* _FSL_DEVICE_H_ */ -#endif /* __KERNEL__ */ +#endif /* _FSL_DEVICE_H_ */ +#endif /* __KERNEL__ */ diff --git a/trunk/include/linux/gfs2_ondisk.h b/trunk/include/linux/gfs2_ondisk.h deleted file mode 100644 index a7ae7c177cac..000000000000 --- a/trunk/include/linux/gfs2_ondisk.h +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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. - */ - -#ifndef __GFS2_ONDISK_DOT_H__ -#define __GFS2_ONDISK_DOT_H__ - -#define GFS2_MAGIC 0x01161970 -#define GFS2_BASIC_BLOCK 512 -#define GFS2_BASIC_BLOCK_SHIFT 9 - -/* Lock numbers of the LM_TYPE_NONDISK type */ - -#define GFS2_MOUNT_LOCK 0 -#define GFS2_LIVE_LOCK 1 -#define GFS2_TRANS_LOCK 2 -#define GFS2_RENAME_LOCK 3 - -/* Format numbers for various metadata types */ - -#define GFS2_FORMAT_NONE 0 -#define GFS2_FORMAT_SB 100 -#define GFS2_FORMAT_RG 200 -#define GFS2_FORMAT_RB 300 -#define GFS2_FORMAT_DI 400 -#define GFS2_FORMAT_IN 500 -#define GFS2_FORMAT_LF 600 -#define GFS2_FORMAT_JD 700 -#define GFS2_FORMAT_LH 800 -#define GFS2_FORMAT_LD 900 -#define GFS2_FORMAT_LB 1000 -#define GFS2_FORMAT_EA 1600 -#define GFS2_FORMAT_ED 1700 -#define GFS2_FORMAT_QC 1400 -/* These are format numbers for entities contained in files */ -#define GFS2_FORMAT_RI 1100 -#define GFS2_FORMAT_DE 1200 -#define GFS2_FORMAT_QU 1500 -/* These are part of the superblock */ -#define GFS2_FORMAT_FS 1801 -#define GFS2_FORMAT_MULTI 1900 - -/* - * An on-disk inode number - */ - -struct gfs2_inum { - __be64 no_formal_ino; - __be64 no_addr; -}; - -static inline int gfs2_inum_equal(const struct gfs2_inum *ino1, - const struct gfs2_inum *ino2) -{ - return ino1->no_formal_ino == ino2->no_formal_ino && - ino1->no_addr == ino2->no_addr; -} - -/* - * Generic metadata head structure - * Every inplace buffer logged in the journal must start with this. - */ - -#define GFS2_METATYPE_NONE 0 -#define GFS2_METATYPE_SB 1 -#define GFS2_METATYPE_RG 2 -#define GFS2_METATYPE_RB 3 -#define GFS2_METATYPE_DI 4 -#define GFS2_METATYPE_IN 5 -#define GFS2_METATYPE_LF 6 -#define GFS2_METATYPE_JD 7 -#define GFS2_METATYPE_LH 8 -#define GFS2_METATYPE_LD 9 -#define GFS2_METATYPE_LB 12 -#define GFS2_METATYPE_EA 10 -#define GFS2_METATYPE_ED 11 -#define GFS2_METATYPE_QC 14 - -struct gfs2_meta_header { - __be32 mh_magic; - __be32 mh_type; - __be64 __pad0; /* Was generation number in gfs1 */ - __be32 mh_format; - __be32 __pad1; /* Was incarnation number in gfs1 */ -}; - -/* - * super-block structure - * - * It's probably good if SIZEOF_SB <= GFS2_BASIC_BLOCK (512 bytes) - * - * Order is important, need to be able to read old superblocks to do on-disk - * version upgrades. - */ - -/* Address of superblock in GFS2 basic blocks */ -#define GFS2_SB_ADDR 128 - -/* The lock number for the superblock (must be zero) */ -#define GFS2_SB_LOCK 0 - -/* Requirement: GFS2_LOCKNAME_LEN % 8 == 0 - Includes: the fencing zero at the end */ -#define GFS2_LOCKNAME_LEN 64 - -struct gfs2_sb { - struct gfs2_meta_header sb_header; - - __be32 sb_fs_format; - __be32 sb_multihost_format; - __u32 __pad0; /* Was superblock flags in gfs1 */ - - __be32 sb_bsize; - __be32 sb_bsize_shift; - __u32 __pad1; /* Was journal segment size in gfs1 */ - - struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */ - struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */ - struct gfs2_inum sb_root_dir; - - char sb_lockproto[GFS2_LOCKNAME_LEN]; - char sb_locktable[GFS2_LOCKNAME_LEN]; - /* In gfs1, quota and license dinodes followed */ -}; - -/* - * resource index structure - */ - -struct gfs2_rindex { - __be64 ri_addr; /* grp block disk address */ - __be32 ri_length; /* length of rgrp header in fs blocks */ - __u32 __pad; - - __be64 ri_data0; /* first data location */ - __be32 ri_data; /* num of data blocks in rgrp */ - - __be32 ri_bitbytes; /* number of bytes in data bitmaps */ - - __u8 ri_reserved[64]; -}; - -/* - * resource group header structure - */ - -/* Number of blocks per byte in rgrp */ -#define GFS2_NBBY 4 -#define GFS2_BIT_SIZE 2 -#define GFS2_BIT_MASK 0x00000003 - -#define GFS2_BLKST_FREE 0 -#define GFS2_BLKST_USED 1 -#define GFS2_BLKST_UNLINKED 2 -#define GFS2_BLKST_DINODE 3 - -#define GFS2_RGF_JOURNAL 0x00000001 -#define GFS2_RGF_METAONLY 0x00000002 -#define GFS2_RGF_DATAONLY 0x00000004 -#define GFS2_RGF_NOALLOC 0x00000008 - -struct gfs2_rgrp { - struct gfs2_meta_header rg_header; - - __be32 rg_flags; - __be32 rg_free; - __be32 rg_dinodes; - __be32 __pad; - __be64 rg_igeneration; - - __u8 rg_reserved[80]; /* Several fields from gfs1 now reserved */ -}; - -/* - * quota structure - */ - -struct gfs2_quota { - __be64 qu_limit; - __be64 qu_warn; - __be64 qu_value; - __u8 qu_reserved[64]; -}; - -/* - * dinode structure - */ - -#define GFS2_MAX_META_HEIGHT 10 -#define GFS2_DIR_MAX_DEPTH 17 - -#define DT2IF(dt) (((dt) << 12) & S_IFMT) -#define IF2DT(sif) (((sif) & S_IFMT) >> 12) - -enum { - gfs2fl_Jdata = 0, - gfs2fl_ExHash = 1, - gfs2fl_Unused = 2, - gfs2fl_EaIndirect = 3, - gfs2fl_Directio = 4, - gfs2fl_Immutable = 5, - gfs2fl_AppendOnly = 6, - gfs2fl_NoAtime = 7, - gfs2fl_Sync = 8, - gfs2fl_System = 9, - gfs2fl_TruncInProg = 29, - gfs2fl_InheritDirectio = 30, - gfs2fl_InheritJdata = 31, -}; - -/* Dinode flags */ -#define GFS2_DIF_JDATA 0x00000001 -#define GFS2_DIF_EXHASH 0x00000002 -#define GFS2_DIF_UNUSED 0x00000004 /* only in gfs1 */ -#define GFS2_DIF_EA_INDIRECT 0x00000008 -#define GFS2_DIF_DIRECTIO 0x00000010 -#define GFS2_DIF_IMMUTABLE 0x00000020 -#define GFS2_DIF_APPENDONLY 0x00000040 -#define GFS2_DIF_NOATIME 0x00000080 -#define GFS2_DIF_SYNC 0x00000100 -#define GFS2_DIF_SYSTEM 0x00000200 /* New in gfs2 */ -#define GFS2_DIF_TRUNC_IN_PROG 0x20000000 /* New in gfs2 */ -#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000 -#define GFS2_DIF_INHERIT_JDATA 0x80000000 - -struct gfs2_dinode { - struct gfs2_meta_header di_header; - - struct gfs2_inum di_num; - - __be32 di_mode; /* mode of file */ - __be32 di_uid; /* owner's user id */ - __be32 di_gid; /* owner's group id */ - __be32 di_nlink; /* number of links to this file */ - __be64 di_size; /* number of bytes in file */ - __be64 di_blocks; /* number of blocks in file */ - __be64 di_atime; /* time last accessed */ - __be64 di_mtime; /* time last modified */ - __be64 di_ctime; /* time last changed */ - __be32 di_major; /* device major number */ - __be32 di_minor; /* device minor number */ - - /* This section varies from gfs1. Padding added to align with - * remainder of dinode - */ - __be64 di_goal_meta; /* rgrp to alloc from next */ - __be64 di_goal_data; /* data block goal */ - __be64 di_generation; /* generation number for NFS */ - - __be32 di_flags; /* GFS2_DIF_... */ - __be32 di_payload_format; /* GFS2_FORMAT_... */ - __u16 __pad1; /* Was ditype in gfs1 */ - __be16 di_height; /* height of metadata */ - __u32 __pad2; /* Unused incarnation number from gfs1 */ - - /* These only apply to directories */ - __u16 __pad3; /* Padding */ - __be16 di_depth; /* Number of bits in the table */ - __be32 di_entries; /* The number of entries in the directory */ - - struct gfs2_inum __pad4; /* Unused even in current gfs1 */ - - __be64 di_eattr; /* extended attribute block number */ - - __u8 di_reserved[56]; -}; - -/* - * directory structure - many of these per directory file - */ - -#define GFS2_FNAMESIZE 255 -#define GFS2_DIRENT_SIZE(name_len) ((sizeof(struct gfs2_dirent) + (name_len) + 7) & ~7) - -struct gfs2_dirent { - struct gfs2_inum de_inum; - __be32 de_hash; - __be16 de_rec_len; - __be16 de_name_len; - __be16 de_type; - __u8 __pad[14]; -}; - -/* - * Header of leaf directory nodes - */ - -struct gfs2_leaf { - struct gfs2_meta_header lf_header; - - __be16 lf_depth; /* Depth of leaf */ - __be16 lf_entries; /* Number of dirents in leaf */ - __be32 lf_dirent_format; /* Format of the dirents */ - __be64 lf_next; /* Next leaf, if overflow */ - - __u8 lf_reserved[64]; -}; - -/* - * Extended attribute header format - */ - -#define GFS2_EA_MAX_NAME_LEN 255 -#define GFS2_EA_MAX_DATA_LEN 65536 - -#define GFS2_EATYPE_UNUSED 0 -#define GFS2_EATYPE_USR 1 -#define GFS2_EATYPE_SYS 2 -#define GFS2_EATYPE_SECURITY 3 - -#define GFS2_EATYPE_LAST 3 -#define GFS2_EATYPE_VALID(x) ((x) <= GFS2_EATYPE_LAST) - -#define GFS2_EAFLAG_LAST 0x01 /* last ea in block */ - -struct gfs2_ea_header { - __be32 ea_rec_len; - __be32 ea_data_len; - __u8 ea_name_len; /* no NULL pointer after the string */ - __u8 ea_type; /* GFS2_EATYPE_... */ - __u8 ea_flags; /* GFS2_EAFLAG_... */ - __u8 ea_num_ptrs; - __u32 __pad; -}; - -/* - * Log header structure - */ - -#define GFS2_LOG_HEAD_UNMOUNT 0x00000001 /* log is clean */ - -struct gfs2_log_header { - struct gfs2_meta_header lh_header; - - __be64 lh_sequence; /* Sequence number of this transaction */ - __be32 lh_flags; /* GFS2_LOG_HEAD_... */ - __be32 lh_tail; /* Block number of log tail */ - __be32 lh_blkno; - __be32 lh_hash; -}; - -/* - * Log type descriptor - */ - -#define GFS2_LOG_DESC_METADATA 300 -/* ld_data1 is the number of metadata blocks in the descriptor. - ld_data2 is unused. */ - -#define GFS2_LOG_DESC_REVOKE 301 -/* ld_data1 is the number of revoke blocks in the descriptor. - ld_data2 is unused. */ - -#define GFS2_LOG_DESC_JDATA 302 -/* ld_data1 is the number of data blocks in the descriptor. - ld_data2 is unused. */ - -struct gfs2_log_descriptor { - struct gfs2_meta_header ld_header; - - __be32 ld_type; /* GFS2_LOG_DESC_... */ - __be32 ld_length; /* Number of buffers in this chunk */ - __be32 ld_data1; /* descriptor-specific field */ - __be32 ld_data2; /* descriptor-specific field */ - - __u8 ld_reserved[32]; -}; - -/* - * Inum Range - * Describe a range of formal inode numbers allocated to - * one machine to assign to inodes. - */ - -#define GFS2_INUM_QUANTUM 1048576 - -struct gfs2_inum_range { - __be64 ir_start; - __be64 ir_length; -}; - -/* - * Statfs change - * Describes an change to the pool of free and allocated - * blocks. - */ - -struct gfs2_statfs_change { - __be64 sc_total; - __be64 sc_free; - __be64 sc_dinodes; -}; - -/* - * Quota change - * Describes an allocation change for a particular - * user or group. - */ - -#define GFS2_QCF_USER 0x00000001 - -struct gfs2_quota_change { - __be64 qc_change; - __be32 qc_flags; /* GFS2_QCF_... */ - __be32 qc_id; -}; - -#ifdef __KERNEL__ -/* Translation functions */ - -extern void gfs2_inum_in(struct gfs2_inum *no, const void *buf); -extern void gfs2_inum_out(const struct gfs2_inum *no, void *buf); -extern void gfs2_sb_in(struct gfs2_sb *sb, const void *buf); -extern void gfs2_rindex_in(struct gfs2_rindex *ri, const void *buf); -extern void gfs2_rindex_out(const struct gfs2_rindex *ri, void *buf); -extern void gfs2_rgrp_in(struct gfs2_rgrp *rg, const void *buf); -extern void gfs2_rgrp_out(const struct gfs2_rgrp *rg, void *buf); -extern void gfs2_quota_in(struct gfs2_quota *qu, const void *buf); -extern void gfs2_quota_out(const struct gfs2_quota *qu, void *buf); -extern void gfs2_dinode_in(struct gfs2_dinode *di, const void *buf); -extern void gfs2_dinode_out(const struct gfs2_dinode *di, void *buf); -extern void gfs2_ea_header_in(struct gfs2_ea_header *ea, const void *buf); -extern void gfs2_ea_header_out(const struct gfs2_ea_header *ea, void *buf); -extern void gfs2_log_header_in(struct gfs2_log_header *lh, const void *buf); -extern void gfs2_inum_range_in(struct gfs2_inum_range *ir, const void *buf); -extern void gfs2_inum_range_out(const struct gfs2_inum_range *ir, void *buf); -extern void gfs2_statfs_change_in(struct gfs2_statfs_change *sc, const void *buf); -extern void gfs2_statfs_change_out(const struct gfs2_statfs_change *sc, void *buf); -extern void gfs2_quota_change_in(struct gfs2_quota_change *qc, const void *buf); - -/* Printing functions */ - -extern void gfs2_rindex_print(const struct gfs2_rindex *ri); -extern void gfs2_dinode_print(const struct gfs2_dinode *di); - -#endif /* __KERNEL__ */ - -#endif /* __GFS2_ONDISK_DOT_H__ */ diff --git a/trunk/include/linux/hardirq.h b/trunk/include/linux/hardirq.h index 612472aaa79c..50d8b5744cf6 100644 --- a/trunk/include/linux/hardirq.h +++ b/trunk/include/linux/hardirq.h @@ -28,16 +28,11 @@ #ifndef HARDIRQ_BITS #define HARDIRQ_BITS 12 - -#ifndef MAX_HARDIRQS_PER_CPU -#define MAX_HARDIRQS_PER_CPU NR_IRQS -#endif - /* * The hardirq mask has to be large enough to have space for potentially * all IRQ sources in the system nesting on a single CPU. */ -#if (1 << HARDIRQ_BITS) < MAX_HARDIRQS_PER_CPU +#if (1 << HARDIRQ_BITS) < NR_IRQS # error HARDIRQ_BITS is too low! #endif #endif diff --git a/trunk/include/linux/htirq.h b/trunk/include/linux/htirq.h deleted file mode 100644 index 1f15ce279a23..000000000000 --- a/trunk/include/linux/htirq.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef LINUX_HTIRQ_H -#define LINUX_HTIRQ_H - -/* Helper functions.. */ -void write_ht_irq_low(unsigned int irq, u32 data); -void write_ht_irq_high(unsigned int irq, u32 data); -u32 read_ht_irq_low(unsigned int irq); -u32 read_ht_irq_high(unsigned int irq); -void mask_ht_irq(unsigned int irq); -void unmask_ht_irq(unsigned int irq); - -/* The arch hook for getting things started */ -int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev); - -#endif /* LINUX_HTIRQ_H */ diff --git a/trunk/include/linux/in.h b/trunk/include/linux/in.h index 2619859f6e1b..d79fc75fa7c2 100644 --- a/trunk/include/linux/in.h +++ b/trunk/include/linux/in.h @@ -40,7 +40,6 @@ enum { IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */ IPPROTO_AH = 51, /* Authentication Header protocol */ - IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ IPPROTO_PIM = 103, /* Protocol Independent Multicast */ IPPROTO_COMP = 108, /* Compression Header protocol */ diff --git a/trunk/include/linux/ip.h b/trunk/include/linux/ip.h index ecee9bb27d0e..6b25d36fc54c 100644 --- a/trunk/include/linux/ip.h +++ b/trunk/include/linux/ip.h @@ -80,8 +80,6 @@ #define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ #define IPOPT_TS_PRESPEC 3 /* specified modules only */ -#define IPV4_BEET_PHMAXLEN 8 - struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, @@ -125,11 +123,4 @@ struct ip_comp_hdr { __be16 cpi; }; -struct ip_beet_phdr { - __u8 nexthdr; - __u8 hdrlen; - __u8 padlen; - __u8 reserved; -}; - #endif /* _LINUX_IP_H */ diff --git a/trunk/include/linux/ipc.h b/trunk/include/linux/ipc.h index 636094c29b16..d9e2b3f36c35 100644 --- a/trunk/include/linux/ipc.h +++ b/trunk/include/linux/ipc.h @@ -2,6 +2,7 @@ #define _LINUX_IPC_H #include +#include #define IPC_PRIVATE ((__kernel_key_t) 0) @@ -51,8 +52,6 @@ struct ipc_perm #ifdef __KERNEL__ -#include - #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ /* used by in-kernel data structures */ diff --git a/trunk/include/linux/ipsec.h b/trunk/include/linux/ipsec.h index d17a6302a0e9..d3c527616b5e 100644 --- a/trunk/include/linux/ipsec.h +++ b/trunk/include/linux/ipsec.h @@ -12,8 +12,7 @@ enum { IPSEC_MODE_ANY = 0, /* We do not support this for SA */ IPSEC_MODE_TRANSPORT = 1, - IPSEC_MODE_TUNNEL = 2, - IPSEC_MODE_BEET = 3 + IPSEC_MODE_TUNNEL = 2 }; enum { diff --git a/trunk/include/linux/irq.h b/trunk/include/linux/irq.h index 6f463606c318..48d3cb3b6a47 100644 --- a/trunk/include/linux/irq.h +++ b/trunk/include/linux/irq.h @@ -59,7 +59,6 @@ #define IRQ_NOAUTOEN 0x08000000 /* IRQ will not be enabled on request irq */ #define IRQ_DELAYED_DISABLE 0x10000000 /* IRQ disable (masking) happens delayed. */ #define IRQ_WAKEUP 0x20000000 /* IRQ triggers system wakeup */ -#define IRQ_MOVE_PENDING 0x40000000 /* need to re-target IRQ destination */ struct proc_dir_entry; @@ -133,6 +132,7 @@ struct irq_chip { * @affinity: IRQ affinity on SMP * @cpu: cpu index useful for balancing * @pending_mask: pending rebalanced interrupts + * @move_irq: need to re-target IRQ destination * @dir: /proc/irq/ procfs entry * @affinity_entry: /proc/irq/smp_affinity procfs entry on SMP * @@ -159,6 +159,7 @@ struct irq_desc { #endif #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) cpumask_t pending_mask; + unsigned int move_irq; /* need to re-target IRQ dest */ #endif #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; @@ -205,7 +206,36 @@ static inline void set_native_irq_info(int irq, cpumask_t mask) void set_pending_irq(unsigned int irq, cpumask_t mask); void move_native_irq(int irq); -void move_masked_irq(int irq); + +#ifdef CONFIG_PCI_MSI +/* + * Wonder why these are dummies? + * For e.g the set_ioapic_affinity_vector() calls the set_ioapic_affinity_irq() + * counter part after translating the vector to irq info. We need to perform + * this operation on the real irq, when we dont use vector, i.e when + * pci_use_vector() is false. + */ +static inline void move_irq(int irq) +{ +} + +static inline void set_irq_info(int irq, cpumask_t mask) +{ +} + +#else /* CONFIG_PCI_MSI */ + +static inline void move_irq(int irq) +{ + move_native_irq(irq); +} + +static inline void set_irq_info(int irq, cpumask_t mask) +{ + set_native_irq_info(irq, mask); +} + +#endif /* CONFIG_PCI_MSI */ #else /* CONFIG_GENERIC_PENDING_IRQ || CONFIG_IRQBALANCE */ @@ -217,20 +247,21 @@ static inline void move_native_irq(int irq) { } -static inline void move_masked_irq(int irq) +static inline void set_pending_irq(unsigned int irq, cpumask_t mask) { } -static inline void set_pending_irq(unsigned int irq, cpumask_t mask) +static inline void set_irq_info(int irq, cpumask_t mask) { + set_native_irq_info(irq, mask); } #endif /* CONFIG_GENERIC_PENDING_IRQ */ #else /* CONFIG_SMP */ +#define move_irq(x) #define move_native_irq(x) -#define move_masked_irq(x) #endif /* CONFIG_SMP */ @@ -368,22 +399,8 @@ set_irq_chained_handler(unsigned int irq, __set_irq_handler(irq, handle, 1); } -/* Handle dynamic irq creation and destruction */ -extern int create_irq(void); -extern void destroy_irq(unsigned int irq); - -/* Test to see if a driver has successfully requested an irq */ -static inline int irq_has_action(unsigned int irq) -{ - struct irq_desc *desc = irq_desc + irq; - return desc->action != NULL; -} - -/* Dynamic irq helper functions */ -extern void dynamic_irq_init(unsigned int irq); -extern void dynamic_irq_cleanup(unsigned int irq); - /* Set/get chip/data for an IRQ: */ + extern int set_irq_chip(unsigned int irq, struct irq_chip *chip); extern int set_irq_data(unsigned int irq, void *data); extern int set_irq_chip_data(unsigned int irq, void *data); diff --git a/trunk/include/linux/libata.h b/trunk/include/linux/libata.h index d1af1dbeaeb4..d6a3d4b345fc 100644 --- a/trunk/include/linux/libata.h +++ b/trunk/include/linux/libata.h @@ -109,10 +109,6 @@ static inline u32 ata_msg_init(int dval, int default_msg_enable_bits) #define ATA_TAG_POISON 0xfafbfcfdU /* move to PCI layer? */ -#define PCI_VDEVICE(vendor, device) \ - PCI_VENDOR_ID_##vendor, (device), \ - PCI_ANY_ID, PCI_ANY_ID, 0, 0 - static inline struct device *pci_dev_to_dev(struct pci_dev *pdev) { return &pdev->dev; @@ -142,9 +138,8 @@ enum { ATA_DFLAG_NCQ = (1 << 3), /* device supports NCQ */ ATA_DFLAG_CFG_MASK = (1 << 8) - 1, - ATA_DFLAG_PIO = (1 << 8), /* device limited to PIO mode */ - ATA_DFLAG_NCQ_OFF = (1 << 9), /* devied limited to non-NCQ mode */ - ATA_DFLAG_SUSPENDED = (1 << 10), /* device suspended */ + ATA_DFLAG_PIO = (1 << 8), /* device currently in PIO mode */ + ATA_DFLAG_SUSPENDED = (1 << 9), /* device suspended */ ATA_DFLAG_INIT_MASK = (1 << 16) - 1, ATA_DFLAG_DETACH = (1 << 16), diff --git a/trunk/include/linux/lm_interface.h b/trunk/include/linux/lm_interface.h deleted file mode 100644 index 1418fdc9ac02..000000000000 --- a/trunk/include/linux/lm_interface.h +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2006 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 version 2. - */ - -#ifndef __LM_INTERFACE_DOT_H__ -#define __LM_INTERFACE_DOT_H__ - - -typedef void (*lm_callback_t) (void *ptr, unsigned int type, void *data); - -/* - * lm_mount() flags - * - * LM_MFLAG_SPECTATOR - * GFS is asking to join the filesystem's lockspace, but it doesn't want to - * modify the filesystem. The lock module shouldn't assign a journal to the FS - * mount. It shouldn't send recovery callbacks to the FS mount. If the node - * dies or withdraws, all locks can be wiped immediately. - */ - -#define LM_MFLAG_SPECTATOR 0x00000001 - -/* - * lm_lockstruct flags - * - * LM_LSFLAG_LOCAL - * The lock_nolock module returns LM_LSFLAG_LOCAL to GFS, indicating that GFS - * can make single-node optimizations. - */ - -#define LM_LSFLAG_LOCAL 0x00000001 - -/* - * lm_lockname types - */ - -#define LM_TYPE_RESERVED 0x00 -#define LM_TYPE_NONDISK 0x01 -#define LM_TYPE_INODE 0x02 -#define LM_TYPE_RGRP 0x03 -#define LM_TYPE_META 0x04 -#define LM_TYPE_IOPEN 0x05 -#define LM_TYPE_FLOCK 0x06 -#define LM_TYPE_PLOCK 0x07 -#define LM_TYPE_QUOTA 0x08 -#define LM_TYPE_JOURNAL 0x09 - -/* - * lm_lock() states - * - * SHARED is compatible with SHARED, not with DEFERRED or EX. - * DEFERRED is compatible with DEFERRED, not with SHARED or EX. - */ - -#define LM_ST_UNLOCKED 0 -#define LM_ST_EXCLUSIVE 1 -#define LM_ST_DEFERRED 2 -#define LM_ST_SHARED 3 - -/* - * lm_lock() flags - * - * LM_FLAG_TRY - * Don't wait to acquire the lock if it can't be granted immediately. - * - * LM_FLAG_TRY_1CB - * Send one blocking callback if TRY is set and the lock is not granted. - * - * LM_FLAG_NOEXP - * GFS sets this flag on lock requests it makes while doing journal recovery. - * These special requests should not be blocked due to the recovery like - * ordinary locks would be. - * - * LM_FLAG_ANY - * A SHARED request may also be granted in DEFERRED, or a DEFERRED request may - * also be granted in SHARED. The preferred state is whichever is compatible - * with other granted locks, or the specified state if no other locks exist. - * - * LM_FLAG_PRIORITY - * Override fairness considerations. Suppose a lock is held in a shared state - * and there is a pending request for the deferred state. A shared lock - * request with the priority flag would be allowed to bypass the deferred - * request and directly join the other shared lock. A shared lock request - * without the priority flag might be forced to wait until the deferred - * requested had acquired and released the lock. - */ - -#define LM_FLAG_TRY 0x00000001 -#define LM_FLAG_TRY_1CB 0x00000002 -#define LM_FLAG_NOEXP 0x00000004 -#define LM_FLAG_ANY 0x00000008 -#define LM_FLAG_PRIORITY 0x00000010 - -/* - * lm_lock() and lm_async_cb return flags - * - * LM_OUT_ST_MASK - * Masks the lower two bits of lock state in the returned value. - * - * LM_OUT_CACHEABLE - * The lock hasn't been released so GFS can continue to cache data for it. - * - * LM_OUT_CANCELED - * The lock request was canceled. - * - * LM_OUT_ASYNC - * The result of the request will be returned in an LM_CB_ASYNC callback. - */ - -#define LM_OUT_ST_MASK 0x00000003 -#define LM_OUT_CACHEABLE 0x00000004 -#define LM_OUT_CANCELED 0x00000008 -#define LM_OUT_ASYNC 0x00000080 -#define LM_OUT_ERROR 0x00000100 - -/* - * lm_callback_t types - * - * LM_CB_NEED_E LM_CB_NEED_D LM_CB_NEED_S - * Blocking callback, a remote node is requesting the given lock in - * EXCLUSIVE, DEFERRED, or SHARED. - * - * LM_CB_NEED_RECOVERY - * The given journal needs to be recovered. - * - * LM_CB_DROPLOCKS - * Reduce the number of cached locks. - * - * LM_CB_ASYNC - * The given lock has been granted. - */ - -#define LM_CB_NEED_E 257 -#define LM_CB_NEED_D 258 -#define LM_CB_NEED_S 259 -#define LM_CB_NEED_RECOVERY 260 -#define LM_CB_DROPLOCKS 261 -#define LM_CB_ASYNC 262 - -/* - * lm_recovery_done() messages - */ - -#define LM_RD_GAVEUP 308 -#define LM_RD_SUCCESS 309 - - -struct lm_lockname { - u64 ln_number; - unsigned int ln_type; -}; - -#define lm_name_equal(name1, name2) \ - (((name1)->ln_number == (name2)->ln_number) && \ - ((name1)->ln_type == (name2)->ln_type)) \ - -struct lm_async_cb { - struct lm_lockname lc_name; - int lc_ret; -}; - -struct lm_lockstruct; - -struct lm_lockops { - const char *lm_proto_name; - - /* - * Mount/Unmount - */ - - int (*lm_mount) (char *table_name, char *host_data, - lm_callback_t cb, void *cb_data, - unsigned int min_lvb_size, int flags, - struct lm_lockstruct *lockstruct, - struct kobject *fskobj); - - void (*lm_others_may_mount) (void *lockspace); - - void (*lm_unmount) (void *lockspace); - - void (*lm_withdraw) (void *lockspace); - - /* - * Lock oriented operations - */ - - int (*lm_get_lock) (void *lockspace, struct lm_lockname *name, void **lockp); - - void (*lm_put_lock) (void *lock); - - unsigned int (*lm_lock) (void *lock, unsigned int cur_state, - unsigned int req_state, unsigned int flags); - - unsigned int (*lm_unlock) (void *lock, unsigned int cur_state); - - void (*lm_cancel) (void *lock); - - int (*lm_hold_lvb) (void *lock, char **lvbp); - void (*lm_unhold_lvb) (void *lock, char *lvb); - - /* - * Posix Lock oriented operations - */ - - int (*lm_plock_get) (void *lockspace, struct lm_lockname *name, - struct file *file, struct file_lock *fl); - - int (*lm_plock) (void *lockspace, struct lm_lockname *name, - struct file *file, int cmd, struct file_lock *fl); - - int (*lm_punlock) (void *lockspace, struct lm_lockname *name, - struct file *file, struct file_lock *fl); - - /* - * Client oriented operations - */ - - void (*lm_recovery_done) (void *lockspace, unsigned int jid, - unsigned int message); - - struct module *lm_owner; -}; - -/* - * lm_mount() return values - * - * ls_jid - the journal ID this node should use - * ls_first - this node is the first to mount the file system - * ls_lvb_size - size in bytes of lock value blocks - * ls_lockspace - lock module's context for this file system - * ls_ops - lock module's functions - * ls_flags - lock module features - */ - -struct lm_lockstruct { - unsigned int ls_jid; - unsigned int ls_first; - unsigned int ls_lvb_size; - void *ls_lockspace; - const struct lm_lockops *ls_ops; - int ls_flags; -}; - -/* - * Lock module bottom interface. A lock module makes itself available to GFS - * with these functions. - */ - -int gfs2_register_lockproto(const struct lm_lockops *proto); -void gfs2_unregister_lockproto(const struct lm_lockops *proto); - -/* - * Lock module top interface. GFS calls these functions when mounting or - * unmounting a file system. - */ - -int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data, - lm_callback_t cb, void *cb_data, - unsigned int min_lvb_size, int flags, - struct lm_lockstruct *lockstruct, - struct kobject *fskobj); - -void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct); - -void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct); - -#endif /* __LM_INTERFACE_DOT_H__ */ - diff --git a/trunk/include/linux/lock_dlm_plock.h b/trunk/include/linux/lock_dlm_plock.h deleted file mode 100644 index fc3415113973..000000000000 --- a/trunk/include/linux/lock_dlm_plock.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -#ifndef __LOCK_DLM_PLOCK_DOT_H__ -#define __LOCK_DLM_PLOCK_DOT_H__ - -#define GDLM_PLOCK_MISC_NAME "lock_dlm_plock" - -#define GDLM_PLOCK_VERSION_MAJOR 1 -#define GDLM_PLOCK_VERSION_MINOR 1 -#define GDLM_PLOCK_VERSION_PATCH 0 - -enum { - GDLM_PLOCK_OP_LOCK = 1, - GDLM_PLOCK_OP_UNLOCK, - GDLM_PLOCK_OP_GET, -}; - -struct gdlm_plock_info { - __u32 version[3]; - __u8 optype; - __u8 ex; - __u8 wait; - __u8 pad; - __u32 pid; - __s32 nodeid; - __s32 rv; - __u32 fsid; - __u64 number; - __u64 start; - __u64 end; - __u64 owner; -}; - -#endif - diff --git a/trunk/include/linux/lockd/lockd.h b/trunk/include/linux/lockd/lockd.h index 2909619c0295..47b7dbd647a6 100644 --- a/trunk/include/linux/lockd/lockd.h +++ b/trunk/include/linux/lockd/lockd.h @@ -37,15 +37,17 @@ * Lockd host handle (used both by the client and server personality). */ struct nlm_host { - struct hlist_node h_hash; /* doubly linked list */ + struct nlm_host * h_next; /* linked list (hash table) */ struct sockaddr_in h_addr; /* peer address */ struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ - char * h_name; /* remote hostname */ + char h_name[20]; /* remote hostname */ u32 h_version; /* interface version */ unsigned short h_proto; /* transport proto */ unsigned short h_reclaiming : 1, h_server : 1, /* server side, not client side */ - h_inuse : 1; + h_inuse : 1, + h_killed : 1, + h_monitored : 1; wait_queue_head_t h_gracewait; /* wait while reclaiming */ struct rw_semaphore h_rwsem; /* Reboot recovery lock */ u32 h_state; /* pseudo-state counter */ @@ -59,16 +61,6 @@ struct nlm_host { spinlock_t h_lock; struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ - struct nsm_handle * h_nsmhandle; /* NSM status handle */ -}; - -struct nsm_handle { - struct list_head sm_link; - atomic_t sm_count; - char * sm_name; - struct sockaddr_in sm_addr; - unsigned int sm_monitored : 1, - sm_sticky : 1; /* don't unmonitor */ }; /* @@ -104,14 +96,15 @@ struct nlm_rqst { * an NFS client. */ struct nlm_file { - struct hlist_node f_list; /* linked list */ + struct nlm_file * f_next; /* linked list */ struct nfs_fh f_handle; /* NFS file handle */ struct file * f_file; /* VFS file pointer */ struct nlm_share * f_shares; /* DOS shares */ - struct list_head f_blocks; /* blocked locks */ + struct nlm_block * f_blocks; /* blocked locks */ unsigned int f_locks; /* guesstimate # of locks */ unsigned int f_count; /* reference count */ - struct mutex f_mutex; /* avoid concurrent access */ + struct semaphore f_sema; /* avoid concurrent access */ + int f_hash; /* hash of f_handle */ }; /* @@ -121,17 +114,25 @@ struct nlm_file { #define NLM_NEVER (~(unsigned long) 0) struct nlm_block { struct kref b_count; /* Reference count */ - struct list_head b_list; /* linked list of all blocks */ - struct list_head b_flist; /* linked list (per file) */ + struct nlm_block * b_next; /* linked list (all blocks) */ + struct nlm_block * b_fnext; /* linked list (per file) */ struct nlm_rqst * b_call; /* RPC args & callback info */ struct svc_serv * b_daemon; /* NLM service */ struct nlm_host * b_host; /* host handle for RPC clnt */ unsigned long b_when; /* next re-xmit */ unsigned int b_id; /* block id */ + unsigned char b_queued; /* re-queued */ unsigned char b_granted; /* VFS granted lock */ struct nlm_file * b_file; /* file in question */ }; +/* + * Valid actions for nlmsvc_traverse_files + */ +#define NLM_ACT_CHECK 0 /* check for locks */ +#define NLM_ACT_MARK 1 /* mark & sweep */ +#define NLM_ACT_UNLOCK 2 /* release all locks */ + /* * Global variables */ @@ -142,7 +143,6 @@ extern struct svc_procedure nlmsvc_procedures4[]; #endif extern int nlmsvc_grace_period; extern unsigned long nlmsvc_timeout; -extern int nsm_use_hostnames; /* * Lockd client functions @@ -155,31 +155,22 @@ struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock void nlmclnt_finish_block(struct nlm_wait *block); int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *); -void nlmclnt_recovery(struct nlm_host *); +void nlmclnt_recovery(struct nlm_host *, u32); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); -void nlmclnt_next_cookie(struct nlm_cookie *); /* * Host cache */ -struct nlm_host * nlmclnt_lookup_host(const struct sockaddr_in *, int, int, const char *, int); -struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *, const char *, int); -struct nlm_host * nlm_lookup_host(int server, const struct sockaddr_in *, int, int, const char *, int); +struct nlm_host * nlmclnt_lookup_host(struct sockaddr_in *, int, int); +struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *); +struct nlm_host * nlm_lookup_host(int server, struct sockaddr_in *, int, int); struct rpc_clnt * nlm_bind_host(struct nlm_host *); void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *); void nlm_release_host(struct nlm_host *); void nlm_shutdown_hosts(void); -extern void nlm_host_rebooted(const struct sockaddr_in *, const char *, int, u32); -struct nsm_handle *nsm_find(const struct sockaddr_in *, const char *, int); -void nsm_release(struct nsm_handle *); - +extern struct nlm_host *nlm_find_client(void); -/* - * This is used in garbage collection and resource reclaim - * A return value != 0 means destroy the lock/block/share - */ -typedef int (*nlm_host_match_fn_t)(struct nlm_host *cur, struct nlm_host *ref); /* * Server-side lock handling @@ -192,8 +183,8 @@ u32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); unsigned long nlmsvc_retry_blocked(void); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, - nlm_host_match_fn_t match); -void nlmsvc_grant_reply(struct nlm_cookie *, u32); + int action); +void nlmsvc_grant_reply(struct svc_rqst *, struct nlm_cookie *, u32); /* * File handling for the server personality diff --git a/trunk/include/linux/lockd/share.h b/trunk/include/linux/lockd/share.h index cd7816e74c05..c75a424ebe4c 100644 --- a/trunk/include/linux/lockd/share.h +++ b/trunk/include/linux/lockd/share.h @@ -25,7 +25,6 @@ u32 nlmsvc_share_file(struct nlm_host *, struct nlm_file *, struct nlm_args *); u32 nlmsvc_unshare_file(struct nlm_host *, struct nlm_file *, struct nlm_args *); -void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, - nlm_host_match_fn_t); +void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *, int); #endif /* LINUX_LOCKD_SHARE_H */ diff --git a/trunk/include/linux/lockd/sm_inter.h b/trunk/include/linux/lockd/sm_inter.h index fc61d40964da..1080bb6ae315 100644 --- a/trunk/include/linux/lockd/sm_inter.h +++ b/trunk/include/linux/lockd/sm_inter.h @@ -28,8 +28,7 @@ struct nsm_args { u32 prog; /* RPC callback info */ u32 vers; u32 proc; - - char * mon_name; + u32 proto; /* protocol (udp/tcp) plus server/client flag */ }; /* @@ -42,6 +41,6 @@ struct nsm_res { int nsm_monitor(struct nlm_host *); int nsm_unmonitor(struct nlm_host *); -extern int nsm_local_state; +extern u32 nsm_local_state; #endif /* LINUX_LOCKD_SM_INTER_H */ diff --git a/trunk/include/linux/msi.h b/trunk/include/linux/msi.h deleted file mode 100644 index c7ef94343673..000000000000 --- a/trunk/include/linux/msi.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef LINUX_MSI_H -#define LINUX_MSI_H - -struct msi_msg { - u32 address_lo; /* low 32 bits of msi message address */ - u32 address_hi; /* high 32 bits of msi message address */ - u32 data; /* 16 bits of msi message data */ -}; - -/* Heper functions */ -extern void mask_msi_irq(unsigned int irq); -extern void unmask_msi_irq(unsigned int irq); -extern void read_msi_msg(unsigned int irq, struct msi_msg *msg); - -extern void write_msi_msg(unsigned int irq, struct msi_msg *msg); - -struct msi_desc { - struct { - __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ - __u8 maskbit : 1; /* mask-pending bit supported ? */ - __u8 unused : 1; - __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ - __u8 pos; /* Location of the msi capability */ - __u16 entry_nr; /* specific enabled entry */ - unsigned default_irq; /* default pre-assigned irq */ - }msi_attrib; - - struct { - __u16 head; - __u16 tail; - }link; - - void __iomem *mask_base; - struct pci_dev *dev; - -#ifdef CONFIG_PM - /* PM save area for MSIX address/data */ - struct msi_msg msg_save; -#endif -}; - -/* - * The arch hook for setup up msi irqs - */ -int arch_setup_msi_irq(unsigned int irq, struct pci_dev *dev); -void arch_teardown_msi_irq(unsigned int irq); - - -#endif /* LINUX_MSI_H */ diff --git a/trunk/include/linux/netfilter_bridge/ebt_mark_t.h b/trunk/include/linux/netfilter_bridge/ebt_mark_t.h index 6270f6f33693..110fec6a40a2 100644 --- a/trunk/include/linux/netfilter_bridge/ebt_mark_t.h +++ b/trunk/include/linux/netfilter_bridge/ebt_mark_t.h @@ -1,18 +1,6 @@ #ifndef __LINUX_BRIDGE_EBT_MARK_T_H #define __LINUX_BRIDGE_EBT_MARK_T_H -/* The target member is reused for adding new actions, the - * value of the real target is -1 to -NUM_STANDARD_TARGETS. - * For backward compatibility, the 4 lsb (2 would be enough, - * but let's play it safe) are kept to designate this target. - * The remaining bits designate the action. By making the set - * action 0xfffffff0, the result will look ok for older - * versions. [September 2006] */ -#define MARK_SET_VALUE (0xfffffff0) -#define MARK_OR_VALUE (0xffffffe0) -#define MARK_AND_VALUE (0xffffffd0) -#define MARK_XOR_VALUE (0xffffffc0) - struct ebt_mark_t_info { unsigned long mark; diff --git a/trunk/include/linux/netfilter_ipv4.h b/trunk/include/linux/netfilter_ipv4.h index 5b63a231a76b..ce02c984f3ba 100644 --- a/trunk/include/linux/netfilter_ipv4.h +++ b/trunk/include/linux/netfilter_ipv4.h @@ -77,7 +77,7 @@ enum nf_ip_hook_priorities { #define SO_ORIGINAL_DST 80 #ifdef __KERNEL__ -extern int ip_route_me_harder(struct sk_buff **pskb, unsigned addr_type); +extern int ip_route_me_harder(struct sk_buff **pskb); extern int ip_xfrm_me_harder(struct sk_buff **pskb); extern unsigned int nf_ip_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol); diff --git a/trunk/include/linux/nfsd/const.h b/trunk/include/linux/nfsd/const.h index f0cc77790527..b75bb1b38d09 100644 --- a/trunk/include/linux/nfsd/const.h +++ b/trunk/include/linux/nfsd/const.h @@ -20,31 +20,17 @@ #define NFSSVC_MAXVERS 3 /* - * Maximum blocksizes supported by daemon under various circumstances. + * Maximum blocksize supported by daemon currently at 32K */ -#define NFSSVC_MAXBLKSIZE RPCSVC_MAXPAYLOAD -/* NFSv2 is limited by the protocol specification, see RFC 1094 */ -#define NFSSVC_MAXBLKSIZE_V2 (8*1024) +#define NFSSVC_MAXBLKSIZE (32*1024) #ifdef __KERNEL__ -#include - #ifndef NFS_SUPER_MAGIC # define NFS_SUPER_MAGIC 0x6969 #endif -/* - * Largest number of bytes we need to allocate for an NFS - * call or reply. Used to control buffer sizes. We use - * the length of v3 WRITE, READDIR and READDIR replies - * which are an RPC header, up to 26 XDR units of reply - * data, and some page data. - * - * Note that accuracy here doesn't matter too much as the - * size is rounded up to a page size when allocating space. - */ -#define NFSD_BUFSIZE ((RPC_MAX_HEADER_WITH_AUTH+26)*XDR_UNIT + NFSSVC_MAXBLKSIZE) +#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE) #ifdef CONFIG_NFSD_V4 # define NFSSVC_XDRSIZE NFS4_SVC_XDRSIZE diff --git a/trunk/include/linux/nfsd/export.h b/trunk/include/linux/nfsd/export.h index 6e78ea969f49..d2a8abb5011a 100644 --- a/trunk/include/linux/nfsd/export.h +++ b/trunk/include/linux/nfsd/export.h @@ -45,36 +45,15 @@ #ifdef __KERNEL__ -/* - * FS Locations - */ - -#define MAX_FS_LOCATIONS 128 - -struct nfsd4_fs_location { - char *hosts; /* colon separated list of hosts */ - char *path; /* slash separated list of path components */ -}; - -struct nfsd4_fs_locations { - uint32_t locations_count; - struct nfsd4_fs_location *locations; -/* If we're not actually serving this data ourselves (only providing a - * list of replicas that do serve it) then we set "migrated": */ - int migrated; -}; - struct svc_export { struct cache_head h; struct auth_domain * ex_client; int ex_flags; struct vfsmount * ex_mnt; struct dentry * ex_dentry; - char * ex_path; uid_t ex_anon_uid; gid_t ex_anon_gid; int ex_fsid; - struct nfsd4_fs_locations ex_fslocs; }; /* an "export key" (expkey) maps a filehandlefragement to an diff --git a/trunk/include/linux/nfsd/nfsd.h b/trunk/include/linux/nfsd/nfsd.h index d0d4aae7085f..e1dbc86c270b 100644 --- a/trunk/include/linux/nfsd/nfsd.h +++ b/trunk/include/linux/nfsd/nfsd.h @@ -145,7 +145,6 @@ int nfsd_vers(int vers, enum vers_op change); void nfsd_reset_versions(void); int nfsd_create_serv(void); -extern int nfsd_max_blksize; /* * NFSv4 State @@ -216,7 +215,6 @@ void nfsd_lockd_shutdown(void); #define nfserr_clid_inuse __constant_htonl(NFSERR_CLID_INUSE) #define nfserr_stale_clientid __constant_htonl(NFSERR_STALE_CLIENTID) #define nfserr_resource __constant_htonl(NFSERR_RESOURCE) -#define nfserr_moved __constant_htonl(NFSERR_MOVED) #define nfserr_nofilehandle __constant_htonl(NFSERR_NOFILEHANDLE) #define nfserr_minor_vers_mismatch __constant_htonl(NFSERR_MINOR_VERS_MISMATCH) #define nfserr_share_denied __constant_htonl(NFSERR_SHARE_DENIED) @@ -293,6 +291,7 @@ static inline int is_fsid(struct svc_fh *fh, struct knfsd_fh *reffh) /* * The following attributes are currently not supported by the NFSv4 server: * ARCHIVE (deprecated anyway) + * FS_LOCATIONS (will be supported eventually) * HIDDEN (unlikely to be supported any time soon) * MIMETYPE (unlikely to be supported any time soon) * QUOTA_* (will be supported in a forthcoming patch) @@ -308,7 +307,7 @@ static inline int is_fsid(struct svc_fh *fh, struct knfsd_fh *reffh) | FATTR4_WORD0_ACLSUPPORT | FATTR4_WORD0_CANSETTIME | FATTR4_WORD0_CASE_INSENSITIVE \ | FATTR4_WORD0_CASE_PRESERVING | FATTR4_WORD0_CHOWN_RESTRICTED \ | FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FILEID | FATTR4_WORD0_FILES_AVAIL \ - | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_HOMOGENEOUS \ + | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_HOMOGENEOUS \ | FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \ | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL) diff --git a/trunk/include/linux/nfsd/xdr.h b/trunk/include/linux/nfsd/xdr.h index 0e53de87d886..a38f9d776de9 100644 --- a/trunk/include/linux/nfsd/xdr.h +++ b/trunk/include/linux/nfsd/xdr.h @@ -30,6 +30,7 @@ struct nfsd_readargs { struct svc_fh fh; __u32 offset; __u32 count; + struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; @@ -37,6 +38,7 @@ struct nfsd_writeargs { svc_fh fh; __u32 offset; int len; + struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; diff --git a/trunk/include/linux/nfsd/xdr3.h b/trunk/include/linux/nfsd/xdr3.h index 474d882dc2f3..a4322741f8b9 100644 --- a/trunk/include/linux/nfsd/xdr3.h +++ b/trunk/include/linux/nfsd/xdr3.h @@ -33,6 +33,7 @@ struct nfsd3_readargs { struct svc_fh fh; __u64 offset; __u32 count; + struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; @@ -42,6 +43,7 @@ struct nfsd3_writeargs { __u32 count; int stable; __u32 len; + struct kvec vec[RPCSVC_MAXPAGES]; int vlen; }; diff --git a/trunk/include/linux/nfsd/xdr4.h b/trunk/include/linux/nfsd/xdr4.h index 66e642762a07..77adba7d2281 100644 --- a/trunk/include/linux/nfsd/xdr4.h +++ b/trunk/include/linux/nfsd/xdr4.h @@ -241,6 +241,7 @@ struct nfsd4_read { stateid_t rd_stateid; /* request */ u64 rd_offset; /* request */ u32 rd_length; /* request */ + struct kvec rd_iov[RPCSVC_MAXPAGES]; int rd_vlen; struct file *rd_filp; @@ -325,6 +326,7 @@ struct nfsd4_write { u64 wr_offset; /* request */ u32 wr_stable_how; /* request */ u32 wr_buflen; /* request */ + struct kvec wr_vec[RPCSVC_MAXPAGES]; /* request */ int wr_vlen; u32 wr_bytes_written; /* response */ diff --git a/trunk/include/linux/notifier.h b/trunk/include/linux/notifier.h index 10a43ed0527e..7ff386a6ae87 100644 --- a/trunk/include/linux/notifier.h +++ b/trunk/include/linux/notifier.h @@ -12,10 +12,9 @@ #include #include #include -#include /* - * Notifier chains are of four types: + * Notifier chains are of three types: * * Atomic notifier chains: Chain callbacks run in interrupt/atomic * context. Callouts are not allowed to block. @@ -24,27 +23,13 @@ * Raw notifier chains: There are no restrictions on callbacks, * registration, or unregistration. All locking and protection * must be provided by the caller. - * SRCU notifier chains: A variant of blocking notifier chains, with - * the same restrictions. * * atomic_notifier_chain_register() may be called from an atomic context, - * but blocking_notifier_chain_register() and srcu_notifier_chain_register() - * must be called from a process context. Ditto for the corresponding - * _unregister() routines. + * but blocking_notifier_chain_register() must be called from a process + * context. Ditto for the corresponding _unregister() routines. * - * atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(), - * and srcu_notifier_chain_unregister() _must not_ be called from within - * the call chain. - * - * SRCU notifier chains are an alternative form of blocking notifier chains. - * They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for - * protection of the chain links. This means there is _very_ low overhead - * in srcu_notifier_call_chain(): no cache bounces and no memory barriers. - * As compensation, srcu_notifier_chain_unregister() is rather expensive. - * SRCU notifier chains should be used when the chain will be called very - * often but notifier_blocks will seldom be removed. Also, SRCU notifier - * chains are slightly more difficult to use because they require special - * runtime initialization. + * atomic_notifier_chain_unregister() and blocking_notifier_chain_unregister() + * _must not_ be called from within the call chain. */ struct notifier_block { @@ -67,12 +52,6 @@ struct raw_notifier_head { struct notifier_block *head; }; -struct srcu_notifier_head { - struct mutex mutex; - struct srcu_struct srcu; - struct notifier_block *head; -}; - #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \ spin_lock_init(&(name)->lock); \ (name)->head = NULL; \ @@ -85,11 +64,6 @@ struct srcu_notifier_head { (name)->head = NULL; \ } while (0) -/* srcu_notifier_heads must be initialized and cleaned up dynamically */ -extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); -#define srcu_cleanup_notifier_head(name) \ - cleanup_srcu_struct(&(name)->srcu); - #define ATOMIC_NOTIFIER_INIT(name) { \ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .head = NULL } @@ -98,7 +72,6 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); .head = NULL } #define RAW_NOTIFIER_INIT(name) { \ .head = NULL } -/* srcu_notifier_heads cannot be initialized statically */ #define ATOMIC_NOTIFIER_HEAD(name) \ struct atomic_notifier_head name = \ @@ -118,8 +91,6 @@ extern int blocking_notifier_chain_register(struct blocking_notifier_head *, struct notifier_block *); extern int raw_notifier_chain_register(struct raw_notifier_head *, struct notifier_block *); -extern int srcu_notifier_chain_register(struct srcu_notifier_head *, - struct notifier_block *); extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *, struct notifier_block *); @@ -127,8 +98,6 @@ extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *, struct notifier_block *); extern int raw_notifier_chain_unregister(struct raw_notifier_head *, struct notifier_block *); -extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *, - struct notifier_block *); extern int atomic_notifier_call_chain(struct atomic_notifier_head *, unsigned long val, void *v); @@ -136,8 +105,6 @@ extern int blocking_notifier_call_chain(struct blocking_notifier_head *, unsigned long val, void *v); extern int raw_notifier_call_chain(struct raw_notifier_head *, unsigned long val, void *v); -extern int srcu_notifier_call_chain(struct srcu_notifier_head *, - unsigned long val, void *v); #define NOTIFY_DONE 0x0000 /* Don't care */ #define NOTIFY_OK 0x0001 /* Suits me */ diff --git a/trunk/include/linux/pci.h b/trunk/include/linux/pci.h index 5c604f5fad67..4431ce4e1e6f 100644 --- a/trunk/include/linux/pci.h +++ b/trunk/include/linux/pci.h @@ -595,7 +595,6 @@ struct msix_entry { u16 entry; /* driver uses to specify entry, OS writes */ }; - #ifndef CONFIG_PCI_MSI static inline void pci_scan_msi_device(struct pci_dev *dev) {} static inline int pci_enable_msi(struct pci_dev *dev) {return -1;} @@ -614,12 +613,6 @@ extern void pci_disable_msix(struct pci_dev *dev); extern void msi_remove_pci_irq_vectors(struct pci_dev *dev); #endif -#ifdef CONFIG_HT_IRQ -/* The functions a driver should call */ -int ht_create_irq(struct pci_dev *dev, int idx); -void ht_destroy_irq(unsigned int irq); -#endif /* CONFIG_HT_IRQ */ - extern void pci_block_user_cfg_access(struct pci_dev *dev); extern void pci_unblock_user_cfg_access(struct pci_dev *dev); diff --git a/trunk/include/linux/pci_regs.h b/trunk/include/linux/pci_regs.h index c312a12ad2d6..7d0e26cba420 100644 --- a/trunk/include/linux/pci_regs.h +++ b/trunk/include/linux/pci_regs.h @@ -12,11 +12,6 @@ * PCI Local Bus Specification * PCI to PCI Bridge Specification * PCI System Design Guide - * - * For hypertransport information, please consult the following manuals - * from http://www.hypertransport.org - * - * The Hypertransport I/O Link Specification */ #ifndef LINUX_PCI_REGS_H @@ -468,20 +463,4 @@ #define PCI_PWR_CAP 12 /* Capability */ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ -/* Hypertransport sub capability types */ -#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ -#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ -#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ -#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ -#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ -#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ -#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ -#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ -#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ -#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ -#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ -#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ -#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ - - #endif /* LINUX_PCI_REGS_H */ diff --git a/trunk/include/linux/rcupdate.h b/trunk/include/linux/rcupdate.h index c6b7485eac7c..b4ca73d65891 100644 --- a/trunk/include/linux/rcupdate.h +++ b/trunk/include/linux/rcupdate.h @@ -19,7 +19,7 @@ * * Author: Dipankar Sarma * - * Based on the original work by Paul McKenney + * Based on the original work by Paul McKenney * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. * Papers: * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf @@ -66,8 +66,6 @@ struct rcu_ctrlblk { long completed; /* Number of the last completed batch */ int next_pending; /* Is the next batch already waiting? */ - int signaled; - spinlock_t lock ____cacheline_internodealigned_in_smp; cpumask_t cpumask; /* CPUs that need to switch in order */ /* for current batch to proceed. */ @@ -108,6 +106,9 @@ struct rcu_data { long blimit; /* Upper limit on a processed batch */ int cpu; struct rcu_head barrier; +#ifdef CONFIG_SMP + long last_rs_qlen; /* qlen during the last resched */ +#endif }; DECLARE_PER_CPU(struct rcu_data, rcu_data); diff --git a/trunk/include/linux/scx200.h b/trunk/include/linux/scx200.h index de466e11e271..693c0557e70b 100644 --- a/trunk/include/linux/scx200.h +++ b/trunk/include/linux/scx200.h @@ -32,7 +32,7 @@ extern unsigned scx200_cb_base; /* High Resolution Timer */ #define SCx200_TIMER_OFFSET 0x08 -#define SCx200_TIMER_SIZE 0x06 +#define SCx200_TIMER_SIZE 0x05 /* Clock Generators */ #define SCx200_CLOCKGEN_OFFSET 0x10 diff --git a/trunk/include/linux/slab.h b/trunk/include/linux/slab.h index c4947b8a2c03..70be57d8ae0d 100644 --- a/trunk/include/linux/slab.h +++ b/trunk/include/linux/slab.h @@ -77,6 +77,13 @@ struct cache_sizes { extern struct cache_sizes malloc_sizes[]; extern void *__kmalloc(size_t, gfp_t); +#ifndef CONFIG_DEBUG_SLAB +#define ____kmalloc(size, flags) __kmalloc(size, flags) +#else +extern void *__kmalloc_track_caller(size_t, gfp_t, void*); +#define ____kmalloc(size, flags) \ + __kmalloc_track_caller(size, flags, __builtin_return_address(0)) +#endif /** * kmalloc - allocate memory @@ -146,23 +153,6 @@ static inline void *kmalloc(size_t size, gfp_t flags) return __kmalloc(size, flags); } -/* - * kmalloc_track_caller is a special version of kmalloc that records the - * calling function of the routine calling it for slab leak tracking instead - * of just the calling function (confusing, eh?). - * It's useful when the call to kmalloc comes from a widely-used standard - * allocator where we care about the real place the memory allocation - * request comes from. - */ -#ifndef CONFIG_DEBUG_SLAB -#define kmalloc_track_caller(size, flags) \ - __kmalloc(size, flags) -#else -extern void *__kmalloc_track_caller(size_t, gfp_t, void*); -#define kmalloc_track_caller(size, flags) \ - __kmalloc_track_caller(size, flags, __builtin_return_address(0)) -#endif - extern void *__kzalloc(size_t, gfp_t); /** @@ -281,7 +271,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) #define kmem_cache_alloc_node(c, f, n) kmem_cache_alloc(c, f) #define kmalloc_node(s, f, n) kmalloc(s, f) #define kzalloc(s, f) __kzalloc(s, f) -#define kmalloc_track_caller kmalloc +#define ____kmalloc kmalloc #endif /* CONFIG_SLOB */ diff --git a/trunk/include/linux/sound.h b/trunk/include/linux/sound.h index 9e2a94feed6b..f63d8342ffa3 100644 --- a/trunk/include/linux/sound.h +++ b/trunk/include/linux/sound.h @@ -35,8 +35,10 @@ extern int register_sound_special_device(const struct file_operations *fops, int extern int register_sound_mixer(const struct file_operations *fops, int dev); extern int register_sound_midi(const struct file_operations *fops, int dev); extern int register_sound_dsp(const struct file_operations *fops, int dev); +extern int register_sound_synth(const struct file_operations *fops, int dev); extern void unregister_sound_special(int unit); extern void unregister_sound_mixer(int unit); extern void unregister_sound_midi(int unit); extern void unregister_sound_dsp(int unit); +extern void unregister_sound_synth(int unit); diff --git a/trunk/include/linux/srcu.h b/trunk/include/linux/srcu.h deleted file mode 100644 index aca0eee53930..000000000000 --- a/trunk/include/linux/srcu.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sleepable Read-Copy Update mechanism for mutual exclusion - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) IBM Corporation, 2006 - * - * Author: Paul McKenney - * - * For detailed explanation of Read-Copy Update mechanism see - - * Documentation/RCU/ *.txt - * - */ - -#ifndef _LINUX_SRCU_H -#define _LINUX_SRCU_H - -struct srcu_struct_array { - int c[2]; -}; - -struct srcu_struct { - int completed; - struct srcu_struct_array *per_cpu_ref; - struct mutex mutex; -}; - -#ifndef CONFIG_PREEMPT -#define srcu_barrier() barrier() -#else /* #ifndef CONFIG_PREEMPT */ -#define srcu_barrier() -#endif /* #else #ifndef CONFIG_PREEMPT */ - -int init_srcu_struct(struct srcu_struct *sp); -void cleanup_srcu_struct(struct srcu_struct *sp); -int srcu_read_lock(struct srcu_struct *sp) __acquires(sp); -void srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp); -void synchronize_srcu(struct srcu_struct *sp); -long srcu_batches_completed(struct srcu_struct *sp); - -#endif diff --git a/trunk/include/linux/sunrpc/auth.h b/trunk/include/linux/sunrpc/auth.h index 534cdc7be58d..862c0d8c8381 100644 --- a/trunk/include/linux/sunrpc/auth.h +++ b/trunk/include/linux/sunrpc/auth.h @@ -20,6 +20,9 @@ /* size of the nodename buffer */ #define UNX_MAXNODENAME 32 +/* Maximum size (in bytes) of an rpc credential or verifier */ +#define RPC_MAX_AUTH_SIZE (400) + /* Work around the lack of a VFS credential */ struct auth_cred { uid_t uid; diff --git a/trunk/include/linux/sunrpc/cache.h b/trunk/include/linux/sunrpc/cache.h index 3699dff7db8f..b5612c958cce 100644 --- a/trunk/include/linux/sunrpc/cache.h +++ b/trunk/include/linux/sunrpc/cache.h @@ -163,17 +163,6 @@ static inline void cache_put(struct cache_head *h, struct cache_detail *cd) kref_put(&h->ref, cd->cache_put); } -static inline int cache_valid(struct cache_head *h) -{ - /* If an item has been unhashed pending removal when - * the refcount drops to 0, the expiry_time will be - * set to 0. We don't want to consider such items - * valid in this context even though CACHE_VALID is - * set. - */ - return (h->expiry_time != 0 && test_bit(CACHE_VALID, &h->flags)); -} - extern int cache_check(struct cache_detail *detail, struct cache_head *h, struct cache_req *rqstp); extern void cache_flush(void); diff --git a/trunk/include/linux/sunrpc/msg_prot.h b/trunk/include/linux/sunrpc/msg_prot.h index 1e65f2dd80e5..8d10d148834e 100644 --- a/trunk/include/linux/sunrpc/msg_prot.h +++ b/trunk/include/linux/sunrpc/msg_prot.h @@ -11,9 +11,6 @@ #define RPC_VERSION 2 -/* size of an XDR encoding unit in bytes, i.e. 32bit */ -#define XDR_UNIT (4) - /* spec defines authentication flavor as an unsigned 32 bit integer */ typedef u32 rpc_authflavor_t; @@ -37,9 +34,6 @@ enum rpc_auth_flavors { RPC_AUTH_GSS_SPKMP = 390011, }; -/* Maximum size (in bytes) of an rpc credential or verifier */ -#define RPC_MAX_AUTH_SIZE (400) - enum rpc_msg_type { RPC_CALL = 0, RPC_REPLY = 1 @@ -107,39 +101,5 @@ typedef __be32 rpc_fraghdr; #define RPC_FRAGMENT_SIZE_MASK (~RPC_LAST_STREAM_FRAGMENT) #define RPC_MAX_FRAGMENT_SIZE ((1U << 31) - 1) -/* - * RPC call and reply header size as number of 32bit words (verifier - * size computed separately, see below) - */ -#define RPC_CALLHDRSIZE (6) -#define RPC_REPHDRSIZE (4) - - -/* - * Maximum RPC header size, including authentication, - * as number of 32bit words (see RFCs 1831, 1832). - * - * xid 1 xdr unit = 4 bytes - * mtype 1 - * rpc_version 1 - * program 1 - * prog_version 1 - * procedure 1 - * cred { - * flavor 1 - * length 1 - * body 100 xdr units = 400 bytes - * } - * verf { - * flavor 1 - * length 1 - * body 100 xdr units = 400 bytes - * } - * TOTAL 210 xdr units = 840 bytes - */ -#define RPC_MAX_HEADER_WITH_AUTH \ - (RPC_CALLHDRSIZE + 2*(2+RPC_MAX_AUTH_SIZE/4)) - - #endif /* __KERNEL__ */ #endif /* _LINUX_SUNRPC_MSGPROT_H_ */ diff --git a/trunk/include/linux/sunrpc/svc.h b/trunk/include/linux/sunrpc/svc.h index d6288e89fd9d..4ebcdf91f3b3 100644 --- a/trunk/include/linux/sunrpc/svc.h +++ b/trunk/include/linux/sunrpc/svc.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -96,28 +95,8 @@ static inline void svc_get(struct svc_serv *serv) * Maximum payload size supported by a kernel RPC server. * This is use to determine the max number of pages nfsd is * willing to return in a single READ operation. - * - * These happen to all be powers of 2, which is not strictly - * necessary but helps enforce the real limitation, which is - * that they should be multiples of PAGE_CACHE_SIZE. - * - * For UDP transports, a block plus NFS,RPC, and UDP headers - * has to fit into the IP datagram limit of 64K. The largest - * feasible number for all known page sizes is probably 48K, - * but we choose 32K here. This is the same as the historical - * Linux limit; someone who cares more about NFS/UDP performance - * can test a larger number. - * - * For TCP transports we have more freedom. A size of 1MB is - * chosen to match the client limit. Other OSes are known to - * have larger limits, but those numbers are probably beyond - * the point of diminishing returns. */ -#define RPCSVC_MAXPAYLOAD (1*1024*1024u) -#define RPCSVC_MAXPAYLOAD_TCP RPCSVC_MAXPAYLOAD -#define RPCSVC_MAXPAYLOAD_UDP (32*1024u) - -extern u32 svc_max_payload(const struct svc_rqst *rqstp); +#define RPCSVC_MAXPAYLOAD (64*1024u) /* * RPC Requsts and replies are stored in one or more pages. @@ -191,6 +170,7 @@ static inline void svc_putu32(struct kvec *iov, __be32 val) /* * The context of a single thread, including the request currently being * processed. + * NOTE: First two items must be prev/next. */ struct svc_rqst { struct list_head rq_list; /* idle list */ @@ -209,11 +189,12 @@ struct svc_rqst { struct xdr_buf rq_arg; struct xdr_buf rq_res; - struct page * rq_pages[RPCSVC_MAXPAGES]; - struct page * *rq_respages; /* points into rq_pages */ - int rq_resused; /* number of pages used for result */ - - struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ + struct page * rq_argpages[RPCSVC_MAXPAGES]; + struct page * rq_respages[RPCSVC_MAXPAGES]; + int rq_restailpage; + short rq_argused; /* pages used for argument */ + short rq_arghi; /* pages available in argument page list */ + short rq_resused; /* pages used for result */ __be32 rq_xid; /* transmission id */ u32 rq_prog; /* program number */ @@ -274,18 +255,63 @@ xdr_ressize_check(struct svc_rqst *rqstp, __be32 *p) return vec->iov_len <= PAGE_SIZE; } -static inline void svc_free_res_pages(struct svc_rqst *rqstp) +static inline struct page * +svc_take_res_page(struct svc_rqst *rqstp) +{ + if (rqstp->rq_arghi <= rqstp->rq_argused) + return NULL; + rqstp->rq_arghi--; + rqstp->rq_respages[rqstp->rq_resused] = + rqstp->rq_argpages[rqstp->rq_arghi]; + return rqstp->rq_respages[rqstp->rq_resused++]; +} + +static inline void svc_take_page(struct svc_rqst *rqstp) +{ + if (rqstp->rq_arghi <= rqstp->rq_argused) { + WARN_ON(1); + return; + } + rqstp->rq_arghi--; + rqstp->rq_respages[rqstp->rq_resused] = + rqstp->rq_argpages[rqstp->rq_arghi]; + rqstp->rq_resused++; +} + +static inline void svc_pushback_allpages(struct svc_rqst *rqstp) +{ + while (rqstp->rq_resused) { + if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) + continue; + rqstp->rq_argpages[rqstp->rq_arghi++] = + rqstp->rq_respages[rqstp->rq_resused]; + rqstp->rq_respages[rqstp->rq_resused] = NULL; + } +} + +static inline void svc_pushback_unused_pages(struct svc_rqst *rqstp) { - while (rqstp->rq_resused) { - struct page **pp = (rqstp->rq_respages + - --rqstp->rq_resused); - if (*pp) { - put_page(*pp); - *pp = NULL; + while (rqstp->rq_resused && + rqstp->rq_res.pages != &rqstp->rq_respages[rqstp->rq_resused]) { + + if (rqstp->rq_respages[--rqstp->rq_resused] != NULL) { + rqstp->rq_argpages[rqstp->rq_arghi++] = + rqstp->rq_respages[rqstp->rq_resused]; + rqstp->rq_respages[rqstp->rq_resused] = NULL; } } } +static inline void svc_free_allpages(struct svc_rqst *rqstp) +{ + while (rqstp->rq_resused) { + if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) + continue; + put_page(rqstp->rq_respages[rqstp->rq_resused]); + rqstp->rq_respages[rqstp->rq_resused] = NULL; + } +} + struct svc_deferred_req { u32 prot; /* protocol (UDP or TCP) */ struct sockaddr_in addr; @@ -321,9 +347,6 @@ struct svc_version { struct svc_procedure * vs_proc; /* per-procedure info */ u32 vs_xdrsize; /* xdrsize needed for this version */ - unsigned int vs_hidden : 1; /* Don't register with portmapper. - * Only used for nfsacl so far. */ - /* Override dispatch function (e.g. when caching replies). * A return value of 0 means drop the request. * vs_dispatch == NULL means use default dispatcher. diff --git a/trunk/include/linux/sunrpc/svcauth.h b/trunk/include/linux/sunrpc/svcauth.h index de92619b0826..a6601650deeb 100644 --- a/trunk/include/linux/sunrpc/svcauth.h +++ b/trunk/include/linux/sunrpc/svcauth.h @@ -126,7 +126,6 @@ extern struct auth_domain *auth_domain_find(char *name); extern struct auth_domain *auth_unix_lookup(struct in_addr addr); extern int auth_unix_forget_old(struct auth_domain *dom); extern void svcauth_unix_purge(void); -extern void svcauth_unix_info_release(void *); static inline unsigned long hash_str(char *name, int bits) { diff --git a/trunk/include/linux/sunrpc/svcsock.h b/trunk/include/linux/sunrpc/svcsock.h index 98b21ad370fd..4c296152cbfa 100644 --- a/trunk/include/linux/sunrpc/svcsock.h +++ b/trunk/include/linux/sunrpc/svcsock.h @@ -54,9 +54,6 @@ struct svc_sock { int sk_reclen; /* length of record */ int sk_tcplen; /* current read length */ time_t sk_lastrecv; /* time of last received request */ - - /* cache of various info for TCP sockets */ - void *sk_info_authunix; }; /* diff --git a/trunk/include/linux/sunrpc/xprt.h b/trunk/include/linux/sunrpc/xprt.h index 60394fbc4c70..6cf626580752 100644 --- a/trunk/include/linux/sunrpc/xprt.h +++ b/trunk/include/linux/sunrpc/xprt.h @@ -15,7 +15,6 @@ #include #include #include -#include extern unsigned int xprt_udp_slot_table_entries; extern unsigned int xprt_tcp_slot_table_entries; @@ -24,6 +23,13 @@ extern unsigned int xprt_tcp_slot_table_entries; #define RPC_DEF_SLOT_TABLE (16U) #define RPC_MAX_SLOT_TABLE (128U) +/* + * RPC call and reply header size as number of 32bit words (verifier + * size computed separately) + */ +#define RPC_CALLHDRSIZE 6 +#define RPC_REPHDRSIZE 4 + /* * Parameters for choosing a free port */ diff --git a/trunk/include/linux/tifm.h b/trunk/include/linux/tifm.h deleted file mode 100644 index 203dd5e11ecb..000000000000 --- a/trunk/include/linux/tifm.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * tifm.h - TI FlashMedia driver - * - * Copyright (C) 2006 Alex Dubov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#ifndef _TIFM_H -#define _TIFM_H - -#include -#include -#include -#include -#include - -/* Host registers (relative to pci base address): */ -enum { - FM_SET_INTERRUPT_ENABLE = 0x008, - FM_CLEAR_INTERRUPT_ENABLE = 0x00c, - FM_INTERRUPT_STATUS = 0x014 }; - -/* Socket registers (relative to socket base address): */ -enum { - SOCK_CONTROL = 0x004, - SOCK_PRESENT_STATE = 0x008, - SOCK_DMA_ADDRESS = 0x00c, - SOCK_DMA_CONTROL = 0x010, - SOCK_DMA_FIFO_INT_ENABLE_SET = 0x014, - SOCK_DMA_FIFO_INT_ENABLE_CLEAR = 0x018, - SOCK_DMA_FIFO_STATUS = 0x020, - SOCK_FIFO_CONTROL = 0x024, - SOCK_FIFO_PAGE_SIZE = 0x028, - SOCK_MMCSD_COMMAND = 0x104, - SOCK_MMCSD_ARG_LOW = 0x108, - SOCK_MMCSD_ARG_HIGH = 0x10c, - SOCK_MMCSD_CONFIG = 0x110, - SOCK_MMCSD_STATUS = 0x114, - SOCK_MMCSD_INT_ENABLE = 0x118, - SOCK_MMCSD_COMMAND_TO = 0x11c, - SOCK_MMCSD_DATA_TO = 0x120, - SOCK_MMCSD_DATA = 0x124, - SOCK_MMCSD_BLOCK_LEN = 0x128, - SOCK_MMCSD_NUM_BLOCKS = 0x12c, - SOCK_MMCSD_BUFFER_CONFIG = 0x130, - SOCK_MMCSD_SPI_CONFIG = 0x134, - SOCK_MMCSD_SDIO_MODE_CONFIG = 0x138, - SOCK_MMCSD_RESPONSE = 0x144, - SOCK_MMCSD_SDIO_SR = 0x164, - SOCK_MMCSD_SYSTEM_CONTROL = 0x168, - SOCK_MMCSD_SYSTEM_STATUS = 0x16c, - SOCK_MS_COMMAND = 0x184, - SOCK_MS_DATA = 0x188, - SOCK_MS_STATUS = 0x18c, - SOCK_MS_SYSTEM = 0x190, - SOCK_FIFO_ACCESS = 0x200 }; - - -#define TIFM_IRQ_ENABLE 0x80000000 -#define TIFM_IRQ_SOCKMASK 0x00000001 -#define TIFM_IRQ_CARDMASK 0x00000100 -#define TIFM_IRQ_FIFOMASK 0x00010000 -#define TIFM_IRQ_SETALL 0xffffffff -#define TIFM_IRQ_SETALLSOCK 0x0000000f - -#define TIFM_CTRL_LED 0x00000040 -#define TIFM_CTRL_FAST_CLK 0x00000100 - -#define TIFM_SOCK_STATE_OCCUPIED 0x00000008 -#define TIFM_SOCK_STATE_POWERED 0x00000080 - -#define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */ -#define TIFM_FIFO_INT_SETALL 0x0000ffff -#define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */ - -#define TIFM_DMA_RESET 0x00000002 /* Meaning of this constant is unverified */ -#define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ -#define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ - -typedef enum {FM_NULL = 0, FM_XD = 0x01, FM_MS = 0x02, FM_SD = 0x03} tifm_media_id; - -struct tifm_driver; -struct tifm_dev { - char __iomem *addr; - spinlock_t lock; - tifm_media_id media_id; - char wq_name[KOBJ_NAME_LEN]; - struct workqueue_struct *wq; - - unsigned int (*signal_irq)(struct tifm_dev *sock, - unsigned int sock_irq_status); - - struct tifm_driver *drv; - struct device dev; -}; - -struct tifm_driver { - tifm_media_id *id_table; - int (*probe)(struct tifm_dev *dev); - void (*remove)(struct tifm_dev *dev); - - struct device_driver driver; -}; - -struct tifm_adapter { - char __iomem *addr; - unsigned int irq_status; - unsigned int insert_mask; - unsigned int remove_mask; - spinlock_t lock; - unsigned int id; - unsigned int max_sockets; - char wq_name[KOBJ_NAME_LEN]; - unsigned int inhibit_new_cards; - struct workqueue_struct *wq; - struct work_struct media_inserter; - struct work_struct media_remover; - struct tifm_dev **sockets; - struct class_device cdev; - struct device *dev; - - void (*eject)(struct tifm_adapter *fm, struct tifm_dev *sock); -}; - -struct tifm_adapter *tifm_alloc_adapter(void); -void tifm_free_device(struct device *dev); -void tifm_free_adapter(struct tifm_adapter *fm); -int tifm_add_adapter(struct tifm_adapter *fm); -void tifm_remove_adapter(struct tifm_adapter *fm); -struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id); -int tifm_register_driver(struct tifm_driver *drv); -void tifm_unregister_driver(struct tifm_driver *drv); -void tifm_eject(struct tifm_dev *sock); -int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, - int direction); -void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, - int direction); - - -static inline void *tifm_get_drvdata(struct tifm_dev *dev) -{ - return dev_get_drvdata(&dev->dev); -} - -static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) -{ - dev_set_drvdata(&dev->dev, data); -} - -struct tifm_device_id { - tifm_media_id media_id; -}; - -#endif diff --git a/trunk/include/linux/utsname.h b/trunk/include/linux/utsname.h index a4555fe3754c..02e4b6972064 100644 --- a/trunk/include/linux/utsname.h +++ b/trunk/include/linux/utsname.h @@ -1,6 +1,11 @@ #ifndef _LINUX_UTSNAME_H #define _LINUX_UTSNAME_H +#include +#include +#include +#include + #define __OLD_UTS_LEN 8 struct oldold_utsname { @@ -30,13 +35,6 @@ struct new_utsname { char domainname[65]; }; -#ifdef __KERNEL__ - -#include -#include -#include -#include - struct uts_namespace { struct kref kref; struct new_utsname name; @@ -88,7 +86,4 @@ static inline struct new_utsname *init_utsname(void) } extern struct rw_semaphore uts_sem; - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_UTSNAME_H */ +#endif diff --git a/trunk/include/linux/wavefront.h b/trunk/include/linux/wavefront.h new file mode 100644 index 000000000000..51ab3c933acd --- /dev/null +++ b/trunk/include/linux/wavefront.h @@ -0,0 +1,675 @@ +#ifndef __wavefront_h__ +#define __wavefront_h__ + +/* WaveFront header file. + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#if (!defined(__GNUC__) && !defined(__GNUG__)) + + You will not be able to compile this file correctly without gcc, because + it is necessary to pack the "wavefront_alias" structure to a size + of 22 bytes, corresponding to 16-bit alignment (as would have been + the case on the original platform, MS-DOS). If this is not done, + then WavePatch-format files cannot be read/written correctly. + The method used to do this here ("__attribute__((packed)") is + completely compiler dependent. + + All other wavefront_* types end up aligned to 32 bit values and + still have the same (correct) size. + +#else + + /* However, note that as of G++ 2.7.3.2, g++ was unable to + correctly parse *type* __attribute__ tags. It will do the + right thing if we use the "packed" attribute on each struct + member, which has the same semantics anyway. + */ + +#endif /* __GNUC__ */ + +/***************************** WARNING ******************************** + PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO + BE USED WITH EITHER C *OR* C++. + **********************************************************************/ + +#ifndef NUM_MIDIKEYS +#define NUM_MIDIKEYS 128 +#endif /* NUM_MIDIKEYS */ + +#ifndef NUM_MIDICHANNELS +#define NUM_MIDICHANNELS 16 +#endif /* NUM_MIDICHANNELS */ + +/* These are very useful/important. the original wavefront interface + was developed on a 16 bit system, where sizeof(int) = 2 + bytes. Defining things like this makes the code much more portable, and + easier to understand without having to toggle back and forth + between a 16-bit view of the world and a 32-bit one. + */ + +typedef short INT16; +typedef unsigned short UINT16; +typedef int INT32; +typedef unsigned int UINT32; +typedef char CHAR8; +typedef unsigned char UCHAR8; + +/* Pseudo-commands not part of the WaveFront command set. + These are used for various driver controls and direct + hardware control. + */ + +#define WFC_DEBUG_DRIVER 0 +#define WFC_FX_IOCTL 1 +#define WFC_PATCH_STATUS 2 +#define WFC_PROGRAM_STATUS 3 +#define WFC_SAMPLE_STATUS 4 +#define WFC_DISABLE_INTERRUPTS 5 +#define WFC_ENABLE_INTERRUPTS 6 +#define WFC_INTERRUPT_STATUS 7 +#define WFC_ROMSAMPLES_RDONLY 8 +#define WFC_IDENTIFY_SLOT_TYPE 9 + +/* Wavefront synth commands + */ + +#define WFC_DOWNLOAD_SAMPLE 0x80 +#define WFC_DOWNLOAD_BLOCK 0x81 +#define WFC_DOWNLOAD_MULTISAMPLE 0x82 +#define WFC_DOWNLOAD_SAMPLE_ALIAS 0x83 +#define WFC_DELETE_SAMPLE 0x84 +#define WFC_REPORT_FREE_MEMORY 0x85 +#define WFC_DOWNLOAD_PATCH 0x86 +#define WFC_DOWNLOAD_PROGRAM 0x87 +#define WFC_SET_SYNTHVOL 0x89 +#define WFC_SET_NVOICES 0x8B +#define WFC_DOWNLOAD_DRUM 0x90 +#define WFC_GET_SYNTHVOL 0x92 +#define WFC_GET_NVOICES 0x94 +#define WFC_DISABLE_CHANNEL 0x9A +#define WFC_ENABLE_CHANNEL 0x9B +#define WFC_MISYNTH_OFF 0x9D +#define WFC_MISYNTH_ON 0x9E +#define WFC_FIRMWARE_VERSION 0x9F +#define WFC_GET_NSAMPLES 0xA0 +#define WFC_DISABLE_DRUM_PROGRAM 0xA2 +#define WFC_UPLOAD_PATCH 0xA3 +#define WFC_UPLOAD_PROGRAM 0xA4 +#define WFC_SET_TUNING 0xA6 +#define WFC_GET_TUNING 0xA7 +#define WFC_VMIDI_ON 0xA8 +#define WFC_VMIDI_OFF 0xA9 +#define WFC_MIDI_STATUS 0xAA +#define WFC_GET_CHANNEL_STATUS 0xAB +#define WFC_DOWNLOAD_SAMPLE_HEADER 0xAC +#define WFC_UPLOAD_SAMPLE_HEADER 0xAD +#define WFC_UPLOAD_MULTISAMPLE 0xAE +#define WFC_UPLOAD_SAMPLE_ALIAS 0xAF +#define WFC_IDENTIFY_SAMPLE_TYPE 0xB0 +#define WFC_DOWNLOAD_EDRUM_PROGRAM 0xB1 +#define WFC_UPLOAD_EDRUM_PROGRAM 0xB2 +#define WFC_SET_EDRUM_CHANNEL 0xB3 +#define WFC_INSTOUT_LEVELS 0xB4 +#define WFC_PEAKOUT_LEVELS 0xB5 +#define WFC_REPORT_CHANNEL_PROGRAMS 0xB6 +#define WFC_HARDWARE_VERSION 0xCF +#define WFC_UPLOAD_SAMPLE_PARAMS 0xD7 +#define WFC_DOWNLOAD_OS 0xF1 +#define WFC_NOOP 0xFF + +#define WF_MAX_SAMPLE 512 +#define WF_MAX_PATCH 256 +#define WF_MAX_PROGRAM 128 + +#define WF_SECTION_MAX 44 /* longest OS section length */ + +/* # of bytes we send to the board when sending it various kinds of + substantive data, such as samples, patches and programs. +*/ + +#define WF_PROGRAM_BYTES 32 +#define WF_PATCH_BYTES 132 +#define WF_SAMPLE_BYTES 27 +#define WF_SAMPLE_HDR_BYTES 25 +#define WF_ALIAS_BYTES 25 +#define WF_DRUM_BYTES 9 +#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */ + +#define WF_ACK 0x80 +#define WF_DMA_ACK 0x81 + +/* OR-values for MIDI status bits */ + +#define WF_MIDI_VIRTUAL_ENABLED 0x1 +#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2 +#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4 + +/* slot indexes for struct address_info: makes code a little more mnemonic */ + +#define WF_SYNTH_SLOT 0 +#define WF_INTERNAL_MIDI_SLOT 1 +#define WF_EXTERNAL_MIDI_SLOT 2 + +/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401 + emulation. Note these NEVER show up in output from the device and + should NEVER be used in input unless Virtual MIDI mode has been + disabled. If they do show up as input, the results are unpredictable. +*/ + +#define WF_EXTERNAL_SWITCH 0xFD +#define WF_INTERNAL_SWITCH 0xF9 + +/* Debugging flags */ + +#define WF_DEBUG_CMD 0x1 +#define WF_DEBUG_DATA 0x2 +#define WF_DEBUG_LOAD_PATCH 0x4 +#define WF_DEBUG_IO 0x8 + +/* WavePatch file format stuff */ + +#define WF_WAVEPATCH_VERSION 120; /* Current version number (1.2) */ +#define WF_MAX_COMMENT 64 /* Comment length */ +#define WF_NUM_LAYERS 4 +#define WF_NAME_LENGTH 32 +#define WF_SOURCE_LENGTH 260 + +#define BankFileID "Bank" +#define DrumkitFileID "DrumKit" +#define ProgramFileID "Program" + +struct wf_envelope +{ + UCHAR8 attack_time:7; + UCHAR8 Unused1:1; + + UCHAR8 decay1_time:7; + UCHAR8 Unused2:1; + + UCHAR8 decay2_time:7; + UCHAR8 Unused3:1; + + UCHAR8 sustain_time:7; + UCHAR8 Unused4:1; + + UCHAR8 release_time:7; + UCHAR8 Unused5:1; + + UCHAR8 release2_time:7; + UCHAR8 Unused6:1; + + CHAR8 attack_level; + CHAR8 decay1_level; + CHAR8 decay2_level; + CHAR8 sustain_level; + CHAR8 release_level; + + UCHAR8 attack_velocity:7; + UCHAR8 Unused7:1; + + UCHAR8 volume_velocity:7; + UCHAR8 Unused8:1; + + UCHAR8 keyboard_scaling:7; + UCHAR8 Unused9:1; +}; +typedef struct wf_envelope wavefront_envelope; + +struct wf_lfo +{ + UCHAR8 sample_number; + + UCHAR8 frequency:7; + UCHAR8 Unused1:1; + + UCHAR8 am_src:4; + UCHAR8 fm_src:4; + + CHAR8 fm_amount; + CHAR8 am_amount; + CHAR8 start_level; + CHAR8 end_level; + + UCHAR8 ramp_delay:7; + UCHAR8 wave_restart:1; /* for LFO2 only */ + + UCHAR8 ramp_time:7; + UCHAR8 Unused2:1; +}; +typedef struct wf_lfo wavefront_lfo; + +struct wf_patch +{ + INT16 frequency_bias; /* ** THIS IS IN MOTOROLA FORMAT!! ** */ + + UCHAR8 amplitude_bias:7; + UCHAR8 Unused1:1; + + UCHAR8 portamento:7; + UCHAR8 Unused2:1; + + UCHAR8 sample_number; + + UCHAR8 pitch_bend:4; + UCHAR8 sample_msb:1; + UCHAR8 Unused3:3; + + UCHAR8 mono:1; + UCHAR8 retrigger:1; + UCHAR8 nohold:1; + UCHAR8 restart:1; + UCHAR8 filterconfig:2; /* SDK says "not used" */ + UCHAR8 reuse:1; + UCHAR8 reset_lfo:1; + + UCHAR8 fm_src2:4; + UCHAR8 fm_src1:4; + + CHAR8 fm_amount1; + CHAR8 fm_amount2; + + UCHAR8 am_src:4; + UCHAR8 Unused4:4; + + CHAR8 am_amount; + + UCHAR8 fc1_mode:4; + UCHAR8 fc2_mode:4; + + CHAR8 fc1_mod_amount; + CHAR8 fc1_keyboard_scaling; + CHAR8 fc1_bias; + CHAR8 fc2_mod_amount; + CHAR8 fc2_keyboard_scaling; + CHAR8 fc2_bias; + + UCHAR8 randomizer:7; + UCHAR8 Unused5:1; + + struct wf_envelope envelope1; + struct wf_envelope envelope2; + struct wf_lfo lfo1; + struct wf_lfo lfo2; +}; +typedef struct wf_patch wavefront_patch; + +struct wf_layer +{ + UCHAR8 patch_number; + + UCHAR8 mix_level:7; + UCHAR8 mute:1; + + UCHAR8 split_point:7; + UCHAR8 play_below:1; + + UCHAR8 pan_mod_src:2; + UCHAR8 pan_or_mod:1; + UCHAR8 pan:4; + UCHAR8 split_type:1; +}; +typedef struct wf_layer wavefront_layer; + +struct wf_program +{ + struct wf_layer layer[WF_NUM_LAYERS]; +}; +typedef struct wf_program wavefront_program; + +struct wf_sample_offset +{ + INT32 Fraction:4; + INT32 Integer:20; + INT32 Unused:8; +}; +typedef struct wf_sample_offset wavefront_sample_offset; + +/* Sample slot types */ + +#define WF_ST_SAMPLE 0 +#define WF_ST_MULTISAMPLE 1 +#define WF_ST_ALIAS 2 +#define WF_ST_EMPTY 3 + +/* pseudo's */ + +#define WF_ST_DRUM 4 +#define WF_ST_PROGRAM 5 +#define WF_ST_PATCH 6 +#define WF_ST_SAMPLEHDR 7 + +#define WF_ST_MASK 0xf + +/* Flags for slot status. These occupy the upper bits of the same byte + as a sample type. +*/ + +#define WF_SLOT_USED 0x80 /* XXX don't rely on this being accurate */ +#define WF_SLOT_FILLED 0x40 +#define WF_SLOT_ROM 0x20 + +#define WF_SLOT_MASK 0xf0 + +/* channel constants */ + +#define WF_CH_MONO 0 +#define WF_CH_LEFT 1 +#define WF_CH_RIGHT 2 + +/* Sample formats */ + +#define LINEAR_16BIT 0 +#define WHITE_NOISE 1 +#define LINEAR_8BIT 2 +#define MULAW_8BIT 3 + +#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2) + + +/* + + Because most/all of the sample data we pass in via pointers has + never been copied (just mmap-ed into user space straight from the + disk), it would be nice to allow handling of multi-channel sample + data without forcing user-level extraction of the relevant bytes. + + So, we need a way of specifying which channel to use (the WaveFront + only handles mono samples in a given slot), and the only way to do + this without using some struct other than wavefront_sample as the + interface is the awful hack of using the unused bits in a + wavefront_sample: + + Val Meaning + --- ------- + 0 no channel selection (use channel 1, sample is MONO) + 1 use first channel, and skip one + 2 use second channel, and skip one + 3 use third channel, and skip two + 4 use fourth channel, skip three + 5 use fifth channel, skip four + 6 use six channel, skip five + + + This can handle up to 4 channels, and anyone downloading >4 channels + of sample data just to select one of them needs to find some tools + like sox ... + + NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is + important. + +*/ + +#define WF_SET_CHANNEL(samp,chn) \ + (samp)->Unused1 = chn & 0x1; \ + (samp)->Unused2 = chn & 0x2; \ + (samp)->Unused3 = chn & 0x4 + +#define WF_GET_CHANNEL(samp) \ + (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1) + +typedef struct wf_sample { + struct wf_sample_offset sampleStartOffset; + struct wf_sample_offset loopStartOffset; + struct wf_sample_offset loopEndOffset; + struct wf_sample_offset sampleEndOffset; + INT16 FrequencyBias; + UCHAR8 SampleResolution:2; /* sample_format */ + UCHAR8 Unused1:1; + UCHAR8 Loop:1; + UCHAR8 Bidirectional:1; + UCHAR8 Unused2:1; + UCHAR8 Reverse:1; + UCHAR8 Unused3:1; +} wavefront_sample; + +typedef struct wf_multisample { + INT16 NumberOfSamples; /* log2 of the number of samples */ + INT16 SampleNumber[NUM_MIDIKEYS]; +} wavefront_multisample; + +typedef struct wf_alias { + INT16 OriginalSample; + + struct wf_sample_offset sampleStartOffset; + struct wf_sample_offset loopStartOffset; + struct wf_sample_offset sampleEndOffset; + struct wf_sample_offset loopEndOffset; + + INT16 FrequencyBias; + + UCHAR8 SampleResolution:2; + UCHAR8 Unused1:1; + UCHAR8 Loop:1; + UCHAR8 Bidirectional:1; + UCHAR8 Unused2:1; + UCHAR8 Reverse:1; + UCHAR8 Unused3:1; + + /* This structure is meant to be padded only to 16 bits on their + original. Of course, whoever wrote their documentation didn't + realize that sizeof(struct) can be >= + sum(sizeof(struct-fields)) and so thought that giving a C level + description of the structs used in WavePatch files was + sufficient. I suppose it was, as long as you remember the + standard 16->32 bit issues. + */ + + UCHAR8 sixteen_bit_padding; +} __attribute__((packed)) wavefront_alias; + +typedef struct wf_drum { + UCHAR8 PatchNumber; + UCHAR8 MixLevel:7; + UCHAR8 Unmute:1; + UCHAR8 Group:4; + UCHAR8 Unused1:4; + UCHAR8 PanModSource:2; + UCHAR8 PanModulated:1; + UCHAR8 PanAmount:4; + UCHAR8 Unused2:1; +} wavefront_drum; + +typedef struct wf_drumkit { + struct wf_drum drum[NUM_MIDIKEYS]; +} wavefront_drumkit; + +typedef struct wf_channel_programs { + UCHAR8 Program[NUM_MIDICHANNELS]; +} wavefront_channel_programs; + +/* How to get MIDI channel status from the data returned by + a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs) +*/ + +#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7)) + +typedef union wf_any { + wavefront_sample s; + wavefront_multisample ms; + wavefront_alias a; + wavefront_program pr; + wavefront_patch p; + wavefront_drum d; +} wavefront_any; + +/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h + might work for other wave-table based patch loading situations. + Alas, his fears were correct. The WaveFront doesn't even come with + just "patches", but several different kind of structures that + control the sound generation process. + */ + +typedef struct wf_patch_info { + + /* the first two fields are used by the OSS "patch loading" interface + only, and are unused by the current user-level library. + */ + + INT16 key; /* Use WAVEFRONT_PATCH here */ + UINT16 devno; /* fill in when sending */ + UCHAR8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */ + +#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999 + + UINT16 number; /* patch/sample/prog number */ + + UINT32 size; /* size of any data included in + one of the fields in `hdrptr', or + as `dataptr'. + + NOTE: for actual samples, this is + the size of the *SELECTED CHANNEL* + even if more data is actually available. + + So, a stereo sample (2 channels) of + 6000 bytes total has `size' = 3000. + + See the macros and comments for + WF_{GET,SET}_CHANNEL above. + + */ + wavefront_any __user *hdrptr; /* user-space ptr to hdr bytes */ + UINT16 __user *dataptr; /* actual sample data */ + + wavefront_any hdr; /* kernel-space copy of hdr bytes */ +} wavefront_patch_info; + +/* The maximum number of bytes we will ever move to or from user space + in response to a WFC_* command. This obviously doesn't cover + actual sample data. +*/ + +#define WF_MAX_READ sizeof(wavefront_multisample) +#define WF_MAX_WRITE sizeof(wavefront_multisample) + +/* + This allows us to execute any WF command except the download/upload + ones, which are handled differently due to copyin/copyout issues as + well as data-nybbling to/from the card. + */ + +typedef struct wavefront_control { + int cmd; /* WFC_* */ + char status; /* return status to user-space */ + unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */ + unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */ +} wavefront_control; + +#define WFCTL_WFCMD 0x1 +#define WFCTL_LOAD_SPP 0x2 + +/* Modulator table */ + +#define WF_MOD_LFO1 0 +#define WF_MOD_LFO2 1 +#define WF_MOD_ENV1 2 +#define WF_MOD_ENV2 3 +#define WF_MOD_KEYBOARD 4 +#define WF_MOD_LOGKEY 5 +#define WF_MOD_VELOCITY 6 +#define WF_MOD_LOGVEL 7 +#define WF_MOD_RANDOM 8 +#define WF_MOD_PRESSURE 9 +#define WF_MOD_MOD_WHEEL 10 +#define WF_MOD_1 WF_MOD_MOD_WHEEL +#define WF_MOD_BREATH 11 +#define WF_MOD_2 WF_MOD_BREATH +#define WF_MOD_FOOT 12 +#define WF_MOD_4 WF_MOD_FOOT +#define WF_MOD_VOLUME 13 +#define WF_MOD_7 WF_MOD_VOLUME +#define WF_MOD_PAN 14 +#define WF_MOD_10 WF_MOD_PAN +#define WF_MOD_EXPR 15 +#define WF_MOD_11 WF_MOD_EXPR + +/* FX-related material */ + +typedef struct wf_fx_info { + int request; /* see list below */ + int data[4]; /* we don't need much */ +} wavefront_fx_info; + +/* support for each of these will be forthcoming once I or someone + else has figured out which of the addresses on page 6 and page 7 of + the YSS225 control each parameter. Incidentally, these come from + the Windows driver interface, but again, Turtle Beach didn't + document the API to use them. +*/ + +#define WFFX_SETOUTGAIN 0 +#define WFFX_SETSTEREOOUTGAIN 1 +#define WFFX_SETREVERBIN1GAIN 2 +#define WFFX_SETREVERBIN2GAIN 3 +#define WFFX_SETREVERBIN3GAIN 4 +#define WFFX_SETCHORUSINPORT 5 +#define WFFX_SETREVERBIN1PORT 6 +#define WFFX_SETREVERBIN2PORT 7 +#define WFFX_SETREVERBIN3PORT 8 +#define WFFX_SETEFFECTPORT 9 +#define WFFX_SETAUXPORT 10 +#define WFFX_SETREVERBTYPE 11 +#define WFFX_SETREVERBDELAY 12 +#define WFFX_SETCHORUSLFO 13 +#define WFFX_SETCHORUSPMD 14 +#define WFFX_SETCHORUSAMD 15 +#define WFFX_SETEFFECT 16 +#define WFFX_SETBASEALL 17 +#define WFFX_SETREVERBALL 18 +#define WFFX_SETCHORUSALL 20 +#define WFFX_SETREVERBDEF 22 +#define WFFX_SETCHORUSDEF 23 +#define WFFX_DELAYSETINGAIN 24 +#define WFFX_DELAYSETFBGAIN 25 +#define WFFX_DELAYSETFBLPF 26 +#define WFFX_DELAYSETGAIN 27 +#define WFFX_DELAYSETTIME 28 +#define WFFX_DELAYSETFBTIME 29 +#define WFFX_DELAYSETALL 30 +#define WFFX_DELAYSETDEF 32 +#define WFFX_SDELAYSETINGAIN 33 +#define WFFX_SDELAYSETFBGAIN 34 +#define WFFX_SDELAYSETFBLPF 35 +#define WFFX_SDELAYSETGAIN 36 +#define WFFX_SDELAYSETTIME 37 +#define WFFX_SDELAYSETFBTIME 38 +#define WFFX_SDELAYSETALL 39 +#define WFFX_SDELAYSETDEF 41 +#define WFFX_DEQSETINGAIN 42 +#define WFFX_DEQSETFILTER 43 +#define WFFX_DEQSETALL 44 +#define WFFX_DEQSETDEF 46 +#define WFFX_MUTE 47 +#define WFFX_FLANGESETBALANCE 48 +#define WFFX_FLANGESETDELAY 49 +#define WFFX_FLANGESETDWFFX_TH 50 +#define WFFX_FLANGESETFBGAIN 51 +#define WFFX_FLANGESETINGAIN 52 +#define WFFX_FLANGESETLFO 53 +#define WFFX_FLANGESETALL 54 +#define WFFX_FLANGESETDEF 56 +#define WFFX_PITCHSETSHIFT 57 +#define WFFX_PITCHSETBALANCE 58 +#define WFFX_PITCHSETALL 59 +#define WFFX_PITCHSETDEF 61 +#define WFFX_SRSSETINGAIN 62 +#define WFFX_SRSSETSPACE 63 +#define WFFX_SRSSETCENTER 64 +#define WFFX_SRSSETGAIN 65 +#define WFFX_SRSSETMODE 66 +#define WFFX_SRSSETDEF 68 + +/* Allow direct user-space control over FX memory/coefficient data. + In theory this could be used to download the FX microprogram, + but it would be a little slower, and involve some weird code. + */ + +#define WFFX_MEMSET 69 + +#endif /* __wavefront_h__ */ diff --git a/trunk/include/linux/xfrm.h b/trunk/include/linux/xfrm.h index 8ae7f744917b..430afd058269 100644 --- a/trunk/include/linux/xfrm.h +++ b/trunk/include/linux/xfrm.h @@ -129,8 +129,7 @@ enum #define XFRM_MODE_TUNNEL 1 #define XFRM_MODE_ROUTEOPTIMIZATION 2 #define XFRM_MODE_IN_TRIGGER 3 -#define XFRM_MODE_BEET 4 -#define XFRM_MODE_MAX 5 +#define XFRM_MODE_MAX 4 /* Netlink configuration messages. */ enum { diff --git a/trunk/include/net/netdma.h b/trunk/include/net/netdma.h index 7f53cd1d8b1e..f28c6e064e8f 100644 --- a/trunk/include/net/netdma.h +++ b/trunk/include/net/netdma.h @@ -20,7 +20,6 @@ */ #ifndef NETDMA_H #define NETDMA_H -#include #ifdef CONFIG_NET_DMA #include #include diff --git a/trunk/init/do_mounts.h b/trunk/init/do_mounts.h index e7f2e7fa066e..735705d137ff 100644 --- a/trunk/init/do_mounts.h +++ b/trunk/init/do_mounts.h @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/trunk/kernel/Makefile b/trunk/kernel/Makefile index 5e3f3b75563a..d948ca12acf0 100644 --- a/trunk/kernel/Makefile +++ b/trunk/kernel/Makefile @@ -8,7 +8,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o rwsem.o latency.o nsproxy.o srcu.o + hrtimer.o rwsem.o latency.o nsproxy.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ diff --git a/trunk/kernel/auditfilter.c b/trunk/kernel/auditfilter.c index 4f40d923af8e..1a58a81fb09d 100644 --- a/trunk/kernel/auditfilter.c +++ b/trunk/kernel/auditfilter.c @@ -411,6 +411,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) case AUDIT_FSGID: case AUDIT_LOGINUID: case AUDIT_PERS: + case AUDIT_ARCH: case AUDIT_MSGTYPE: case AUDIT_PPID: case AUDIT_DEVMAJOR: @@ -422,14 +423,6 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) case AUDIT_ARG2: case AUDIT_ARG3: break; - /* arch is only allowed to be = or != */ - case AUDIT_ARCH: - if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL) - && (f->op != AUDIT_NEGATE) && (f->op)) { - err = -EINVAL; - goto exit_free; - } - break; case AUDIT_PERM: if (f->val & ~15) goto exit_free; diff --git a/trunk/kernel/auditsc.c b/trunk/kernel/auditsc.c index 42f2f1179711..105147631753 100644 --- a/trunk/kernel/auditsc.c +++ b/trunk/kernel/auditsc.c @@ -278,11 +278,8 @@ static int audit_filter_rules(struct task_struct *tsk, result = audit_comparator(tsk->pid, f->op, f->val); break; case AUDIT_PPID: - if (ctx) { - if (!ctx->ppid) - ctx->ppid = sys_getppid(); + if (ctx) result = audit_comparator(ctx->ppid, f->op, f->val); - } break; case AUDIT_UID: result = audit_comparator(tsk->uid, f->op, f->val); @@ -798,8 +795,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts /* tsk == current */ context->pid = tsk->pid; - if (!context->ppid) - context->ppid = sys_getppid(); + context->ppid = sys_getppid(); /* sic. tsk == current in all cases */ context->uid = tsk->uid; context->gid = tsk->gid; context->euid = tsk->euid; @@ -1141,7 +1137,6 @@ void audit_syscall_entry(int arch, int major, context->ctime = CURRENT_TIME; context->in_syscall = 1; context->auditable = !!(state == AUDIT_RECORD_CONTEXT); - context->ppid = 0; } /** @@ -1357,13 +1352,7 @@ void __audit_inode_child(const char *dname, const struct inode *inode, } update_context: - idx = context->name_count; - if (context->name_count == AUDIT_NAMES) { - printk(KERN_DEBUG "name_count maxed and losing %s\n", - found_name ?: "(null)"); - return; - } - context->name_count++; + idx = context->name_count++; #if AUDIT_DEBUG context->ino_count++; #endif @@ -1381,16 +1370,7 @@ void __audit_inode_child(const char *dname, const struct inode *inode, /* A parent was not found in audit_names, so copy the inode data for the * provided parent. */ if (!found_name) { - idx = context->name_count; - if (context->name_count == AUDIT_NAMES) { - printk(KERN_DEBUG - "name_count maxed and losing parent inode data: dev=%02x:%02x, inode=%lu", - MAJOR(parent->i_sb->s_dev), - MINOR(parent->i_sb->s_dev), - parent->i_ino); - return; - } - context->name_count++; + idx = context->name_count++; #if AUDIT_DEBUG context->ino_count++; #endif diff --git a/trunk/kernel/irq/chip.c b/trunk/kernel/irq/chip.c index 4cf65f5c6a74..736cb0bd498f 100644 --- a/trunk/kernel/irq/chip.c +++ b/trunk/kernel/irq/chip.c @@ -17,69 +17,6 @@ #include "internals.h" -/** - * dynamic_irq_init - initialize a dynamically allocated irq - * @irq: irq number to initialize - */ -void dynamic_irq_init(unsigned int irq) -{ - struct irq_desc *desc; - unsigned long flags; - - if (irq >= NR_IRQS) { - printk(KERN_ERR "Trying to initialize invalid IRQ%d\n", irq); - WARN_ON(1); - return; - } - - /* Ensure we don't have left over values from a previous use of this irq */ - desc = irq_desc + irq; - spin_lock_irqsave(&desc->lock, flags); - desc->status = IRQ_DISABLED; - desc->chip = &no_irq_chip; - desc->handle_irq = handle_bad_irq; - desc->depth = 1; - desc->handler_data = NULL; - desc->chip_data = NULL; - desc->action = NULL; - desc->irq_count = 0; - desc->irqs_unhandled = 0; -#ifdef CONFIG_SMP - desc->affinity = CPU_MASK_ALL; -#endif - spin_unlock_irqrestore(&desc->lock, flags); -} - -/** - * dynamic_irq_cleanup - cleanup a dynamically allocated irq - * @irq: irq number to initialize - */ -void dynamic_irq_cleanup(unsigned int irq) -{ - struct irq_desc *desc; - unsigned long flags; - - if (irq >= NR_IRQS) { - printk(KERN_ERR "Trying to cleanup invalid IRQ%d\n", irq); - WARN_ON(1); - return; - } - - desc = irq_desc + irq; - spin_lock_irqsave(&desc->lock, flags); - if (desc->action) { - spin_unlock_irqrestore(&desc->lock, flags); - printk(KERN_ERR "Destroying IRQ%d without calling free_irq\n", - irq); - WARN_ON(1); - return; - } - desc->handle_irq = handle_bad_irq; - desc->chip = &no_irq_chip; - spin_unlock_irqrestore(&desc->lock, flags); -} - - /** * set_irq_chip - set the irq chip for an irq * @irq: irq number diff --git a/trunk/kernel/irq/migration.c b/trunk/kernel/irq/migration.c index 4baa3bbcd25a..a57ebe9fa6f6 100644 --- a/trunk/kernel/irq/migration.c +++ b/trunk/kernel/irq/migration.c @@ -7,17 +7,17 @@ void set_pending_irq(unsigned int irq, cpumask_t mask) unsigned long flags; spin_lock_irqsave(&desc->lock, flags); - desc->status |= IRQ_MOVE_PENDING; + desc->move_irq = 1; irq_desc[irq].pending_mask = mask; spin_unlock_irqrestore(&desc->lock, flags); } -void move_masked_irq(int irq) +void move_native_irq(int irq) { struct irq_desc *desc = irq_desc + irq; cpumask_t tmp; - if (likely(!(desc->status & IRQ_MOVE_PENDING))) + if (likely(!desc->move_irq)) return; /* @@ -28,7 +28,7 @@ void move_masked_irq(int irq) return; } - desc->status &= ~IRQ_MOVE_PENDING; + desc->move_irq = 0; if (unlikely(cpus_empty(irq_desc[irq].pending_mask))) return; @@ -48,29 +48,15 @@ void move_masked_irq(int irq) * when an active trigger is comming in. This could * cause some ioapics to mal-function. * Being paranoid i guess! - * - * For correct operation this depends on the caller - * masking the irqs. */ if (likely(!cpus_empty(tmp))) { + if (likely(!(desc->status & IRQ_DISABLED))) + desc->chip->disable(irq); + desc->chip->set_affinity(irq,tmp); + + if (likely(!(desc->status & IRQ_DISABLED))) + desc->chip->enable(irq); } cpus_clear(irq_desc[irq].pending_mask); } - -void move_native_irq(int irq) -{ - struct irq_desc *desc = irq_desc + irq; - - if (likely(!(desc->status & IRQ_MOVE_PENDING))) - return; - - if (likely(!(desc->status & IRQ_DISABLED))) - desc->chip->disable(irq); - - move_masked_irq(irq); - - if (likely(!(desc->status & IRQ_DISABLED))) - desc->chip->enable(irq); -} - diff --git a/trunk/kernel/rcupdate.c b/trunk/kernel/rcupdate.c index 26bb5ffe1ef1..523e46483b99 100644 --- a/trunk/kernel/rcupdate.c +++ b/trunk/kernel/rcupdate.c @@ -71,6 +71,9 @@ static DEFINE_PER_CPU(struct tasklet_struct, rcu_tasklet) = {NULL}; static int blimit = 10; static int qhimark = 10000; static int qlowmark = 100; +#ifdef CONFIG_SMP +static int rsinterval = 1000; +#endif static atomic_t rcu_barrier_cpu_count; static DEFINE_MUTEX(rcu_barrier_mutex); @@ -83,8 +86,8 @@ static void force_quiescent_state(struct rcu_data *rdp, int cpu; cpumask_t cpumask; set_need_resched(); - if (unlikely(!rcp->signaled)) { - rcp->signaled = 1; + if (unlikely(rdp->qlen - rdp->last_rs_qlen > rsinterval)) { + rdp->last_rs_qlen = rdp->qlen; /* * Don't send IPI to itself. With irqs disabled, * rdp->cpu is the current cpu. @@ -298,7 +301,6 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp) smp_mb(); cpus_andnot(rcp->cpumask, cpu_online_map, nohz_cpu_mask); - rcp->signaled = 0; } } @@ -626,6 +628,9 @@ void synchronize_rcu(void) module_param(blimit, int, 0); module_param(qhimark, int, 0); module_param(qlowmark, int, 0); +#ifdef CONFIG_SMP +module_param(rsinterval, int, 0); +#endif EXPORT_SYMBOL_GPL(rcu_batches_completed); EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); EXPORT_SYMBOL_GPL(call_rcu); diff --git a/trunk/kernel/rcutorture.c b/trunk/kernel/rcutorture.c index e2bda18f6f42..23446e91cded 100644 --- a/trunk/kernel/rcutorture.c +++ b/trunk/kernel/rcutorture.c @@ -15,10 +15,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) IBM Corporation, 2005, 2006 + * Copyright (C) IBM Corporation, 2005 * * Authors: Paul E. McKenney - * Josh Triplett * * See also: Documentation/RCU/torture.txt */ @@ -45,25 +44,19 @@ #include #include #include -#include MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Paul E. McKenney and " - "Josh Triplett "); static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ -static int nfakewriters = 4; /* # fake writer threads */ static int stat_interval; /* Interval between stats, in seconds. */ /* Defaults to "only at end of test". */ static int verbose; /* Print more debug info. */ static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ -static char *torture_type = "rcu"; /* What RCU implementation to torture. */ +static char *torture_type = "rcu"; /* What to torture. */ module_param(nreaders, int, 0); MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); -module_param(nfakewriters, int, 0); -MODULE_PARM_DESC(nfakewriters, "Number of RCU fake writer threads"); module_param(stat_interval, int, 0); MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s"); module_param(verbose, bool, 0); @@ -73,7 +66,7 @@ MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); module_param(shuffle_interval, int, 0); MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); module_param(torture_type, charp, 0); -MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); +MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh)"); #define TORTURE_FLAG "-torture:" #define PRINTK_STRING(s) \ @@ -87,7 +80,6 @@ static char printk_buf[4096]; static int nrealreaders; static struct task_struct *writer_task; -static struct task_struct **fakewriter_tasks; static struct task_struct **reader_tasks; static struct task_struct *stats_task; static struct task_struct *shuffler_task; @@ -112,12 +104,11 @@ static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_count) = static DEFINE_PER_CPU(long [RCU_TORTURE_PIPE_LEN + 1], rcu_torture_batch) = { 0 }; static atomic_t rcu_torture_wcount[RCU_TORTURE_PIPE_LEN + 1]; -static atomic_t n_rcu_torture_alloc; -static atomic_t n_rcu_torture_alloc_fail; -static atomic_t n_rcu_torture_free; -static atomic_t n_rcu_torture_mberror; -static atomic_t n_rcu_torture_error; -static struct list_head rcu_torture_removed; +atomic_t n_rcu_torture_alloc; +atomic_t n_rcu_torture_alloc_fail; +atomic_t n_rcu_torture_free; +atomic_t n_rcu_torture_mberror; +atomic_t n_rcu_torture_error; /* * Allocate an element from the rcu_tortures pool. @@ -154,7 +145,7 @@ rcu_torture_free(struct rcu_torture *p) struct rcu_random_state { unsigned long rrs_state; - long rrs_count; + unsigned long rrs_count; }; #define RCU_RANDOM_MULT 39916801 /* prime */ @@ -167,7 +158,7 @@ struct rcu_random_state { * Crude but fast random-number generator. Uses a linear congruential * generator, with occasional help from get_random_bytes(). */ -static unsigned long +static long rcu_random(struct rcu_random_state *rrsp) { long refresh; @@ -189,11 +180,9 @@ struct rcu_torture_ops { void (*init)(void); void (*cleanup)(void); int (*readlock)(void); - void (*readdelay)(struct rcu_random_state *rrsp); void (*readunlock)(int idx); int (*completed)(void); void (*deferredfree)(struct rcu_torture *p); - void (*sync)(void); int (*stats)(char *page); char *name; }; @@ -209,18 +198,6 @@ static int rcu_torture_read_lock(void) __acquires(RCU) return 0; } -static void rcu_read_delay(struct rcu_random_state *rrsp) -{ - long delay; - const long longdelay = 200; - - /* We want there to be long-running readers, but not all the time. */ - - delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay); - if (!delay) - udelay(longdelay); -} - static void rcu_torture_read_unlock(int idx) __releases(RCU) { rcu_read_unlock(); @@ -262,54 +239,13 @@ static struct rcu_torture_ops rcu_ops = { .init = NULL, .cleanup = NULL, .readlock = rcu_torture_read_lock, - .readdelay = rcu_read_delay, .readunlock = rcu_torture_read_unlock, .completed = rcu_torture_completed, .deferredfree = rcu_torture_deferred_free, - .sync = synchronize_rcu, .stats = NULL, .name = "rcu" }; -static void rcu_sync_torture_deferred_free(struct rcu_torture *p) -{ - int i; - struct rcu_torture *rp; - struct rcu_torture *rp1; - - cur_ops->sync(); - list_add(&p->rtort_free, &rcu_torture_removed); - list_for_each_entry_safe(rp, rp1, &rcu_torture_removed, rtort_free) { - i = rp->rtort_pipe_count; - if (i > RCU_TORTURE_PIPE_LEN) - i = RCU_TORTURE_PIPE_LEN; - atomic_inc(&rcu_torture_wcount[i]); - if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { - rp->rtort_mbtest = 0; - list_del(&rp->rtort_free); - rcu_torture_free(rp); - } - } -} - -static void rcu_sync_torture_init(void) -{ - INIT_LIST_HEAD(&rcu_torture_removed); -} - -static struct rcu_torture_ops rcu_sync_ops = { - .init = rcu_sync_torture_init, - .cleanup = NULL, - .readlock = rcu_torture_read_lock, - .readdelay = rcu_read_delay, - .readunlock = rcu_torture_read_unlock, - .completed = rcu_torture_completed, - .deferredfree = rcu_sync_torture_deferred_free, - .sync = synchronize_rcu, - .stats = NULL, - .name = "rcu_sync" -}; - /* * Definitions for rcu_bh torture testing. */ @@ -335,176 +271,19 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p) call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); } -struct rcu_bh_torture_synchronize { - struct rcu_head head; - struct completion completion; -}; - -static void rcu_bh_torture_wakeme_after_cb(struct rcu_head *head) -{ - struct rcu_bh_torture_synchronize *rcu; - - rcu = container_of(head, struct rcu_bh_torture_synchronize, head); - complete(&rcu->completion); -} - -static void rcu_bh_torture_synchronize(void) -{ - struct rcu_bh_torture_synchronize rcu; - - init_completion(&rcu.completion); - call_rcu_bh(&rcu.head, rcu_bh_torture_wakeme_after_cb); - wait_for_completion(&rcu.completion); -} - static struct rcu_torture_ops rcu_bh_ops = { .init = NULL, .cleanup = NULL, .readlock = rcu_bh_torture_read_lock, - .readdelay = rcu_read_delay, /* just reuse rcu's version. */ .readunlock = rcu_bh_torture_read_unlock, .completed = rcu_bh_torture_completed, .deferredfree = rcu_bh_torture_deferred_free, - .sync = rcu_bh_torture_synchronize, .stats = NULL, .name = "rcu_bh" }; -static struct rcu_torture_ops rcu_bh_sync_ops = { - .init = rcu_sync_torture_init, - .cleanup = NULL, - .readlock = rcu_bh_torture_read_lock, - .readdelay = rcu_read_delay, /* just reuse rcu's version. */ - .readunlock = rcu_bh_torture_read_unlock, - .completed = rcu_bh_torture_completed, - .deferredfree = rcu_sync_torture_deferred_free, - .sync = rcu_bh_torture_synchronize, - .stats = NULL, - .name = "rcu_bh_sync" -}; - -/* - * Definitions for srcu torture testing. - */ - -static struct srcu_struct srcu_ctl; - -static void srcu_torture_init(void) -{ - init_srcu_struct(&srcu_ctl); - rcu_sync_torture_init(); -} - -static void srcu_torture_cleanup(void) -{ - synchronize_srcu(&srcu_ctl); - cleanup_srcu_struct(&srcu_ctl); -} - -static int srcu_torture_read_lock(void) -{ - return srcu_read_lock(&srcu_ctl); -} - -static void srcu_read_delay(struct rcu_random_state *rrsp) -{ - long delay; - const long uspertick = 1000000 / HZ; - const long longdelay = 10; - - /* We want there to be long-running readers, but not all the time. */ - - delay = rcu_random(rrsp) % (nrealreaders * 2 * longdelay * uspertick); - if (!delay) - schedule_timeout_interruptible(longdelay); -} - -static void srcu_torture_read_unlock(int idx) -{ - srcu_read_unlock(&srcu_ctl, idx); -} - -static int srcu_torture_completed(void) -{ - return srcu_batches_completed(&srcu_ctl); -} - -static void srcu_torture_synchronize(void) -{ - synchronize_srcu(&srcu_ctl); -} - -static int srcu_torture_stats(char *page) -{ - int cnt = 0; - int cpu; - int idx = srcu_ctl.completed & 0x1; - - cnt += sprintf(&page[cnt], "%s%s per-CPU(idx=%d):", - torture_type, TORTURE_FLAG, idx); - for_each_possible_cpu(cpu) { - cnt += sprintf(&page[cnt], " %d(%d,%d)", cpu, - per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx], - per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx]); - } - cnt += sprintf(&page[cnt], "\n"); - return cnt; -} - -static struct rcu_torture_ops srcu_ops = { - .init = srcu_torture_init, - .cleanup = srcu_torture_cleanup, - .readlock = srcu_torture_read_lock, - .readdelay = srcu_read_delay, - .readunlock = srcu_torture_read_unlock, - .completed = srcu_torture_completed, - .deferredfree = rcu_sync_torture_deferred_free, - .sync = srcu_torture_synchronize, - .stats = srcu_torture_stats, - .name = "srcu" -}; - -/* - * Definitions for sched torture testing. - */ - -static int sched_torture_read_lock(void) -{ - preempt_disable(); - return 0; -} - -static void sched_torture_read_unlock(int idx) -{ - preempt_enable(); -} - -static int sched_torture_completed(void) -{ - return 0; -} - -static void sched_torture_synchronize(void) -{ - synchronize_sched(); -} - -static struct rcu_torture_ops sched_ops = { - .init = rcu_sync_torture_init, - .cleanup = NULL, - .readlock = sched_torture_read_lock, - .readdelay = rcu_read_delay, /* just reuse rcu's version. */ - .readunlock = sched_torture_read_unlock, - .completed = sched_torture_completed, - .deferredfree = rcu_sync_torture_deferred_free, - .sync = sched_torture_synchronize, - .stats = NULL, - .name = "sched" -}; - static struct rcu_torture_ops *torture_ops[] = - { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, &srcu_ops, - &sched_ops, NULL }; + { &rcu_ops, &rcu_bh_ops, NULL }; /* * RCU torture writer kthread. Repeatedly substitutes a new structure @@ -550,30 +329,6 @@ rcu_torture_writer(void *arg) return 0; } -/* - * RCU torture fake writer kthread. Repeatedly calls sync, with a random - * delay between calls. - */ -static int -rcu_torture_fakewriter(void *arg) -{ - DEFINE_RCU_RANDOM(rand); - - VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started"); - set_user_nice(current, 19); - - do { - schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10); - udelay(rcu_random(&rand) & 0x3ff); - cur_ops->sync(); - } while (!kthread_should_stop() && !fullstop); - - VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); - while (!kthread_should_stop()) - schedule_timeout_uninterruptible(1); - return 0; -} - /* * RCU torture reader kthread. Repeatedly dereferences rcu_torture_current, * incrementing the corresponding element of the pipeline array. The @@ -604,7 +359,7 @@ rcu_torture_reader(void *arg) } if (p->rtort_mbtest == 0) atomic_inc(&n_rcu_torture_mberror); - cur_ops->readdelay(&rand); + udelay(rcu_random(&rand) & 0x7f); preempt_disable(); pipe_count = p->rtort_pipe_count; if (pipe_count > RCU_TORTURE_PIPE_LEN) { @@ -728,7 +483,7 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */ /* Shuffle tasks such that we allow @rcu_idle_cpu to become idle. A special case * is when @rcu_idle_cpu = -1, when we allow the tasks to run on all CPUs. */ -static void rcu_torture_shuffle_tasks(void) +void rcu_torture_shuffle_tasks(void) { cpumask_t tmp_mask = CPU_MASK_ALL; int i; @@ -752,12 +507,6 @@ static void rcu_torture_shuffle_tasks(void) set_cpus_allowed(reader_tasks[i], tmp_mask); } - if (fakewriter_tasks != NULL) { - for (i = 0; i < nfakewriters; i++) - if (fakewriter_tasks[i]) - set_cpus_allowed(fakewriter_tasks[i], tmp_mask); - } - if (writer_task) set_cpus_allowed(writer_task, tmp_mask); @@ -791,12 +540,11 @@ rcu_torture_shuffle(void *arg) static inline void rcu_torture_print_module_parms(char *tag) { - printk(KERN_ALERT "%s" TORTURE_FLAG - "--- %s: nreaders=%d nfakewriters=%d " + printk(KERN_ALERT "%s" TORTURE_FLAG "--- %s: nreaders=%d " "stat_interval=%d verbose=%d test_no_idle_hz=%d " "shuffle_interval = %d\n", - torture_type, tag, nrealreaders, nfakewriters, - stat_interval, verbose, test_no_idle_hz, shuffle_interval); + torture_type, tag, nrealreaders, stat_interval, verbose, + test_no_idle_hz, shuffle_interval); } static void @@ -831,19 +579,6 @@ rcu_torture_cleanup(void) } rcu_torture_current = NULL; - if (fakewriter_tasks != NULL) { - for (i = 0; i < nfakewriters; i++) { - if (fakewriter_tasks[i] != NULL) { - VERBOSE_PRINTK_STRING( - "Stopping rcu_torture_fakewriter task"); - kthread_stop(fakewriter_tasks[i]); - } - fakewriter_tasks[i] = NULL; - } - kfree(fakewriter_tasks); - fakewriter_tasks = NULL; - } - if (stats_task != NULL) { VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task"); kthread_stop(stats_task); @@ -931,25 +666,7 @@ rcu_torture_init(void) writer_task = NULL; goto unwind; } - fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]), - GFP_KERNEL); - if (fakewriter_tasks == NULL) { - VERBOSE_PRINTK_ERRSTRING("out of memory"); - firsterr = -ENOMEM; - goto unwind; - } - for (i = 0; i < nfakewriters; i++) { - VERBOSE_PRINTK_STRING("Creating rcu_torture_fakewriter task"); - fakewriter_tasks[i] = kthread_run(rcu_torture_fakewriter, NULL, - "rcu_torture_fakewriter"); - if (IS_ERR(fakewriter_tasks[i])) { - firsterr = PTR_ERR(fakewriter_tasks[i]); - VERBOSE_PRINTK_ERRSTRING("Failed to create fakewriter"); - fakewriter_tasks[i] = NULL; - goto unwind; - } - } - reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]), + reader_tasks = kmalloc(nrealreaders * sizeof(reader_tasks[0]), GFP_KERNEL); if (reader_tasks == NULL) { VERBOSE_PRINTK_ERRSTRING("out of memory"); diff --git a/trunk/kernel/rtmutex-debug.c b/trunk/kernel/rtmutex-debug.c index 0c1faa950af7..da8d6bf46457 100644 --- a/trunk/kernel/rtmutex-debug.c +++ b/trunk/kernel/rtmutex-debug.c @@ -16,7 +16,6 @@ * * See rt.c in preempt-rt for proper credits and further information */ -#include #include #include #include diff --git a/trunk/kernel/rtmutex-tester.c b/trunk/kernel/rtmutex-tester.c index 948bd8f643e2..6dcea9dd8c94 100644 --- a/trunk/kernel/rtmutex-tester.c +++ b/trunk/kernel/rtmutex-tester.c @@ -6,7 +6,6 @@ * Copyright (C) 2006, Timesys Corp., Thomas Gleixner * */ -#include #include #include #include diff --git a/trunk/kernel/srcu.c b/trunk/kernel/srcu.c deleted file mode 100644 index 3507cabe963b..000000000000 --- a/trunk/kernel/srcu.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Sleepable Read-Copy Update mechanism for mutual exclusion. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) IBM Corporation, 2006 - * - * Author: Paul McKenney - * - * For detailed explanation of Read-Copy Update mechanism see - - * Documentation/RCU/ *.txt - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * init_srcu_struct - initialize a sleep-RCU structure - * @sp: structure to initialize. - * - * Must invoke this on a given srcu_struct before passing that srcu_struct - * to any other function. Each srcu_struct represents a separate domain - * of SRCU protection. - */ -int init_srcu_struct(struct srcu_struct *sp) -{ - sp->completed = 0; - mutex_init(&sp->mutex); - sp->per_cpu_ref = alloc_percpu(struct srcu_struct_array); - return (sp->per_cpu_ref ? 0 : -ENOMEM); -} - -/* - * srcu_readers_active_idx -- returns approximate number of readers - * active on the specified rank of per-CPU counters. - */ - -static int srcu_readers_active_idx(struct srcu_struct *sp, int idx) -{ - int cpu; - int sum; - - sum = 0; - for_each_possible_cpu(cpu) - sum += per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]; - return sum; -} - -/** - * srcu_readers_active - returns approximate number of readers. - * @sp: which srcu_struct to count active readers (holding srcu_read_lock). - * - * Note that this is not an atomic primitive, and can therefore suffer - * severe errors when invoked on an active srcu_struct. That said, it - * can be useful as an error check at cleanup time. - */ -int srcu_readers_active(struct srcu_struct *sp) -{ - return srcu_readers_active_idx(sp, 0) + srcu_readers_active_idx(sp, 1); -} - -/** - * cleanup_srcu_struct - deconstruct a sleep-RCU structure - * @sp: structure to clean up. - * - * Must invoke this after you are finished using a given srcu_struct that - * was initialized via init_srcu_struct(), else you leak memory. - */ -void cleanup_srcu_struct(struct srcu_struct *sp) -{ - int sum; - - sum = srcu_readers_active(sp); - WARN_ON(sum); /* Leakage unless caller handles error. */ - if (sum != 0) - return; - free_percpu(sp->per_cpu_ref); - sp->per_cpu_ref = NULL; -} - -/** - * srcu_read_lock - register a new reader for an SRCU-protected structure. - * @sp: srcu_struct in which to register the new reader. - * - * Counts the new reader in the appropriate per-CPU element of the - * srcu_struct. Must be called from process context. - * Returns an index that must be passed to the matching srcu_read_unlock(). - */ -int srcu_read_lock(struct srcu_struct *sp) -{ - int idx; - - preempt_disable(); - idx = sp->completed & 0x1; - barrier(); /* ensure compiler looks -once- at sp->completed. */ - per_cpu_ptr(sp->per_cpu_ref, smp_processor_id())->c[idx]++; - srcu_barrier(); /* ensure compiler won't misorder critical section. */ - preempt_enable(); - return idx; -} - -/** - * srcu_read_unlock - unregister a old reader from an SRCU-protected structure. - * @sp: srcu_struct in which to unregister the old reader. - * @idx: return value from corresponding srcu_read_lock(). - * - * Removes the count for the old reader from the appropriate per-CPU - * element of the srcu_struct. Note that this may well be a different - * CPU than that which was incremented by the corresponding srcu_read_lock(). - * Must be called from process context. - */ -void srcu_read_unlock(struct srcu_struct *sp, int idx) -{ - preempt_disable(); - srcu_barrier(); /* ensure compiler won't misorder critical section. */ - per_cpu_ptr(sp->per_cpu_ref, smp_processor_id())->c[idx]--; - preempt_enable(); -} - -/** - * synchronize_srcu - wait for prior SRCU read-side critical-section completion - * @sp: srcu_struct with which to synchronize. - * - * Flip the completed counter, and wait for the old count to drain to zero. - * As with classic RCU, the updater must use some separate means of - * synchronizing concurrent updates. Can block; must be called from - * process context. - * - * Note that it is illegal to call synchornize_srcu() from the corresponding - * SRCU read-side critical section; doing so will result in deadlock. - * However, it is perfectly legal to call synchronize_srcu() on one - * srcu_struct from some other srcu_struct's read-side critical section. - */ -void synchronize_srcu(struct srcu_struct *sp) -{ - int idx; - - idx = sp->completed; - mutex_lock(&sp->mutex); - - /* - * Check to see if someone else did the work for us while we were - * waiting to acquire the lock. We need -two- advances of - * the counter, not just one. If there was but one, we might have - * shown up -after- our helper's first synchronize_sched(), thus - * having failed to prevent CPU-reordering races with concurrent - * srcu_read_unlock()s on other CPUs (see comment below). So we - * either (1) wait for two or (2) supply the second ourselves. - */ - - if ((sp->completed - idx) >= 2) { - mutex_unlock(&sp->mutex); - return; - } - - synchronize_sched(); /* Force memory barrier on all CPUs. */ - - /* - * The preceding synchronize_sched() ensures that any CPU that - * sees the new value of sp->completed will also see any preceding - * changes to data structures made by this CPU. This prevents - * some other CPU from reordering the accesses in its SRCU - * read-side critical section to precede the corresponding - * srcu_read_lock() -- ensuring that such references will in - * fact be protected. - * - * So it is now safe to do the flip. - */ - - idx = sp->completed & 0x1; - sp->completed++; - - synchronize_sched(); /* Force memory barrier on all CPUs. */ - - /* - * At this point, because of the preceding synchronize_sched(), - * all srcu_read_lock() calls using the old counters have completed. - * Their corresponding critical sections might well be still - * executing, but the srcu_read_lock() primitives themselves - * will have finished executing. - */ - - while (srcu_readers_active_idx(sp, idx)) - schedule_timeout_interruptible(1); - - synchronize_sched(); /* Force memory barrier on all CPUs. */ - - /* - * The preceding synchronize_sched() forces all srcu_read_unlock() - * primitives that were executing concurrently with the preceding - * for_each_possible_cpu() loop to have completed by this point. - * More importantly, it also forces the corresponding SRCU read-side - * critical sections to have also completed, and the corresponding - * references to SRCU-protected data items to be dropped. - * - * Note: - * - * Despite what you might think at first glance, the - * preceding synchronize_sched() -must- be within the - * critical section ended by the following mutex_unlock(). - * Otherwise, a task taking the early exit can race - * with a srcu_read_unlock(), which might have executed - * just before the preceding srcu_readers_active() check, - * and whose CPU might have reordered the srcu_read_unlock() - * with the preceding critical section. In this case, there - * is nothing preventing the synchronize_sched() task that is - * taking the early exit from freeing a data structure that - * is still being referenced (out of order) by the task - * doing the srcu_read_unlock(). - * - * Alternatively, the comparison with "2" on the early exit - * could be changed to "3", but this increases synchronize_srcu() - * latency for bulk loads. So the current code is preferred. - */ - - mutex_unlock(&sp->mutex); -} - -/** - * srcu_batches_completed - return batches completed. - * @sp: srcu_struct on which to report batch completion. - * - * Report the number of batches, correlated with, but not necessarily - * precisely the same as, the number of grace periods that have elapsed. - */ - -long srcu_batches_completed(struct srcu_struct *sp) -{ - return sp->completed; -} - -EXPORT_SYMBOL_GPL(init_srcu_struct); -EXPORT_SYMBOL_GPL(cleanup_srcu_struct); -EXPORT_SYMBOL_GPL(srcu_read_lock); -EXPORT_SYMBOL_GPL(srcu_read_unlock); -EXPORT_SYMBOL_GPL(synchronize_srcu); -EXPORT_SYMBOL_GPL(srcu_batches_completed); -EXPORT_SYMBOL_GPL(srcu_readers_active); diff --git a/trunk/kernel/sys.c b/trunk/kernel/sys.c index 98489d82801b..2314867ae34f 100644 --- a/trunk/kernel/sys.c +++ b/trunk/kernel/sys.c @@ -153,7 +153,7 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl, /* * Atomic notifier chain routines. Registration and unregistration - * use a spinlock, and call_chain is synchronized by RCU (no locks). + * use a mutex, and call_chain is synchronized by RCU (no locks). */ /** @@ -401,129 +401,6 @@ int raw_notifier_call_chain(struct raw_notifier_head *nh, EXPORT_SYMBOL_GPL(raw_notifier_call_chain); -/* - * SRCU notifier chain routines. Registration and unregistration - * use a mutex, and call_chain is synchronized by SRCU (no locks). - */ - -/** - * srcu_notifier_chain_register - Add notifier to an SRCU notifier chain - * @nh: Pointer to head of the SRCU notifier chain - * @n: New entry in notifier chain - * - * Adds a notifier to an SRCU notifier chain. - * Must be called in process context. - * - * Currently always returns zero. - */ - -int srcu_notifier_chain_register(struct srcu_notifier_head *nh, - struct notifier_block *n) -{ - int ret; - - /* - * This code gets used during boot-up, when task switching is - * not yet working and interrupts must remain disabled. At - * such times we must not call mutex_lock(). - */ - if (unlikely(system_state == SYSTEM_BOOTING)) - return notifier_chain_register(&nh->head, n); - - mutex_lock(&nh->mutex); - ret = notifier_chain_register(&nh->head, n); - mutex_unlock(&nh->mutex); - return ret; -} - -EXPORT_SYMBOL_GPL(srcu_notifier_chain_register); - -/** - * srcu_notifier_chain_unregister - Remove notifier from an SRCU notifier chain - * @nh: Pointer to head of the SRCU notifier chain - * @n: Entry to remove from notifier chain - * - * Removes a notifier from an SRCU notifier chain. - * Must be called from process context. - * - * Returns zero on success or %-ENOENT on failure. - */ -int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, - struct notifier_block *n) -{ - int ret; - - /* - * This code gets used during boot-up, when task switching is - * not yet working and interrupts must remain disabled. At - * such times we must not call mutex_lock(). - */ - if (unlikely(system_state == SYSTEM_BOOTING)) - return notifier_chain_unregister(&nh->head, n); - - mutex_lock(&nh->mutex); - ret = notifier_chain_unregister(&nh->head, n); - mutex_unlock(&nh->mutex); - synchronize_srcu(&nh->srcu); - return ret; -} - -EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister); - -/** - * srcu_notifier_call_chain - Call functions in an SRCU notifier chain - * @nh: Pointer to head of the SRCU notifier chain - * @val: Value passed unmodified to notifier function - * @v: Pointer passed unmodified to notifier function - * - * Calls each function in a notifier chain in turn. The functions - * run in a process context, so they are allowed to block. - * - * If the return value of the notifier can be and'ed - * with %NOTIFY_STOP_MASK then srcu_notifier_call_chain - * will return immediately, with the return value of - * the notifier function which halted execution. - * Otherwise the return value is the return value - * of the last notifier function called. - */ - -int srcu_notifier_call_chain(struct srcu_notifier_head *nh, - unsigned long val, void *v) -{ - int ret; - int idx; - - idx = srcu_read_lock(&nh->srcu); - ret = notifier_call_chain(&nh->head, val, v); - srcu_read_unlock(&nh->srcu, idx); - return ret; -} - -EXPORT_SYMBOL_GPL(srcu_notifier_call_chain); - -/** - * srcu_init_notifier_head - Initialize an SRCU notifier head - * @nh: Pointer to head of the srcu notifier chain - * - * Unlike other sorts of notifier heads, SRCU notifier heads require - * dynamic initialization. Be sure to call this routine before - * calling any of the other SRCU notifier routines for this head. - * - * If an SRCU notifier head is deallocated, it must first be cleaned - * up by calling srcu_cleanup_notifier_head(). Otherwise the head's - * per-cpu data (used by the SRCU mechanism) will leak. - */ - -void srcu_init_notifier_head(struct srcu_notifier_head *nh) -{ - mutex_init(&nh->mutex); - if (init_srcu_struct(&nh->srcu) < 0) - BUG(); - nh->head = NULL; -} - -EXPORT_SYMBOL_GPL(srcu_init_notifier_head); - /** * register_reboot_notifier - Register function to be called at reboot time * @nb: Info about notifier function to be called diff --git a/trunk/mm/filemap.c b/trunk/mm/filemap.c index 3464b681f844..ec469235985d 100644 --- a/trunk/mm/filemap.c +++ b/trunk/mm/filemap.c @@ -1139,11 +1139,11 @@ int file_read_actor(read_descriptor_t *desc, struct page *page, } /** - * generic_file_aio_read - generic filesystem read routine + * __generic_file_aio_read - generic filesystem read routine * @iocb: kernel I/O control block * @iov: io vector request * @nr_segs: number of segments in the iovec - * @pos: current file position + * @ppos: current file position * * This is the "read()" routine for all filesystems * that can use the page cache directly. @@ -1198,10 +1198,8 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, if (retval > 0) *ppos = pos + retval; } - if (likely(retval != 0)) { - file_accessed(filp); - goto out; - } + file_accessed(filp); + goto out; } retval = 0; diff --git a/trunk/mm/filemap.h b/trunk/mm/filemap.h index 3f2a343c6015..c2bff04c84ed 100644 --- a/trunk/mm/filemap.h +++ b/trunk/mm/filemap.h @@ -12,7 +12,6 @@ #include #include #include -#include #include size_t diff --git a/trunk/mm/hugetlb.c b/trunk/mm/hugetlb.c index 1d709ff528e1..7c7d03dbf73d 100644 --- a/trunk/mm/hugetlb.c +++ b/trunk/mm/hugetlb.c @@ -364,8 +364,6 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, pte_t *ptep; pte_t pte; struct page *page; - struct page *tmp; - LIST_HEAD(page_list); WARN_ON(!is_vm_hugetlb_page(vma)); BUG_ON(start & ~HPAGE_MASK); @@ -386,16 +384,12 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, continue; page = pte_page(pte); - list_add(&page->lru, &page_list); + put_page(page); add_mm_counter(mm, file_rss, (int) -(HPAGE_SIZE / PAGE_SIZE)); } spin_unlock(&mm->page_table_lock); flush_tlb_range(vma, start, end); - list_for_each_entry_safe(page, tmp, &page_list, lru) { - list_del(&page->lru); - put_page(page); - } } static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, diff --git a/trunk/mm/page_alloc.c b/trunk/mm/page_alloc.c index a8c003e7b3d5..4f59d90b81e6 100644 --- a/trunk/mm/page_alloc.c +++ b/trunk/mm/page_alloc.c @@ -900,8 +900,7 @@ int zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags) { /* free_pages my go negative - that's OK */ - unsigned long min = mark; - long free_pages = z->free_pages - (1 << order) + 1; + long min = mark, free_pages = z->free_pages - (1 << order) + 1; int o; if (alloc_flags & ALLOC_HIGH) @@ -2051,8 +2050,8 @@ int __init early_pfn_to_nid(unsigned long pfn) /** * free_bootmem_with_active_regions - Call free_bootmem_node for each active range - * @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed. - * @max_low_pfn: The highest PFN that will be passed to free_bootmem_node + * @nid: The node to free memory on. If MAX_NUMNODES, all nodes are freed + * @max_low_pfn: The highest PFN that till be passed to free_bootmem_node * * If an architecture guarantees that all ranges registered with * add_active_ranges() contain no holes and may be freed, this @@ -2082,11 +2081,11 @@ void __init free_bootmem_with_active_regions(int nid, /** * sparse_memory_present_with_active_regions - Call memory_present for each active range - * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used. + * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used * * If an architecture guarantees that all ranges registered with * add_active_ranges() contain no holes and may be freed, this - * function may be used instead of calling memory_present() manually. + * this function may be used instead of calling memory_present() manually. */ void __init sparse_memory_present_with_active_regions(int nid) { @@ -2156,14 +2155,14 @@ static void __init account_node_boundary(unsigned int nid, /** * get_pfn_range_for_nid - Return the start and end page frames for a node - * @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned. - * @start_pfn: Passed by reference. On return, it will have the node start_pfn. - * @end_pfn: Passed by reference. On return, it will have the node end_pfn. + * @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned + * @start_pfn: Passed by reference. On return, it will have the node start_pfn + * @end_pfn: Passed by reference. On return, it will have the node end_pfn * * It returns the start and end page frame of a node based on information * provided by an arch calling add_active_range(). If called for a node * with no available memory, a warning is printed and the start and end - * PFNs will be 0. + * PFNs will be 0 */ void __init get_pfn_range_for_nid(unsigned int nid, unsigned long *start_pfn, unsigned long *end_pfn) @@ -2216,7 +2215,7 @@ unsigned long __init zone_spanned_pages_in_node(int nid, /* * Return the number of holes in a range on a node. If nid is MAX_NUMNODES, - * then all holes in the requested range will be accounted for. + * then all holes in the requested range will be accounted for */ unsigned long __init __absent_pages_in_range(int nid, unsigned long range_start_pfn, @@ -2269,7 +2268,7 @@ unsigned long __init __absent_pages_in_range(int nid, * @start_pfn: The start PFN to start searching for holes * @end_pfn: The end PFN to stop searching for holes * - * It returns the number of pages frames in memory holes within a range. + * It returns the number of pages frames in memory holes within a range */ unsigned long __init absent_pages_in_range(unsigned long start_pfn, unsigned long end_pfn) @@ -2583,12 +2582,11 @@ void __init shrink_active_range(unsigned int nid, unsigned long old_end_pfn, /** * remove_all_active_ranges - Remove all currently registered regions - * * During discovery, it may be found that a table like SRAT is invalid * and an alternative discovery method must be used. This function removes * all currently registered regions. */ -void __init remove_all_active_ranges(void) +void __init remove_all_active_ranges() { memset(early_node_map, 0, sizeof(early_node_map)); nr_nodemap_entries = 0; @@ -2638,7 +2636,7 @@ unsigned long __init find_min_pfn_for_node(unsigned long nid) * find_min_pfn_with_active_regions - Find the minimum PFN registered * * It returns the minimum PFN based on information provided via - * add_active_range(). + * add_active_range() */ unsigned long __init find_min_pfn_with_active_regions(void) { @@ -2649,7 +2647,7 @@ unsigned long __init find_min_pfn_with_active_regions(void) * find_max_pfn_with_active_regions - Find the maximum PFN registered * * It returns the maximum PFN based on information provided via - * add_active_range(). + * add_active_range() */ unsigned long __init find_max_pfn_with_active_regions(void) { @@ -2664,7 +2662,10 @@ unsigned long __init find_max_pfn_with_active_regions(void) /** * free_area_init_nodes - Initialise all pg_data_t and zone data - * @max_zone_pfn: an array of max PFNs for each zone + * @arch_max_dma_pfn: The maximum PFN usable for ZONE_DMA + * @arch_max_dma32_pfn: The maximum PFN usable for ZONE_DMA32 + * @arch_max_low_pfn: The maximum PFN usable for ZONE_NORMAL + * @arch_max_high_pfn: The maximum PFN usable for ZONE_HIGHMEM * * This will call free_area_init_node() for each active node in the system. * Using the page ranges provided by add_active_range(), the size of each @@ -2722,15 +2723,14 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn) #endif /* CONFIG_ARCH_POPULATES_NODE_MAP */ /** - * set_dma_reserve - set the specified number of pages reserved in the first zone - * @new_dma_reserve: The number of pages to mark reserved + * set_dma_reserve - Account the specified number of pages reserved in ZONE_DMA + * @new_dma_reserve - The number of pages to mark reserved * * The per-cpu batchsize and zone watermarks are determined by present_pages. * In the DMA zone, a significant percentage may be consumed by kernel image * and other unfreeable allocations which can skew the watermarks badly. This - * function may optionally be used to account for unfreeable pages in the - * first zone (e.g., ZONE_DMA). The effect will be lower watermarks and - * smaller per-cpu batchsize. + * function may optionally be used to account for unfreeable pages in + * ZONE_DMA. The effect will be lower watermarks and smaller per-cpu batchsize */ void __init set_dma_reserve(unsigned long new_dma_reserve) { @@ -2843,11 +2843,10 @@ static void setup_per_zone_lowmem_reserve(void) calculate_totalreserve_pages(); } -/** - * setup_per_zone_pages_min - called when min_free_kbytes changes. - * - * Ensures that the pages_{min,low,high} values for each zone are set correctly - * with respect to min_free_kbytes. +/* + * setup_per_zone_pages_min - called when min_free_kbytes changes. Ensures + * that the pages_{min,low,high} values for each zone are set correctly + * with respect to min_free_kbytes. */ void setup_per_zone_pages_min(void) { diff --git a/trunk/mm/readahead.c b/trunk/mm/readahead.c index 1ba736ac0367..aa7ec424656a 100644 --- a/trunk/mm/readahead.c +++ b/trunk/mm/readahead.c @@ -38,7 +38,6 @@ file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping) ra->ra_pages = mapping->backing_dev_info->ra_pages; ra->prev_page = -1; } -EXPORT_SYMBOL_GPL(file_ra_state_init); /* * Return max readahead size for this inode in number-of-pages. diff --git a/trunk/mm/slab.c b/trunk/mm/slab.c index c23b99250df2..f3514351aed8 100644 --- a/trunk/mm/slab.c +++ b/trunk/mm/slab.c @@ -86,7 +86,6 @@ * All object allocations for a node occur from node specific slab lists. */ -#include #include #include #include @@ -3488,25 +3487,22 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, } -#ifdef CONFIG_DEBUG_SLAB void *__kmalloc(size_t size, gfp_t flags) { +#ifndef CONFIG_DEBUG_SLAB + return __do_kmalloc(size, flags, NULL); +#else return __do_kmalloc(size, flags, __builtin_return_address(0)); +#endif } EXPORT_SYMBOL(__kmalloc); +#ifdef CONFIG_DEBUG_SLAB void *__kmalloc_track_caller(size_t size, gfp_t flags, void *caller) { return __do_kmalloc(size, flags, caller); } EXPORT_SYMBOL(__kmalloc_track_caller); - -#else -void *__kmalloc(size_t size, gfp_t flags) -{ - return __do_kmalloc(size, flags, NULL); -} -EXPORT_SYMBOL(__kmalloc); #endif /** diff --git a/trunk/mm/util.c b/trunk/mm/util.c index ace2aea69f1a..e14fa84ef39a 100644 --- a/trunk/mm/util.c +++ b/trunk/mm/util.c @@ -11,7 +11,7 @@ */ void *__kzalloc(size_t size, gfp_t flags) { - void *ret = kmalloc_track_caller(size, flags); + void *ret = ____kmalloc(size, flags); if (ret) memset(ret, 0, size); return ret; @@ -33,7 +33,7 @@ char *kstrdup(const char *s, gfp_t gfp) return NULL; len = strlen(s) + 1; - buf = kmalloc_track_caller(len, gfp); + buf = ____kmalloc(len, gfp); if (buf) memcpy(buf, s, len); return buf; @@ -51,7 +51,7 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp) { void *p; - p = kmalloc_track_caller(len, gfp); + p = ____kmalloc(len, gfp); if (p) memcpy(p, src, len); return p; diff --git a/trunk/mm/vmstat.c b/trunk/mm/vmstat.c index a2b6a9f96e5c..45b124e012f5 100644 --- a/trunk/mm/vmstat.c +++ b/trunk/mm/vmstat.c @@ -9,7 +9,6 @@ * Christoph Lameter */ -#include #include #include #include diff --git a/trunk/net/atm/lec.h b/trunk/net/atm/lec.h index 8ac6b7321635..877f50939696 100644 --- a/trunk/net/atm/lec.h +++ b/trunk/net/atm/lec.h @@ -7,7 +7,6 @@ #ifndef _LEC_H_ #define _LEC_H_ -#include #include #include #include diff --git a/trunk/net/bridge/netfilter/ebt_mark.c b/trunk/net/bridge/netfilter/ebt_mark.c index b54306a934e5..770c0df972a3 100644 --- a/trunk/net/bridge/netfilter/ebt_mark.c +++ b/trunk/net/bridge/netfilter/ebt_mark.c @@ -22,37 +22,24 @@ static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr, const void *data, unsigned int datalen) { struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data; - int action = info->target & -16; - if (action == MARK_SET_VALUE) + if ((*pskb)->nfmark != info->mark) (*pskb)->nfmark = info->mark; - else if (action == MARK_OR_VALUE) - (*pskb)->nfmark |= info->mark; - else if (action == MARK_AND_VALUE) - (*pskb)->nfmark &= info->mark; - else - (*pskb)->nfmark ^= info->mark; - return info->target | -16; + return info->target; } static int ebt_target_mark_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data; - int tmp; if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info))) return -EINVAL; - tmp = info->target | -16; - if (BASE_CHAIN && tmp == EBT_RETURN) + if (BASE_CHAIN && info->target == EBT_RETURN) return -EINVAL; CLEAR_BASE_CHAIN_BIT; - if (tmp < -NUM_STANDARD_TARGETS || tmp >= 0) - return -EINVAL; - tmp = info->target & -16; - if (tmp != MARK_SET_VALUE && tmp != MARK_OR_VALUE && - tmp != MARK_AND_VALUE && tmp != MARK_XOR_VALUE) + if (INVALID_TARGET) return -EINVAL; return 0; } diff --git a/trunk/net/core/fib_rules.c b/trunk/net/core/fib_rules.c index a99d87d82b7f..6b0e63cacd93 100644 --- a/trunk/net/core/fib_rules.c +++ b/trunk/net/core/fib_rules.c @@ -8,7 +8,6 @@ * Authors: Thomas Graf */ -#include #include #include #include diff --git a/trunk/net/core/neighbour.c b/trunk/net/core/neighbour.c index b4b478353b27..8ce8c471d868 100644 --- a/trunk/net/core/neighbour.c +++ b/trunk/net/core/neighbour.c @@ -344,12 +344,12 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, { struct neighbour *n; int key_len = tbl->key_len; - u32 hash_val = tbl->hash(pkey, dev); + u32 hash_val = tbl->hash(pkey, dev) & tbl->hash_mask; NEIGH_CACHE_STAT_INC(tbl, lookups); read_lock_bh(&tbl->lock); - for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) { + for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) { neigh_hold(n); NEIGH_CACHE_STAT_INC(tbl, hits); @@ -364,12 +364,12 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, const void *pkey) { struct neighbour *n; int key_len = tbl->key_len; - u32 hash_val = tbl->hash(pkey, NULL); + u32 hash_val = tbl->hash(pkey, NULL) & tbl->hash_mask; NEIGH_CACHE_STAT_INC(tbl, lookups); read_lock_bh(&tbl->lock); - for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) { + for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { if (!memcmp(n->primary_key, pkey, key_len)) { neigh_hold(n); NEIGH_CACHE_STAT_INC(tbl, hits); @@ -1998,12 +1998,12 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, int rc, h, s_h = cb->args[1]; int idx, s_idx = idx = cb->args[2]; - read_lock_bh(&tbl->lock); for (h = 0; h <= tbl->hash_mask; h++) { if (h < s_h) continue; if (h > s_h) s_idx = 0; + read_lock_bh(&tbl->lock); for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next, idx++) { if (idx < s_idx) continue; @@ -2016,8 +2016,8 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, goto out; } } + read_unlock_bh(&tbl->lock); } - read_unlock_bh(&tbl->lock); rc = skb->len; out: cb->args[1] = h; diff --git a/trunk/net/core/skbuff.c b/trunk/net/core/skbuff.c index 3c23760c5827..c448c7f6fde2 100644 --- a/trunk/net/core/skbuff.c +++ b/trunk/net/core/skbuff.c @@ -156,8 +156,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, /* Get the DATA. Size must match skb_add_mtu(). */ size = SKB_DATA_ALIGN(size); - data = kmalloc_track_caller(size + sizeof(struct skb_shared_info), - gfp_mask); + data = ____kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); if (!data) goto nodata; diff --git a/trunk/net/ipv4/Kconfig b/trunk/net/ipv4/Kconfig index 5572071af735..d172a9804448 100644 --- a/trunk/net/ipv4/Kconfig +++ b/trunk/net/ipv4/Kconfig @@ -434,15 +434,6 @@ config INET_XFRM_MODE_TUNNEL If unsure, say Y. -config INET_XFRM_MODE_BEET - tristate "IP: IPsec BEET mode" - default y - select XFRM - ---help--- - Support for IPsec BEET mode. - - If unsure, say Y. - config INET_DIAG tristate "INET: socket monitoring interface" default y diff --git a/trunk/net/ipv4/Makefile b/trunk/net/ipv4/Makefile index 15645c51520c..f66049e28aeb 100644 --- a/trunk/net/ipv4/Makefile +++ b/trunk/net/ipv4/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_INET_AH) += ah4.o obj-$(CONFIG_INET_ESP) += esp4.o obj-$(CONFIG_INET_IPCOMP) += ipcomp.o obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o -obj-$(CONFIG_INET_XFRM_MODE_BEET) += xfrm4_mode_beet.o obj-$(CONFIG_INET_TUNNEL) += tunnel4.o obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o diff --git a/trunk/net/ipv4/esp4.c b/trunk/net/ipv4/esp4.c index b5c205b57669..13b29360d102 100644 --- a/trunk/net/ipv4/esp4.c +++ b/trunk/net/ipv4/esp4.c @@ -253,8 +253,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb) * as per draft-ietf-ipsec-udp-encaps-06, * section 3.1.2 */ - if (x->props.mode == XFRM_MODE_TRANSPORT || - x->props.mode == XFRM_MODE_BEET) + if (x->props.mode == XFRM_MODE_TRANSPORT) skb->ip_summed = CHECKSUM_UNNECESSARY; } @@ -272,28 +271,17 @@ static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) { struct esp_data *esp = x->data; u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4); - int enclen = 0; - - switch (x->props.mode) { - case XFRM_MODE_TUNNEL: - mtu = ALIGN(mtu +2, blksize); - break; - default: - case XFRM_MODE_TRANSPORT: - /* The worst case */ + + if (x->props.mode == XFRM_MODE_TUNNEL) { + mtu = ALIGN(mtu + 2, blksize); + } else { + /* The worst case. */ mtu = ALIGN(mtu + 2, 4) + blksize - 4; - break; - case XFRM_MODE_BEET: - /* The worst case. */ - enclen = IPV4_BEET_PHMAXLEN; - mtu = ALIGN(mtu + enclen + 2, blksize); - break; } - if (esp->conf.padlen) mtu = ALIGN(mtu, esp->conf.padlen); - return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen; + return mtu + x->props.header_len + esp->auth.icv_trunc_len; } static void esp4_err(struct sk_buff *skb, u32 info) diff --git a/trunk/net/ipv4/ipcomp.c b/trunk/net/ipv4/ipcomp.c index 3839b706142e..2017d36024d4 100644 --- a/trunk/net/ipv4/ipcomp.c +++ b/trunk/net/ipv4/ipcomp.c @@ -206,7 +206,6 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info) static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x) { struct xfrm_state *t; - u8 mode = XFRM_MODE_TUNNEL; t = xfrm_state_alloc(); if (t == NULL) @@ -217,9 +216,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x) t->id.daddr.a4 = x->id.daddr.a4; memcpy(&t->sel, &x->sel, sizeof(t->sel)); t->props.family = AF_INET; - if (x->props.mode == XFRM_MODE_BEET) - mode = x->props.mode; - t->props.mode = mode; + t->props.mode = XFRM_MODE_TUNNEL; t->props.saddr.a4 = x->props.saddr.a4; t->props.flags = x->props.flags; diff --git a/trunk/net/ipv4/ipvs/ip_vs_core.c b/trunk/net/ipv4/ipvs/ip_vs_core.c index 1445bb47fea4..6dee03935f78 100644 --- a/trunk/net/ipv4/ipvs/ip_vs_core.c +++ b/trunk/net/ipv4/ipvs/ip_vs_core.c @@ -813,16 +813,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff **pskb, skb->nh.iph->saddr = cp->vaddr; ip_send_check(skb->nh.iph); - /* For policy routing, packets originating from this - * machine itself may be routed differently to packets - * passing through. We want this packet to be routed as - * if it came from this machine itself. So re-compute - * the routing information. - */ - if (ip_route_me_harder(pskb, RTN_LOCAL) != 0) - goto drop; - skb = *pskb; - IP_VS_DBG_PKT(10, pp, skb, 0, "After SNAT"); ip_vs_out_stats(cp, skb); diff --git a/trunk/net/ipv4/netfilter.c b/trunk/net/ipv4/netfilter.c index e2005c6810a4..5ac15379a0cf 100644 --- a/trunk/net/ipv4/netfilter.c +++ b/trunk/net/ipv4/netfilter.c @@ -8,7 +8,7 @@ #include /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ -int ip_route_me_harder(struct sk_buff **pskb, unsigned addr_type) +int ip_route_me_harder(struct sk_buff **pskb) { struct iphdr *iph = (*pskb)->nh.iph; struct rtable *rt; @@ -16,13 +16,10 @@ int ip_route_me_harder(struct sk_buff **pskb, unsigned addr_type) struct dst_entry *odst; unsigned int hh_len; - if (addr_type == RTN_UNSPEC) - addr_type = inet_addr_type(iph->saddr); - /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause * packets with foreign saddr to appear on the NF_IP_LOCAL_OUT hook. */ - if (addr_type == RTN_LOCAL) { + if (inet_addr_type(iph->saddr) == RTN_LOCAL) { fl.nl_u.ip4_u.daddr = iph->daddr; fl.nl_u.ip4_u.saddr = iph->saddr; fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); @@ -159,7 +156,7 @@ static int nf_ip_reroute(struct sk_buff **pskb, const struct nf_info *info) if (!(iph->tos == rt_info->tos && iph->daddr == rt_info->daddr && iph->saddr == rt_info->saddr)) - return ip_route_me_harder(pskb, RTN_UNSPEC); + return ip_route_me_harder(pskb); } return 0; } diff --git a/trunk/net/ipv4/netfilter/ip_nat_standalone.c b/trunk/net/ipv4/netfilter/ip_nat_standalone.c index d85d2de50449..021395b67463 100644 --- a/trunk/net/ipv4/netfilter/ip_nat_standalone.c +++ b/trunk/net/ipv4/netfilter/ip_nat_standalone.c @@ -265,8 +265,7 @@ ip_nat_local_fn(unsigned int hooknum, ct->tuplehash[!dir].tuple.src.u.all #endif ) - if (ip_route_me_harder(pskb, RTN_UNSPEC)) - ret = NF_DROP; + return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; } return ret; } diff --git a/trunk/net/ipv4/netfilter/ipt_REJECT.c b/trunk/net/ipv4/netfilter/ipt_REJECT.c index ad0312d0e4fd..fd0c05efed8a 100644 --- a/trunk/net/ipv4/netfilter/ipt_REJECT.c +++ b/trunk/net/ipv4/netfilter/ipt_REJECT.c @@ -38,16 +38,76 @@ MODULE_DESCRIPTION("iptables REJECT target module"); #define DEBUGP(format, args...) #endif +static inline struct rtable *route_reverse(struct sk_buff *skb, + struct tcphdr *tcph, int hook) +{ + struct iphdr *iph = skb->nh.iph; + struct dst_entry *odst; + struct flowi fl = {}; + struct rtable *rt; + + /* We don't require ip forwarding to be enabled to be able to + * send a RST reply for bridged traffic. */ + if (hook != NF_IP_FORWARD +#ifdef CONFIG_BRIDGE_NETFILTER + || (skb->nf_bridge && skb->nf_bridge->mask & BRNF_BRIDGED) +#endif + ) { + fl.nl_u.ip4_u.daddr = iph->saddr; + if (hook == NF_IP_LOCAL_IN) + fl.nl_u.ip4_u.saddr = iph->daddr; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + + if (ip_route_output_key(&rt, &fl) != 0) + return NULL; + } else { + /* non-local src, find valid iif to satisfy + * rp-filter when calling ip_route_input. */ + fl.nl_u.ip4_u.daddr = iph->daddr; + if (ip_route_output_key(&rt, &fl) != 0) + return NULL; + + odst = skb->dst; + if (ip_route_input(skb, iph->saddr, iph->daddr, + RT_TOS(iph->tos), rt->u.dst.dev) != 0) { + dst_release(&rt->u.dst); + return NULL; + } + dst_release(&rt->u.dst); + rt = (struct rtable *)skb->dst; + skb->dst = odst; + + fl.nl_u.ip4_u.daddr = iph->saddr; + fl.nl_u.ip4_u.saddr = iph->daddr; + fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); + } + + if (rt->u.dst.error) { + dst_release(&rt->u.dst); + return NULL; + } + + fl.proto = IPPROTO_TCP; + fl.fl_ip_sport = tcph->dest; + fl.fl_ip_dport = tcph->source; + security_skb_classify_flow(skb, &fl); + + xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0); + + return rt; +} + /* Send RST reply */ static void send_reset(struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; struct iphdr *iph = oldskb->nh.iph; struct tcphdr _otcph, *oth, *tcph; + struct rtable *rt; __be16 tmp_port; __be32 tmp_addr; int needs_ack; - unsigned int addr_type; + int hh_len; /* IP header checks: fragment. */ if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)) @@ -66,13 +126,23 @@ static void send_reset(struct sk_buff *oldskb, int hook) if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP)) return; + if ((rt = route_reverse(oldskb, oth, hook)) == NULL) + return; + + hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); + /* We need a linear, writeable skb. We also need to expand headroom in case hh_len of incoming interface < hh_len of outgoing interface */ - nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), + nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb), GFP_ATOMIC); - if (!nskb) + if (!nskb) { + dst_release(&rt->u.dst); return; + } + + dst_release(nskb->dst); + nskb->dst = &rt->u.dst; /* This packet will not be the same as the other: clear nf fields */ nf_reset(nskb); @@ -114,21 +184,6 @@ static void send_reset(struct sk_buff *oldskb, int hook) tcph->window = 0; tcph->urg_ptr = 0; - /* Set DF, id = 0 */ - nskb->nh.iph->frag_off = htons(IP_DF); - nskb->nh.iph->id = 0; - - addr_type = RTN_UNSPEC; - if (hook != NF_IP_FORWARD -#ifdef CONFIG_BRIDGE_NETFILTER - || (nskb->nf_bridge && nskb->nf_bridge->mask & BRNF_BRIDGED) -#endif - ) - addr_type = RTN_LOCAL; - - if (ip_route_me_harder(&nskb, addr_type)) - goto free_nskb; - /* Adjust TCP checksum */ nskb->ip_summed = CHECKSUM_NONE; tcph->check = 0; @@ -137,8 +192,12 @@ static void send_reset(struct sk_buff *oldskb, int hook) nskb->nh.iph->daddr, csum_partial((char *)tcph, sizeof(struct tcphdr), 0)); - /* Adjust IP TTL */ + + /* Adjust IP TTL, DF */ nskb->nh.iph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); + /* Set DF, id = 0 */ + nskb->nh.iph->frag_off = htons(IP_DF); + nskb->nh.iph->id = 0; /* Adjust IP checksum */ nskb->nh.iph->check = 0; diff --git a/trunk/net/ipv4/netfilter/iptable_mangle.c b/trunk/net/ipv4/netfilter/iptable_mangle.c index b91f3582359b..e62ea2bb9c0a 100644 --- a/trunk/net/ipv4/netfilter/iptable_mangle.c +++ b/trunk/net/ipv4/netfilter/iptable_mangle.c @@ -157,8 +157,7 @@ ipt_local_hook(unsigned int hook, || (*pskb)->nfmark != nfmark #endif || (*pskb)->nh.iph->tos != tos)) - if (ip_route_me_harder(pskb, RTN_UNSPEC)) - ret = NF_DROP; + return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; return ret; } diff --git a/trunk/net/ipv4/tcp_input.c b/trunk/net/ipv4/tcp_input.c index cf06accbe687..3f884cea14ff 100644 --- a/trunk/net/ipv4/tcp_input.c +++ b/trunk/net/ipv4/tcp_input.c @@ -2259,7 +2259,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, __s32 *seq_rtt_p) u32 pkts_acked = 0; void (*rtt_sample)(struct sock *sk, u32 usrtt) = icsk->icsk_ca_ops->rtt_sample; - struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + struct timeval tv; while ((skb = skb_peek(&sk->sk_write_queue)) && skb != sk->sk_send_head) { diff --git a/trunk/net/ipv4/udp.c b/trunk/net/ipv4/udp.c index 865d75214a9a..6d6142f9c478 100644 --- a/trunk/net/ipv4/udp.c +++ b/trunk/net/ipv4/udp.c @@ -675,8 +675,6 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, udp_flush_pending_frames(sk); else if (!corkreq) err = udp_push_pending_frames(sk, up); - else if (unlikely(skb_queue_empty(&sk->sk_write_queue))) - up->pending = 0; release_sock(sk); out: diff --git a/trunk/net/ipv4/xfrm4_mode_beet.c b/trunk/net/ipv4/xfrm4_mode_beet.c deleted file mode 100644 index 89cf59ea7bbe..000000000000 --- a/trunk/net/ipv4/xfrm4_mode_beet.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * xfrm4_mode_beet.c - BEET mode encapsulation for IPv4. - * - * Copyright (c) 2006 Diego Beltrami - * Miika Komu - * Herbert Xu - * Abhinav Pathak - * Jeff Ahrenholz - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Add encapsulation header. - * - * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt. - * The following fields in it shall be filled in by x->type->output: - * tot_len - * check - * - * On exit, skb->h will be set to the start of the payload to be processed - * by x->type->output and skb->nh will be set to the top IP header. - */ -static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb) -{ - struct iphdr *iph, *top_iph = NULL; - int hdrlen, optlen; - - iph = skb->nh.iph; - skb->h.ipiph = iph; - - hdrlen = 0; - optlen = iph->ihl * 4 - sizeof(*iph); - if (unlikely(optlen)) - hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4); - - skb->nh.raw = skb_push(skb, x->props.header_len + hdrlen); - top_iph = skb->nh.iph; - hdrlen = iph->ihl * 4 - optlen; - skb->h.raw += hdrlen; - - memmove(top_iph, iph, hdrlen); - if (unlikely(optlen)) { - struct ip_beet_phdr *ph; - - BUG_ON(optlen < 0); - - ph = (struct ip_beet_phdr *)skb->h.raw; - ph->padlen = 4 - (optlen & 4); - ph->hdrlen = (optlen + ph->padlen + sizeof(*ph)) / 8; - ph->nexthdr = top_iph->protocol; - - top_iph->protocol = IPPROTO_BEETPH; - top_iph->ihl = sizeof(struct iphdr) / 4; - } - - top_iph->saddr = x->props.saddr.a4; - top_iph->daddr = x->id.daddr.a4; - - return 0; -} - -static int xfrm4_beet_input(struct xfrm_state *x, struct sk_buff *skb) -{ - struct iphdr *iph = skb->nh.iph; - int phlen = 0; - int optlen = 0; - __u8 ph_nexthdr = 0, protocol = 0; - int err = -EINVAL; - - protocol = iph->protocol; - - if (unlikely(iph->protocol == IPPROTO_BEETPH)) { - struct ip_beet_phdr *ph = (struct ip_beet_phdr*)(iph + 1); - - if (!pskb_may_pull(skb, sizeof(*ph))) - goto out; - - phlen = ph->hdrlen * 8; - optlen = phlen - ph->padlen - sizeof(*ph); - if (optlen < 0 || optlen & 3 || optlen > 250) - goto out; - - if (!pskb_may_pull(skb, phlen)) - goto out; - - ph_nexthdr = ph->nexthdr; - } - - skb_push(skb, sizeof(*iph) - phlen + optlen); - memmove(skb->data, skb->nh.raw, sizeof(*iph)); - skb->nh.raw = skb->data; - - iph = skb->nh.iph; - iph->ihl = (sizeof(*iph) + optlen) / 4; - iph->tot_len = htons(skb->len); - iph->daddr = x->sel.daddr.a4; - iph->saddr = x->sel.saddr.a4; - if (ph_nexthdr) - iph->protocol = ph_nexthdr; - else - iph->protocol = protocol; - iph->check = 0; - iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); - err = 0; -out: - return err; -} - -static struct xfrm_mode xfrm4_beet_mode = { - .input = xfrm4_beet_input, - .output = xfrm4_beet_output, - .owner = THIS_MODULE, - .encap = XFRM_MODE_BEET, -}; - -static int __init xfrm4_beet_init(void) -{ - return xfrm_register_mode(&xfrm4_beet_mode, AF_INET); -} - -static void __exit xfrm4_beet_exit(void) -{ - int err; - - err = xfrm_unregister_mode(&xfrm4_beet_mode, AF_INET); - BUG_ON(err); -} - -module_init(xfrm4_beet_init); -module_exit(xfrm4_beet_exit); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_BEET); diff --git a/trunk/net/ipv6/Kconfig b/trunk/net/ipv6/Kconfig index a460e8132b4d..a2d211da2aba 100644 --- a/trunk/net/ipv6/Kconfig +++ b/trunk/net/ipv6/Kconfig @@ -136,16 +136,6 @@ config INET6_XFRM_MODE_TUNNEL If unsure, say Y. -config INET6_XFRM_MODE_BEET - tristate "IPv6: IPsec BEET mode" - depends on IPV6 - default IPV6 - select XFRM - ---help--- - Support for IPsec BEET mode. - - If unsure, say Y. - config INET6_XFRM_MODE_ROUTEOPTIMIZATION tristate "IPv6: MIPv6 route optimization mode (EXPERIMENTAL)" depends on IPV6 && EXPERIMENTAL diff --git a/trunk/net/ipv6/Makefile b/trunk/net/ipv6/Makefile index 87274e47fe32..0213c6612b58 100644 --- a/trunk/net/ipv6/Makefile +++ b/trunk/net/ipv6/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o obj-$(CONFIG_INET6_XFRM_MODE_TRANSPORT) += xfrm6_mode_transport.o obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o -obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o diff --git a/trunk/net/ipv6/fib6_rules.c b/trunk/net/ipv6/fib6_rules.c index 34f5bfaddfc2..d8c1057e8b00 100644 --- a/trunk/net/ipv6/fib6_rules.c +++ b/trunk/net/ipv6/fib6_rules.c @@ -13,7 +13,6 @@ * Ville Nuorvala */ -#include #include #include diff --git a/trunk/net/ipv6/ipcomp6.c b/trunk/net/ipv6/ipcomp6.c index 71f59f18ede8..a2860e35efd7 100644 --- a/trunk/net/ipv6/ipcomp6.c +++ b/trunk/net/ipv6/ipcomp6.c @@ -199,7 +199,6 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x) { struct xfrm_state *t = NULL; - u8 mode = XFRM_MODE_TUNNEL; t = xfrm_state_alloc(); if (!t) @@ -213,9 +212,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x) memcpy(t->id.daddr.a6, x->id.daddr.a6, sizeof(struct in6_addr)); memcpy(&t->sel, &x->sel, sizeof(t->sel)); t->props.family = AF_INET6; - if (x->props.mode == XFRM_MODE_BEET) - mode = x->props.mode; - t->props.mode = mode; + t->props.mode = XFRM_MODE_TUNNEL; memcpy(t->props.saddr.a6, x->props.saddr.a6, sizeof(struct in6_addr)); if (xfrm_init_state(t)) diff --git a/trunk/net/ipv6/mip6.c b/trunk/net/ipv6/mip6.c index 99d116caecda..7ccdc8fc5a31 100644 --- a/trunk/net/ipv6/mip6.c +++ b/trunk/net/ipv6/mip6.c @@ -22,7 +22,6 @@ * Masahide NAKAMURA @USAGI */ -#include #include #include #include diff --git a/trunk/net/ipv6/udp.c b/trunk/net/ipv6/udp.c index e0c3934a7e4b..9662561701d1 100644 --- a/trunk/net/ipv6/udp.c +++ b/trunk/net/ipv6/udp.c @@ -546,7 +546,7 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct in6_addr *daddr, *final_p = NULL, final; struct ipv6_txoptions *opt = NULL; struct ip6_flowlabel *flowlabel = NULL; - struct flowi fl; + struct flowi *fl = &inet->cork.fl; struct dst_entry *dst; int addr_len = msg->msg_namelen; int ulen = len; @@ -626,19 +626,19 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, } ulen += sizeof(struct udphdr); - memset(&fl, 0, sizeof(fl)); + memset(fl, 0, sizeof(*fl)); if (sin6) { if (sin6->sin6_port == 0) return -EINVAL; - fl.fl_ip_dport = sin6->sin6_port; + fl->fl_ip_dport = sin6->sin6_port; daddr = &sin6->sin6_addr; if (np->sndflow) { - fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; - if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) { - flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); + fl->fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; + if (fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) { + flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel); if (flowlabel == NULL) return -EINVAL; daddr = &flowlabel->dst; @@ -656,32 +656,32 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, if (addr_len >= sizeof(struct sockaddr_in6) && sin6->sin6_scope_id && ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL) - fl.oif = sin6->sin6_scope_id; + fl->oif = sin6->sin6_scope_id; } else { if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; - fl.fl_ip_dport = inet->dport; + fl->fl_ip_dport = inet->dport; daddr = &np->daddr; - fl.fl6_flowlabel = np->flow_label; + fl->fl6_flowlabel = np->flow_label; connected = 1; } - if (!fl.oif) - fl.oif = sk->sk_bound_dev_if; + if (!fl->oif) + fl->oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { opt = &opt_space; memset(opt, 0, sizeof(struct ipv6_txoptions)); opt->tot_len = sizeof(*opt); - err = datagram_send_ctl(msg, &fl, opt, &hlimit, &tclass); + err = datagram_send_ctl(msg, fl, opt, &hlimit, &tclass); if (err < 0) { fl6_sock_release(flowlabel); return err; } - if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { - flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); + if ((fl->fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { + flowlabel = fl6_sock_lookup(sk, fl->fl6_flowlabel); if (flowlabel == NULL) return -EINVAL; } @@ -695,39 +695,39 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, opt = fl6_merge_options(&opt_space, flowlabel, opt); opt = ipv6_fixup_options(&opt_space, opt); - fl.proto = IPPROTO_UDP; - ipv6_addr_copy(&fl.fl6_dst, daddr); - if (ipv6_addr_any(&fl.fl6_src) && !ipv6_addr_any(&np->saddr)) - ipv6_addr_copy(&fl.fl6_src, &np->saddr); - fl.fl_ip_sport = inet->sport; + fl->proto = IPPROTO_UDP; + ipv6_addr_copy(&fl->fl6_dst, daddr); + if (ipv6_addr_any(&fl->fl6_src) && !ipv6_addr_any(&np->saddr)) + ipv6_addr_copy(&fl->fl6_src, &np->saddr); + fl->fl_ip_sport = inet->sport; /* merge ip6_build_xmit from ip6_output */ if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; - ipv6_addr_copy(&final, &fl.fl6_dst); - ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + ipv6_addr_copy(&final, &fl->fl6_dst); + ipv6_addr_copy(&fl->fl6_dst, rt0->addr); final_p = &final; connected = 0; } - if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) { - fl.oif = np->mcast_oif; + if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst)) { + fl->oif = np->mcast_oif; connected = 0; } - security_sk_classify_flow(sk, &fl); + security_sk_classify_flow(sk, fl); - err = ip6_sk_dst_lookup(sk, &dst, &fl); + err = ip6_sk_dst_lookup(sk, &dst, fl); if (err) goto out; if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); + ipv6_addr_copy(&fl->fl6_dst, final_p); - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) + if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) goto out; if (hlimit < 0) { - if (ipv6_addr_is_multicast(&fl.fl6_dst)) + if (ipv6_addr_is_multicast(&fl->fl6_dst)) hlimit = np->mcast_hops; else hlimit = np->hop_limit; @@ -763,23 +763,21 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, do_append_data: up->len += ulen; err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, - sizeof(struct udphdr), hlimit, tclass, opt, &fl, + sizeof(struct udphdr), hlimit, tclass, opt, fl, (struct rt6_info*)dst, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); if (err) udp_v6_flush_pending_frames(sk); else if (!corkreq) err = udp_v6_push_pending_frames(sk, up); - else if (unlikely(skb_queue_empty(&sk->sk_write_queue))) - up->pending = 0; if (dst) { if (connected) { ip6_dst_store(sk, dst, - ipv6_addr_equal(&fl.fl6_dst, &np->daddr) ? + ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ? &np->daddr : NULL, #ifdef CONFIG_IPV6_SUBTREES - ipv6_addr_equal(&fl.fl6_src, &np->saddr) ? + ipv6_addr_equal(&fl->fl6_src, &np->saddr) ? &np->saddr : #endif NULL); diff --git a/trunk/net/ipv6/xfrm6_mode_beet.c b/trunk/net/ipv6/xfrm6_mode_beet.c deleted file mode 100644 index edcfffa9e87b..000000000000 --- a/trunk/net/ipv6/xfrm6_mode_beet.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * xfrm6_mode_beet.c - BEET mode encapsulation for IPv6. - * - * Copyright (c) 2006 Diego Beltrami - * Miika Komu - * Herbert Xu - * Abhinav Pathak - * Jeff Ahrenholz - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Add encapsulation header. - * - * The top IP header will be constructed per draft-nikander-esp-beet-mode-06.txt. - * The following fields in it shall be filled in by x->type->output: - * payload_len - * - * On exit, skb->h will be set to the start of the encapsulation header to be - * filled in by x->type->output and skb->nh will be set to the nextheader field - * of the extension header directly preceding the encapsulation header, or in - * its absence, that of the top IP header. The value of skb->data will always - * point to the top IP header. - */ -static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb) -{ - struct ipv6hdr *iph, *top_iph; - u8 *prevhdr; - int hdr_len; - - skb_push(skb, x->props.header_len); - iph = skb->nh.ipv6h; - - hdr_len = ip6_find_1stfragopt(skb, &prevhdr); - skb->nh.raw = prevhdr - x->props.header_len; - skb->h.raw = skb->data + hdr_len; - memmove(skb->data, iph, hdr_len); - - skb->nh.raw = skb->data; - top_iph = skb->nh.ipv6h; - skb->nh.raw = &top_iph->nexthdr; - skb->h.ipv6h = top_iph + 1; - - ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); - ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); - - return 0; -} - -static int xfrm6_beet_input(struct xfrm_state *x, struct sk_buff *skb) -{ - struct ipv6hdr *ip6h; - int size = sizeof(struct ipv6hdr); - int err = -EINVAL; - - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) - goto out; - - skb_push(skb, size); - memmove(skb->data, skb->nh.raw, size); - skb->nh.raw = skb->data; - - skb->mac.raw = memmove(skb->data - skb->mac_len, - skb->mac.raw, skb->mac_len); - - ip6h = skb->nh.ipv6h; - ip6h->payload_len = htons(skb->len - size); - ipv6_addr_copy(&ip6h->daddr, (struct in6_addr *) &x->sel.daddr.a6); - ipv6_addr_copy(&ip6h->saddr, (struct in6_addr *) &x->sel.saddr.a6); - err = 0; -out: - return err; -} - -static struct xfrm_mode xfrm6_beet_mode = { - .input = xfrm6_beet_input, - .output = xfrm6_beet_output, - .owner = THIS_MODULE, - .encap = XFRM_MODE_BEET, -}; - -static int __init xfrm6_beet_init(void) -{ - return xfrm_register_mode(&xfrm6_beet_mode, AF_INET6); -} - -static void __exit xfrm6_beet_exit(void) -{ - int err; - - err = xfrm_unregister_mode(&xfrm6_beet_mode, AF_INET6); - BUG_ON(err); -} - -module_init(xfrm6_beet_init); -module_exit(xfrm6_beet_exit); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_BEET); diff --git a/trunk/net/netfilter/Kconfig b/trunk/net/netfilter/Kconfig index ce94732b8e23..0a28d2c5c44f 100644 --- a/trunk/net/netfilter/Kconfig +++ b/trunk/net/netfilter/Kconfig @@ -365,7 +365,7 @@ config NETFILTER_XT_MATCH_MULTIPORT config NETFILTER_XT_MATCH_PHYSDEV tristate '"physdev" match support' - depends on NETFILTER_XTABLES && BRIDGE && BRIDGE_NETFILTER + depends on NETFILTER_XTABLES && BRIDGE_NETFILTER help Physdev packet matching matches against the physical bridge ports the IP packet arrived on or will leave by. diff --git a/trunk/net/sched/estimator.c b/trunk/net/sched/estimator.c new file mode 100644 index 000000000000..0ebc98e9be2d --- /dev/null +++ b/trunk/net/sched/estimator.c @@ -0,0 +1,196 @@ +/* + * net/sched/estimator.c Simple rate estimator. + * + * 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. + * + * Authors: Alexey Kuznetsov, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + This code is NOT intended to be used for statistics collection, + its purpose is to provide a base for statistical multiplexing + for controlled load service. + If you need only statistics, run a user level daemon which + periodically reads byte counters. + + Unfortunately, rate estimation is not a very easy task. + F.e. I did not find a simple way to estimate the current peak rate + and even failed to formulate the problem 8)8) + + So I preferred not to built an estimator into the scheduler, + but run this task separately. + Ideally, it should be kernel thread(s), but for now it runs + from timers, which puts apparent top bounds on the number of rated + flows, has minimal overhead on small, but is enough + to handle controlled load service, sets of aggregates. + + We measure rate over A=(1<next) { + struct tc_stats *st = e->stats; + u64 nbytes; + u32 npackets; + u32 rate; + + spin_lock(e->stats_lock); + nbytes = st->bytes; + npackets = st->packets; + rate = (nbytes - e->last_bytes)<<(7 - idx); + e->last_bytes = nbytes; + e->avbps += ((long)rate - (long)e->avbps) >> e->ewma_log; + st->bps = (e->avbps+0xF)>>5; + + rate = (npackets - e->last_packets)<<(12 - idx); + e->last_packets = npackets; + e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log; + e->stats->pps = (e->avpps+0x1FF)>>10; + spin_unlock(e->stats_lock); + } + + mod_timer(&elist[idx].timer, jiffies + ((HZ<interval < -2 || parm->interval > 3) + return -EINVAL; + + est = kzalloc(sizeof(*est), GFP_KERNEL); + if (est == NULL) + return -ENOBUFS; + + est->interval = parm->interval + 2; + est->stats = stats; + est->stats_lock = stats_lock; + est->ewma_log = parm->ewma_log; + est->last_bytes = stats->bytes; + est->avbps = stats->bps<<5; + est->last_packets = stats->packets; + est->avpps = stats->pps<<10; + + est->next = elist[est->interval].list; + if (est->next == NULL) { + init_timer(&elist[est->interval].timer); + elist[est->interval].timer.data = est->interval; + elist[est->interval].timer.expires = jiffies + ((HZ<interval)/4); + elist[est->interval].timer.function = est_timer; + add_timer(&elist[est->interval].timer); + } + write_lock_bh(&est_lock); + elist[est->interval].list = est; + write_unlock_bh(&est_lock); + return 0; +} + +void qdisc_kill_estimator(struct tc_stats *stats) +{ + int idx; + struct qdisc_estimator *est, **pest; + + for (idx=0; idx <= EST_MAX_INTERVAL; idx++) { + int killed = 0; + pest = &elist[idx].list; + while ((est=*pest) != NULL) { + if (est->stats != stats) { + pest = &est->next; + continue; + } + + write_lock_bh(&est_lock); + *pest = est->next; + write_unlock_bh(&est_lock); + + kfree(est); + killed++; + } + if (killed && elist[idx].list == NULL) + del_timer(&elist[idx].timer); + } +} + +EXPORT_SYMBOL(qdisc_kill_estimator); +EXPORT_SYMBOL(qdisc_new_estimator); diff --git a/trunk/net/sched/sch_htb.c b/trunk/net/sched/sch_htb.c index bb3ddd4784b1..6c058e3660c0 100644 --- a/trunk/net/sched/sch_htb.c +++ b/trunk/net/sched/sch_htb.c @@ -391,7 +391,7 @@ static inline void htb_add_class_to_row(struct htb_sched *q, /* If this triggers, it is a bug in this code, but it need not be fatal */ static void htb_safe_rb_erase(struct rb_node *rb, struct rb_root *root) { - if (RB_EMPTY_NODE(rb)) { + if (!RB_EMPTY_NODE(rb)) { WARN_ON(1); } else { rb_erase(rb, root); diff --git a/trunk/net/sunrpc/auth_gss/svcauth_gss.c b/trunk/net/sunrpc/auth_gss/svcauth_gss.c index 447d9aef4605..638c0b576203 100644 --- a/trunk/net/sunrpc/auth_gss/svcauth_gss.c +++ b/trunk/net/sunrpc/auth_gss/svcauth_gss.c @@ -903,9 +903,9 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs struct gss_svc_data { /* decoded gss client cred: */ struct rpc_gss_wire_cred clcred; - /* save a pointer to the beginning of the encoded verifier, - * for use in encryption/checksumming in svcauth_gss_release: */ - __be32 *verf_start; + /* pointer to the beginning of the procedure-specific results, + * which may be encrypted/checksummed in svcauth_gss_release: */ + __be32 *body_start; struct rsc *rsci; }; @@ -968,7 +968,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) if (!svcdata) goto auth_err; rqstp->rq_auth_data = svcdata; - svcdata->verf_start = NULL; + svcdata->body_start = NULL; svcdata->rsci = NULL; gc = &svcdata->clcred; @@ -1097,7 +1097,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) goto complete; case RPC_GSS_PROC_DATA: *authp = rpcsec_gsserr_ctxproblem; - svcdata->verf_start = resv->iov_base + resv->iov_len; if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) goto auth_err; rqstp->rq_cred = rsci->cred; @@ -1111,6 +1110,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) gc->gc_seq, rsci->mechctx)) goto auth_err; /* placeholders for length and seq. number: */ + svcdata->body_start = resv->iov_base + resv->iov_len; svc_putnl(resv, 0); svc_putnl(resv, 0); break; @@ -1119,6 +1119,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) gc->gc_seq, rsci->mechctx)) goto auth_err; /* placeholders for length and seq. number: */ + svcdata->body_start = resv->iov_base + resv->iov_len; svc_putnl(resv, 0); svc_putnl(resv, 0); break; @@ -1146,32 +1147,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) return ret; } -u32 * -svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd) -{ - u32 *p, verf_len; - - p = gsd->verf_start; - gsd->verf_start = NULL; - - /* If the reply stat is nonzero, don't wrap: */ - if (*(p-1) != rpc_success) - return NULL; - /* Skip the verifier: */ - p += 1; - verf_len = ntohl(*p++); - p += XDR_QUADLEN(verf_len); - /* move accept_stat to right place: */ - memcpy(p, p + 2, 4); - /* Also don't wrap if the accept stat is nonzero: */ - if (*p != rpc_success) { - resbuf->head[0].iov_len -= 2 * 4; - return NULL; - } - p++; - return p; -} - static inline int svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) { @@ -1185,9 +1160,17 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) int integ_offset, integ_len; int stat = -EINVAL; - p = svcauth_gss_prepare_to_wrap(resbuf, gsd); - if (p == NULL) + p = gsd->body_start; + gsd->body_start = NULL; + /* move accept_stat to right place: */ + memcpy(p, p + 2, 4); + /* Don't wrap in failure case: */ + /* Counting on not getting here if call was not even accepted! */ + if (*p != rpc_success) { + resbuf->head[0].iov_len -= 2 * 4; goto out; + } + p++; integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; integ_len = resbuf->len - integ_offset; BUG_ON(integ_len % 4); @@ -1208,6 +1191,7 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) resbuf->tail[0].iov_base = resbuf->head[0].iov_base + resbuf->head[0].iov_len; resbuf->tail[0].iov_len = 0; + rqstp->rq_restailpage = 0; resv = &resbuf->tail[0]; } else { resv = &resbuf->tail[0]; @@ -1239,16 +1223,24 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) int offset; int pad; - p = svcauth_gss_prepare_to_wrap(resbuf, gsd); - if (p == NULL) + p = gsd->body_start; + gsd->body_start = NULL; + /* move accept_stat to right place: */ + memcpy(p, p + 2, 4); + /* Don't wrap in failure case: */ + /* Counting on not getting here if call was not even accepted! */ + if (*p != rpc_success) { + resbuf->head[0].iov_len -= 2 * 4; return 0; + } + p++; len = p++; offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; *p++ = htonl(gc->gc_seq); inpages = resbuf->pages; /* XXX: Would be better to write some xdr helper functions for * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ - if (resbuf->tail[0].iov_base) { + if (resbuf->tail[0].iov_base && rqstp->rq_restailpage == 0) { BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base + PAGE_SIZE); BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base); @@ -1266,6 +1258,7 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) resbuf->tail[0].iov_base = resbuf->head[0].iov_base + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; resbuf->tail[0].iov_len = 0; + rqstp->rq_restailpage = 0; } if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) return -ENOMEM; @@ -1289,7 +1282,7 @@ svcauth_gss_release(struct svc_rqst *rqstp) if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; /* Release can be called twice, but we only wrap once. */ - if (gsd->verf_start == NULL) + if (gsd->body_start == NULL) goto out; /* normally not set till svc_send, but we need it here: */ /* XXX: what for? Do we mess it up the moment we call svc_putu32 diff --git a/trunk/net/sunrpc/svc.c b/trunk/net/sunrpc/svc.c index c2c8bb20d07f..a99e67b164c1 100644 --- a/trunk/net/sunrpc/svc.c +++ b/trunk/net/sunrpc/svc.c @@ -417,15 +417,18 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) if (size > RPCSVC_MAXPAYLOAD) size = RPCSVC_MAXPAYLOAD; pages = 2 + (size+ PAGE_SIZE -1) / PAGE_SIZE; + rqstp->rq_argused = 0; + rqstp->rq_resused = 0; arghi = 0; BUG_ON(pages > RPCSVC_MAXPAGES); while (pages) { struct page *p = alloc_page(GFP_KERNEL); if (!p) break; - rqstp->rq_pages[arghi++] = p; + rqstp->rq_argpages[arghi++] = p; pages--; } + rqstp->rq_arghi = arghi; return ! pages; } @@ -435,10 +438,14 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size) static void svc_release_buffer(struct svc_rqst *rqstp) { - int i; - for (i=0; irq_pages); i++) - if (rqstp->rq_pages[i]) - put_page(rqstp->rq_pages[i]); + while (rqstp->rq_arghi) + put_page(rqstp->rq_argpages[--rqstp->rq_arghi]); + while (rqstp->rq_resused) { + if (rqstp->rq_respages[--rqstp->rq_resused] == NULL) + continue; + put_page(rqstp->rq_respages[rqstp->rq_resused]); + } + rqstp->rq_argused = 0; } /* @@ -644,32 +651,23 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) unsigned long flags; int i, error = 0, dummy; + progp = serv->sv_program; + + dprintk("RPC: svc_register(%s, %s, %d)\n", + progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port); + if (!port) clear_thread_flag(TIF_SIGPENDING); - for (progp = serv->sv_program; progp; progp = progp->pg_next) { - for (i = 0; i < progp->pg_nvers; i++) { - if (progp->pg_vers[i] == NULL) - continue; - - dprintk("RPC: svc_register(%s, %s, %d, %d)%s\n", - progp->pg_name, - proto == IPPROTO_UDP? "udp" : "tcp", - port, - i, - progp->pg_vers[i]->vs_hidden? - " (but not telling portmap)" : ""); - - if (progp->pg_vers[i]->vs_hidden) - continue; - - error = rpc_register(progp->pg_prog, i, proto, port, &dummy); - if (error < 0) - break; - if (port && !dummy) { - error = -EACCES; - break; - } + for (i = 0; i < progp->pg_nvers; i++) { + if (progp->pg_vers[i] == NULL) + continue; + error = rpc_register(progp->pg_prog, i, proto, port, &dummy); + if (error < 0) + break; + if (port && !dummy) { + error = -EACCES; + break; } } @@ -699,7 +697,7 @@ svc_process(struct svc_rqst *rqstp) u32 dir, prog, vers, proc; __be32 auth_stat, rpc_stat; int auth_res; - __be32 *reply_statp; + __be32 *accept_statp; rpc_stat = rpc_success; @@ -709,10 +707,10 @@ svc_process(struct svc_rqst *rqstp) /* setup response xdr_buf. * Initially it has just one page */ - rqstp->rq_resused = 1; + svc_take_page(rqstp); /* must succeed */ resv->iov_base = page_address(rqstp->rq_respages[0]); resv->iov_len = 0; - rqstp->rq_res.pages = rqstp->rq_respages + 1; + rqstp->rq_res.pages = rqstp->rq_respages+1; rqstp->rq_res.len = 0; rqstp->rq_res.page_base = 0; rqstp->rq_res.page_len = 0; @@ -740,7 +738,7 @@ svc_process(struct svc_rqst *rqstp) goto err_bad_rpc; /* Save position in case we later decide to reject: */ - reply_statp = resv->iov_base + resv->iov_len; + accept_statp = resv->iov_base + resv->iov_len; svc_putnl(resv, 0); /* ACCEPT */ @@ -888,7 +886,7 @@ svc_process(struct svc_rqst *rqstp) dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat)); serv->sv_stats->rpcbadauth++; /* Restore write pointer to location of accept status: */ - xdr_ressize_check(rqstp, reply_statp); + xdr_ressize_check(rqstp, accept_statp); svc_putnl(resv, 1); /* REJECT */ svc_putnl(resv, 1); /* AUTH_ERROR */ svc_putnl(resv, ntohl(auth_stat)); /* status */ @@ -928,18 +926,3 @@ svc_process(struct svc_rqst *rqstp) svc_putnl(resv, ntohl(rpc_stat)); goto sendit; } - -/* - * Return (transport-specific) limit on the rpc payload. - */ -u32 svc_max_payload(const struct svc_rqst *rqstp) -{ - int max = RPCSVC_MAXPAYLOAD_TCP; - - if (rqstp->rq_sock->sk_sock->type == SOCK_DGRAM) - max = RPCSVC_MAXPAYLOAD_UDP; - if (rqstp->rq_server->sv_bufsz < max) - max = rqstp->rq_server->sv_bufsz; - return max; -} -EXPORT_SYMBOL_GPL(svc_max_payload); diff --git a/trunk/net/sunrpc/svcauth_unix.c b/trunk/net/sunrpc/svcauth_unix.c index e1bd933629fe..40d41a2831d7 100644 --- a/trunk/net/sunrpc/svcauth_unix.c +++ b/trunk/net/sunrpc/svcauth_unix.c @@ -9,7 +9,6 @@ #include #include #include -#include #define RPCDBG_FACILITY RPCDBG_AUTH @@ -376,44 +375,6 @@ void svcauth_unix_purge(void) cache_purge(&ip_map_cache); } -static inline struct ip_map * -ip_map_cached_get(struct svc_rqst *rqstp) -{ - struct ip_map *ipm = rqstp->rq_sock->sk_info_authunix; - if (ipm != NULL) { - if (!cache_valid(&ipm->h)) { - /* - * The entry has been invalidated since it was - * remembered, e.g. by a second mount from the - * same IP address. - */ - rqstp->rq_sock->sk_info_authunix = NULL; - cache_put(&ipm->h, &ip_map_cache); - return NULL; - } - cache_get(&ipm->h); - } - return ipm; -} - -static inline void -ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm) -{ - struct svc_sock *svsk = rqstp->rq_sock; - - if (svsk->sk_sock->type == SOCK_STREAM && svsk->sk_info_authunix == NULL) - svsk->sk_info_authunix = ipm; /* newly cached, keep the reference */ - else - cache_put(&ipm->h, &ip_map_cache); -} - -void -svcauth_unix_info_release(void *info) -{ - struct ip_map *ipm = info; - cache_put(&ipm->h, &ip_map_cache); -} - static int svcauth_unix_set_client(struct svc_rqst *rqstp) { @@ -423,10 +384,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) if (rqstp->rq_proc == 0) return SVC_OK; - ipm = ip_map_cached_get(rqstp); - if (ipm == NULL) - ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, - rqstp->rq_addr.sin_addr); + ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class, + rqstp->rq_addr.sin_addr); if (ipm == NULL) return SVC_DENIED; @@ -441,7 +400,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp) case 0: rqstp->rq_client = &ipm->m_client->h; kref_get(&rqstp->rq_client->ref); - ip_map_cached_put(rqstp, ipm); + cache_put(&ipm->h, &ip_map_cache); break; } return SVC_OK; diff --git a/trunk/net/sunrpc/svcsock.c b/trunk/net/sunrpc/svcsock.c index b39e7e2b648f..cba85d195222 100644 --- a/trunk/net/sunrpc/svcsock.c +++ b/trunk/net/sunrpc/svcsock.c @@ -313,7 +313,7 @@ svc_sock_release(struct svc_rqst *rqstp) svc_release_skb(rqstp); - svc_free_res_pages(rqstp); + svc_free_allpages(rqstp); rqstp->rq_res.page_len = 0; rqstp->rq_res.page_base = 0; @@ -412,8 +412,7 @@ svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) /* send head */ if (slen == xdr->head[0].iov_len) flags = 0; - len = kernel_sendpage(sock, rqstp->rq_respages[0], 0, - xdr->head[0].iov_len, flags); + len = kernel_sendpage(sock, rqstp->rq_respages[0], 0, xdr->head[0].iov_len, flags); if (len != xdr->head[0].iov_len) goto out; slen -= xdr->head[0].iov_len; @@ -438,9 +437,8 @@ svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) } /* send tail */ if (xdr->tail[0].iov_len) { - result = kernel_sendpage(sock, rqstp->rq_respages[0], - ((unsigned long)xdr->tail[0].iov_base) - & (PAGE_SIZE-1), + result = kernel_sendpage(sock, rqstp->rq_respages[rqstp->rq_restailpage], + ((unsigned long)xdr->tail[0].iov_base)& (PAGE_SIZE-1), xdr->tail[0].iov_len, 0); if (result > 0) @@ -494,12 +492,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose) } spin_unlock(&serv->sv_lock); if (closesk) - /* Should unregister with portmap, but you cannot - * unregister just one protocol... - */ svc_delete_socket(closesk); - else if (toclose) - return -ENOENT; return len; } EXPORT_SYMBOL(svc_sock_names); @@ -710,11 +703,9 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) if (len <= rqstp->rq_arg.head[0].iov_len) { rqstp->rq_arg.head[0].iov_len = len; rqstp->rq_arg.page_len = 0; - rqstp->rq_respages = rqstp->rq_pages+1; } else { rqstp->rq_arg.page_len = len - rqstp->rq_arg.head[0].iov_len; - rqstp->rq_respages = rqstp->rq_pages + 1 + - (rqstp->rq_arg.page_len + PAGE_SIZE - 1)/ PAGE_SIZE; + rqstp->rq_argused += (rqstp->rq_arg.page_len + PAGE_SIZE - 1)/ PAGE_SIZE; } if (serv->sv_stats) @@ -955,7 +946,7 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) struct svc_sock *svsk = rqstp->rq_sock; struct svc_serv *serv = svsk->sk_server; int len; - struct kvec *vec; + struct kvec vec[RPCSVC_MAXPAGES]; int pnum, vlen; dprintk("svc: tcp_recv %p data %d conn %d close %d\n", @@ -1053,17 +1044,15 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp) len = svsk->sk_reclen; set_bit(SK_DATA, &svsk->sk_flags); - vec = rqstp->rq_vec; vec[0] = rqstp->rq_arg.head[0]; vlen = PAGE_SIZE; pnum = 1; while (vlen < len) { - vec[pnum].iov_base = page_address(rqstp->rq_pages[pnum]); + vec[pnum].iov_base = page_address(rqstp->rq_argpages[rqstp->rq_argused++]); vec[pnum].iov_len = PAGE_SIZE; pnum++; vlen += PAGE_SIZE; } - rqstp->rq_respages = &rqstp->rq_pages[pnum]; /* Now receive data */ len = svc_recvfrom(rqstp, vec, pnum, len); @@ -1215,7 +1204,7 @@ svc_recv(struct svc_rqst *rqstp, long timeout) struct svc_sock *svsk =NULL; struct svc_serv *serv = rqstp->rq_server; struct svc_pool *pool = rqstp->rq_pool; - int len, i; + int len; int pages; struct xdr_buf *arg; DECLARE_WAITQUEUE(wait, current); @@ -1232,22 +1221,27 @@ svc_recv(struct svc_rqst *rqstp, long timeout) "svc_recv: service %p, wait queue active!\n", rqstp); + /* Initialize the buffers */ + /* first reclaim pages that were moved to response list */ + svc_pushback_allpages(rqstp); /* now allocate needed pages. If we get a failure, sleep briefly */ pages = 2 + (serv->sv_bufsz + PAGE_SIZE -1) / PAGE_SIZE; - for (i=0; i < pages ; i++) - while (rqstp->rq_pages[i] == NULL) { - struct page *p = alloc_page(GFP_KERNEL); - if (!p) - schedule_timeout_uninterruptible(msecs_to_jiffies(500)); - rqstp->rq_pages[i] = p; + while (rqstp->rq_arghi < pages) { + struct page *p = alloc_page(GFP_KERNEL); + if (!p) { + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + continue; } + rqstp->rq_argpages[rqstp->rq_arghi++] = p; + } /* Make arg->head point to first page and arg->pages point to rest */ arg = &rqstp->rq_arg; - arg->head[0].iov_base = page_address(rqstp->rq_pages[0]); + arg->head[0].iov_base = page_address(rqstp->rq_argpages[0]); arg->head[0].iov_len = PAGE_SIZE; - arg->pages = rqstp->rq_pages + 1; + rqstp->rq_argused = 1; + arg->pages = rqstp->rq_argpages + 1; arg->page_base = 0; /* save at least one page for response */ arg->page_len = (pages-2)*PAGE_SIZE; @@ -1610,8 +1604,6 @@ svc_delete_socket(struct svc_sock *svsk) sockfd_put(svsk->sk_sock); else sock_release(svsk->sk_sock); - if (svsk->sk_info_authunix != NULL) - svcauth_unix_info_release(svsk->sk_info_authunix); kfree(svsk); } else { spin_unlock_bh(&serv->sv_lock); @@ -1707,7 +1699,6 @@ static int svc_deferred_recv(struct svc_rqst *rqstp) rqstp->rq_prot = dr->prot; rqstp->rq_addr = dr->addr; rqstp->rq_daddr = dr->daddr; - rqstp->rq_respages = rqstp->rq_pages; return dr->argslen<<2; } diff --git a/trunk/net/tipc/link.c b/trunk/net/tipc/link.c index 53bc8cb5adbc..693f02eca6d6 100644 --- a/trunk/net/tipc/link.c +++ b/trunk/net/tipc/link.c @@ -1666,9 +1666,8 @@ static void link_retransmit_failure(struct link *l_ptr, struct sk_buff *buf) char addr_string[16]; tipc_printf(TIPC_OUTPUT, "Msg seq number: %u, ", msg_seqno(msg)); - tipc_printf(TIPC_OUTPUT, "Outstanding acks: %lu\n", - (unsigned long) TIPC_SKB_CB(buf)->handle); - + tipc_printf(TIPC_OUTPUT, "Outstanding acks: %u\n", (u32)TIPC_SKB_CB(buf)->handle); + n_ptr = l_ptr->owner->next; tipc_node_lock(n_ptr); diff --git a/trunk/net/xfrm/xfrm_hash.h b/trunk/net/xfrm/xfrm_hash.h index d401dc8f05ed..6ac4e4f033ac 100644 --- a/trunk/net/xfrm/xfrm_hash.h +++ b/trunk/net/xfrm/xfrm_hash.h @@ -41,18 +41,17 @@ static inline unsigned int __xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t return (h ^ (h >> 16)) & hmask; } -static inline unsigned __xfrm_src_hash(xfrm_address_t *daddr, - xfrm_address_t *saddr, +static inline unsigned __xfrm_src_hash(xfrm_address_t *saddr, unsigned short family, unsigned int hmask) { unsigned int h = family; switch (family) { case AF_INET: - h ^= __xfrm4_daddr_saddr_hash(daddr, saddr); + h ^= __xfrm4_addr_hash(saddr); break; case AF_INET6: - h ^= __xfrm6_daddr_saddr_hash(daddr, saddr); + h ^= __xfrm6_addr_hash(saddr); break; }; return (h ^ (h >> 16)) & hmask; diff --git a/trunk/net/xfrm/xfrm_policy.c b/trunk/net/xfrm/xfrm_policy.c index 2a7861661f14..b6e2e79d7261 100644 --- a/trunk/net/xfrm/xfrm_policy.c +++ b/trunk/net/xfrm/xfrm_policy.c @@ -778,9 +778,8 @@ void xfrm_policy_flush(u8 type) for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { struct xfrm_policy *pol; struct hlist_node *entry; - int i, killed; + int i; - killed = 0; again1: hlist_for_each_entry(pol, entry, &xfrm_policy_inexact[dir], bydst) { @@ -791,7 +790,6 @@ void xfrm_policy_flush(u8 type) write_unlock_bh(&xfrm_policy_lock); xfrm_policy_kill(pol); - killed++; write_lock_bh(&xfrm_policy_lock); goto again1; @@ -809,14 +807,13 @@ void xfrm_policy_flush(u8 type) write_unlock_bh(&xfrm_policy_lock); xfrm_policy_kill(pol); - killed++; write_lock_bh(&xfrm_policy_lock); goto again2; } } - xfrm_policy_count[dir] -= killed; + xfrm_policy_count[dir] = 0; } atomic_inc(&flow_cache_genid); write_unlock_bh(&xfrm_policy_lock); diff --git a/trunk/net/xfrm/xfrm_state.c b/trunk/net/xfrm/xfrm_state.c index 39b8bf3a9ded..f927b7330f02 100644 --- a/trunk/net/xfrm/xfrm_state.c +++ b/trunk/net/xfrm/xfrm_state.c @@ -63,11 +63,10 @@ static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr, return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask); } -static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr, - xfrm_address_t *saddr, +static inline unsigned int xfrm_src_hash(xfrm_address_t *addr, unsigned short family) { - return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask); + return __xfrm_src_hash(addr, family, xfrm_state_hmask); } static inline unsigned int @@ -93,8 +92,7 @@ static void xfrm_hash_transfer(struct hlist_head *list, nhashmask); hlist_add_head(&x->bydst, ndsttable+h); - h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr, - x->props.family, + h = __xfrm_src_hash(&x->props.saddr, x->props.family, nhashmask); hlist_add_head(&x->bysrc, nsrctable+h); @@ -460,7 +458,7 @@ static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family) { - unsigned int h = xfrm_src_hash(daddr, saddr, family); + unsigned int h = xfrm_src_hash(saddr, family); struct xfrm_state *x; struct hlist_node *entry; @@ -589,7 +587,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, if (km_query(x, tmpl, pol) == 0) { x->km.state = XFRM_STATE_ACQ; hlist_add_head(&x->bydst, xfrm_state_bydst+h); - h = xfrm_src_hash(daddr, saddr, family); + h = xfrm_src_hash(saddr, family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); if (x->id.spi) { h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family); @@ -624,7 +622,7 @@ static void __xfrm_state_insert(struct xfrm_state *x) x->props.reqid, x->props.family); hlist_add_head(&x->bydst, xfrm_state_bydst+h); - h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family); + h = xfrm_src_hash(&x->props.saddr, x->props.family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); if (x->id.spi) { @@ -750,7 +748,7 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re x->timer.expires = jiffies + XFRM_ACQ_EXPIRES*HZ; add_timer(&x->timer); hlist_add_head(&x->bydst, xfrm_state_bydst+h); - h = xfrm_src_hash(daddr, saddr, family); + h = xfrm_src_hash(saddr, family); hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); wake_up(&km_waitq); } diff --git a/trunk/net/xfrm/xfrm_user.c b/trunk/net/xfrm/xfrm_user.c index d54b3a70d5df..c59a78d2923a 100644 --- a/trunk/net/xfrm/xfrm_user.c +++ b/trunk/net/xfrm/xfrm_user.c @@ -211,7 +211,6 @@ static int verify_newsa_info(struct xfrm_usersa_info *p, case XFRM_MODE_TRANSPORT: case XFRM_MODE_TUNNEL: case XFRM_MODE_ROUTEOPTIMIZATION: - case XFRM_MODE_BEET: break; default: diff --git a/trunk/scripts/Makefile.headersinst b/trunk/scripts/Makefile.headersinst index 6a026f69b563..cac8f21a3392 100644 --- a/trunk/scripts/Makefile.headersinst +++ b/trunk/scripts/Makefile.headersinst @@ -97,7 +97,7 @@ quiet_cmd_unifdef = UNIFDEF $(patsubst $(INSTALL_HDR_PATH)/%,%,$@) | $(HDRSED) > $@ || : quiet_cmd_check = CHECK $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/.check.%,$(_dst)/%,$@) - cmd_check = $(CONFIG_SHELL) $(srctree)/scripts/hdrcheck.sh \ + cmd_check = $(srctree)/scripts/hdrcheck.sh \ $(INSTALL_HDR_PATH)/include $(subst /.check.,/,$@) $@ quiet_cmd_remove = REMOVE $(_dst)/$@ diff --git a/trunk/sound/aoa/soundbus/sysfs.c b/trunk/sound/aoa/soundbus/sysfs.c index d31f8146952a..f580942b5c09 100644 --- a/trunk/sound/aoa/soundbus/sysfs.c +++ b/trunk/sound/aoa/soundbus/sysfs.c @@ -1,4 +1,3 @@ -#include #include #include /* FIX UP */ diff --git a/trunk/sound/core/memory.c b/trunk/sound/core/memory.c index fe59850be868..93537ab7c2ac 100644 --- a/trunk/sound/core/memory.c +++ b/trunk/sound/core/memory.c @@ -20,7 +20,6 @@ * */ -#include #include #include #include diff --git a/trunk/sound/oss/Makefile b/trunk/sound/oss/Makefile index 2489bd6bb085..86811792002f 100644 --- a/trunk/sound/oss/Makefile +++ b/trunk/sound/oss/Makefile @@ -15,42 +15,71 @@ obj-$(CONFIG_SOUND_HAL2) += hal2.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o +obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o obj-$(CONFIG_SOUND_MSS) += ad1848.o obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o +obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o +obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o obj-$(CONFIG_SOUND_MPU401) += mpu401.o obj-$(CONFIG_SOUND_UART6850) += uart6850.o +obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o obj-$(CONFIG_SOUND_YM3812) += opl3.o obj-$(CONFIG_SOUND_VMIDI) += v_midi.o obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o +obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o ad1848.o obj-$(CONFIG_SOUND_AD1816) += ad1816.o obj-$(CONFIG_SOUND_AD1889) += ad1889.o ac97_codec.o obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o +obj-$(CONFIG_SOUND_AWE32_SYNTH) += awe_wave.o obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o ifeq ($(CONFIG_MIDI_VIA82CXXX),y) obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o endif +obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o +ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y) + obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o +endif obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o obj-$(CONFIG_SOUND_VWSND) += vwsnd.o obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o +obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o +obj-$(CONFIG_SOUND_CMPCI) += cmpci.o +ifeq ($(CONFIG_SOUND_CMPCI_FM),y) + obj-$(CONFIG_SOUND_CMPCI) += sound.o opl3.o +endif +ifeq ($(CONFIG_SOUND_CMPCI_MIDI),y) + obj-$(CONFIG_SOUND_CMPCI) += sound.o mpu401.o +endif +obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o +obj-$(CONFIG_SOUND_AU1000) += au1000.o ac97_codec.o obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o +obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o +obj-$(CONFIG_SOUND_MAESTRO) += maestro.o +obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o +obj-$(CONFIG_SOUND_HARMONY) += harmony.o obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o +obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o obj-$(CONFIG_SOUND_BT878) += btaudio.o +obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o +obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o +obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o ac97_codec.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o ifeq ($(CONFIG_MIDI_EMU10K1),y) @@ -58,25 +87,28 @@ ifeq ($(CONFIG_MIDI_EMU10K1),y) endif obj-$(CONFIG_SOUND_EMU10K1) += emu10k1/ +obj-$(CONFIG_SOUND_CS4281) += cs4281/ obj-$(CONFIG_DMASOUND) += dmasound/ # Declare multi-part drivers. sound-objs := \ - dev_table.o soundcard.o \ - audio.o dmabuf.o \ - midi_synth.o midibuf.o \ - sequencer.o sound_timer.o sys_timer.o + dev_table.o soundcard.o sound_syms.o \ + audio.o audio_syms.o dmabuf.o \ + midi_syms.o midi_synth.o midibuf.o \ + sequencer.o sequencer_syms.o sound_timer.o sys_timer.o +gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o sb-objs := sb_card.o sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o vidc_mod-objs := vidc.o vidc_fill.o +wavefront-objs := wavfront.o wf_midi.o yss225.o hostprogs-y := bin2hex hex2hex # Files generated that shall be removed upon make clean -clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \ +clean-files := maui_boot.h msndperm.c msndinit.c pndsperm.c pndspini.c \ pss_boot.h trix_boot.h # Firmware files that need translation @@ -86,6 +118,21 @@ clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \ # will be forced to be remade. # +# Turtle Beach Maui / Tropez + +$(obj)/maui.o: $(obj)/maui_boot.h + +ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) + $(obj)/maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) $(obj)/bin2hex + $(obj)/bin2hex -i maui_os < $< > $@ +else + $(obj)/maui_boot.h: + ( \ + echo 'static unsigned char * maui_os = NULL;'; \ + echo 'static int maui_osLen = 0;'; \ + ) > $@ +endif + # Turtle Beach MultiSound ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y) diff --git a/trunk/sound/oss/ac97.c b/trunk/sound/oss/ac97.c index 72cf4ed77937..3ba6d91e891d 100644 --- a/trunk/sound/oss/ac97.c +++ b/trunk/sound/oss/ac97.c @@ -112,6 +112,25 @@ ac97_init (struct ac97_hwint *dev) return 0; } +/* Reset the mixer to the currently saved settings. */ +int +ac97_reset (struct ac97_hwint *dev) +{ + int x; + + if (dev->reset_device (dev)) + return -1; + + /* Now set the registers back to their last-written values. */ + for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { + int regnum = mixerRegs[x].ac97_regnum; + int value = dev->last_written_mixer_values [regnum / 2]; + if (value >= 0) + ac97_put_register (dev, regnum, value); + } + return 0; +} + /* Return the contents of register REG; use the cache if the value in it is valid. Returns a negative error code on failure. */ static int @@ -422,6 +441,7 @@ EXPORT_SYMBOL(ac97_init); EXPORT_SYMBOL(ac97_set_values); EXPORT_SYMBOL(ac97_put_register); EXPORT_SYMBOL(ac97_mixer_ioctl); +EXPORT_SYMBOL(ac97_reset); MODULE_LICENSE("GPL"); diff --git a/trunk/sound/oss/ac97.h b/trunk/sound/oss/ac97.h index 01837a9d7d6e..77d454ea3202 100644 --- a/trunk/sound/oss/ac97.h +++ b/trunk/sound/oss/ac97.h @@ -192,6 +192,9 @@ extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value); extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, void __user * arg); +/* Do a complete reset on the AC97 mixer, restoring all mixer registers to + the current values. Normally used after an APM resume event. */ +extern int ac97_reset (struct ac97_hwint *dev); #endif /* diff --git a/trunk/sound/oss/ac97_codec.c b/trunk/sound/oss/ac97_codec.c index 602db497929a..972327c97644 100644 --- a/trunk/sound/oss/ac97_codec.c +++ b/trunk/sound/oss/ac97_codec.c @@ -1399,6 +1399,95 @@ unsigned int ac97_set_adc_rate(struct ac97_codec *codec, unsigned int rate) EXPORT_SYMBOL(ac97_set_adc_rate); +int ac97_save_state(struct ac97_codec *codec) +{ + return 0; +} + +EXPORT_SYMBOL(ac97_save_state); + +int ac97_restore_state(struct ac97_codec *codec) +{ + int i; + unsigned int left, right, val; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!supported_mixer(codec, i)) + continue; + + val = codec->mixer_state[i]; + right = val >> 8; + left = val & 0xff; + codec->write_mixer(codec, i, left, right); + } + return 0; +} + +EXPORT_SYMBOL(ac97_restore_state); + +/** + * ac97_register_driver - register a codec helper + * @driver: Driver handler + * + * Register a handler for codecs matching the codec id. The handler + * attach function is called for all present codecs and will be + * called when new codecs are discovered. + */ + +int ac97_register_driver(struct ac97_driver *driver) +{ + struct list_head *l; + struct ac97_codec *c; + + mutex_lock(&codec_mutex); + INIT_LIST_HEAD(&driver->list); + list_add(&driver->list, &codec_drivers); + + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if(c->driver != NULL || ((c->model ^ driver->codec_id) & driver->codec_mask)) + continue; + if(driver->probe(c, driver)) + continue; + c->driver = driver; + } + mutex_unlock(&codec_mutex); + return 0; +} + +EXPORT_SYMBOL_GPL(ac97_register_driver); + +/** + * ac97_unregister_driver - unregister a codec helper + * @driver: Driver handler + * + * Unregister a handler for codecs matching the codec id. The handler + * remove function is called for all matching codecs. + */ + +void ac97_unregister_driver(struct ac97_driver *driver) +{ + struct list_head *l; + struct ac97_codec *c; + + mutex_lock(&codec_mutex); + list_del_init(&driver->list); + + list_for_each(l, &codecs) + { + c = list_entry(l, struct ac97_codec, list); + if (c->driver == driver) { + driver->remove(c, driver); + c->driver = NULL; + } + } + + mutex_unlock(&codec_mutex); +} + +EXPORT_SYMBOL_GPL(ac97_unregister_driver); + static int swap_headphone(int remove_master) { struct list_head *l; diff --git a/trunk/sound/oss/ac97_plugin_ad1980.c b/trunk/sound/oss/ac97_plugin_ad1980.c new file mode 100644 index 000000000000..0435c43d9704 --- /dev/null +++ b/trunk/sound/oss/ac97_plugin_ad1980.c @@ -0,0 +1,125 @@ +/* + ac97_plugin_ad1980.c Copyright (C) 2003 Red Hat, Inc. All rights reserved. + + The contents of this file are subject to the Open Software License version 1.1 + that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is + included herein by reference. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL") as + distributed in the kernel source COPYING file, in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the OSL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the OSL or the GPL. + + Authors: Alan Cox + + This is an example codec plugin. This one switches the connections + around to match the setups some vendors use with audio switched to + non standard front connectors not the normal rear ones + + This code primarily exists to demonstrate how to use the codec + interface + +*/ + +#include +#include +#include +#include + +/** + * ad1980_remove - codec remove callback + * @codec: The codec that is being removed + * + * This callback occurs when an AC97 codec is being removed. A + * codec remove call will not occur for a codec during that codec + * probe callback. + * + * Most drivers will need to lock their remove versus their + * use of the codec after the probe function. + */ + +static void __devexit ad1980_remove(struct ac97_codec *codec, struct ac97_driver *driver) +{ + /* Nothing to do in the simple example */ +} + + +/** + * ad1980_probe - codec found callback + * @codec: ac97 codec matching the idents + * @driver: ac97_driver it matched + * + * This entry point is called when a codec is found which matches + * the driver. At the point it is called the codec is basically + * operational, mixer operations have been initialised and can + * be overriden. Called in process context. The field driver_private + * is available for the driver to use to store stuff. + * + * The caller can claim the device by returning zero, or return + * a negative error code. + */ + +static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + u16 control; + +#define AC97_AD_MISC 0x76 + + /* Switch the inputs/outputs over (from Dell code) */ + control = codec->codec_read(codec, AC97_AD_MISC); + codec->codec_write(codec, AC97_AD_MISC, control | 0x4420); + + /* We could refuse the device since we dont need to hang around, + but we will claim it */ + return 0; +} + + +static struct ac97_driver ad1980_driver = { + .codec_id = 0x41445370, + .codec_mask = 0xFFFFFFFF, + .name = "AD1980 example", + .probe = ad1980_probe, + .remove = __devexit_p(ad1980_remove), +}; + +/** + * ad1980_exit - module exit path + * + * Our module is being unloaded. At this point unregister_driver + * will call back our remove handler for any existing codecs. You + * may not unregister_driver from interrupt context or from a + * probe/remove callback. + */ + +static void ad1980_exit(void) +{ + ac97_unregister_driver(&ad1980_driver); +} + +/** + * ad1980_init - set up ad1980 handlers + * + * After we call the register function it will call our probe + * function for each existing matching device before returning to us. + * Any devices appearing afterwards whose id's match the codec_id + * will also cause the probe function to be called. + * You may not register_driver from interrupt context or from a + * probe/remove callback. + */ + +static int ad1980_init(void) +{ + return ac97_register_driver(&ad1980_driver); +} + +module_init(ad1980_init); +module_exit(ad1980_exit); +MODULE_LICENSE("GPL"); diff --git a/trunk/sound/oss/ad1848.c b/trunk/sound/oss/ad1848.c index 257b7536fb18..f6b6b886c2ad 100644 --- a/trunk/sound/oss/ad1848.c +++ b/trunk/sound/oss/ad1848.c @@ -195,7 +195,6 @@ static void ad1848_halt(int dev); static void ad1848_halt_input(int dev); static void ad1848_halt_output(int dev); static void ad1848_trigger(int dev, int bits); -static irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy); #ifndef EXCLUDE_TIMERS static int ad1848_tmr_install(int dev); @@ -2196,7 +2195,7 @@ void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); } -static irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy) +irqreturn_t adintr(int irq, void *dev_id, struct pt_regs *dummy) { unsigned char status; ad1848_info *devc; @@ -2803,6 +2802,7 @@ EXPORT_SYMBOL(ad1848_detect); EXPORT_SYMBOL(ad1848_init); EXPORT_SYMBOL(ad1848_unload); EXPORT_SYMBOL(ad1848_control); +EXPORT_SYMBOL(adintr); EXPORT_SYMBOL(probe_ms_sound); EXPORT_SYMBOL(attach_ms_sound); EXPORT_SYMBOL(unload_ms_sound); diff --git a/trunk/sound/oss/ad1848.h b/trunk/sound/oss/ad1848.h index b95ebe28d426..d0573b023973 100644 --- a/trunk/sound/oss/ad1848.h +++ b/trunk/sound/oss/ad1848.h @@ -18,6 +18,7 @@ void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int int ad1848_detect (struct resource *ports, int *flags, int *osp); int ad1848_control(int cmd, int arg); +irqreturn_t adintr(int irq, void *dev_id, struct pt_regs * dummy); void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner); int probe_ms_sound(struct address_info *hw_config, struct resource *ports); diff --git a/trunk/sound/oss/ali5455.c b/trunk/sound/oss/ali5455.c new file mode 100644 index 000000000000..70dcd703a66f --- /dev/null +++ b/trunk/sound/oss/ali5455.c @@ -0,0 +1,3735 @@ +/* + * ALI ali5455 and friends ICH driver for Linux + * LEI HU + * + * Built from: + * drivers/sound/i810_audio + * + * The ALi 5455 is similar but not quite identical to the Intel ICH + * series of controllers. Its easier to keep the driver separated from + * the i810 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * ALi 5455 theory of operation + * + * The chipset provides three DMA channels that talk to an AC97 + * CODEC (AC97 is a digital/analog mixer standard). At its simplest + * you get 48Khz audio with basic volume and mixer controls. At the + * best you get rate adaption in the codec. We set the card up so + * that we never take completion interrupts but instead keep the card + * chasing its tail around a ring buffer. This is needed for mmap + * mode audio and happens to work rather well for non-mmap modes too. + * + * The board has one output channel for PCM audio (supported) and + * a stereo line in and mono microphone input. Again these are normally + * locked to 48Khz only. Right now recording is not finished. + * + * There is no midi support, no synth support. Use timidity. To get + * esd working you need to use esd -r 48000 as it won't probe 48KHz + * by default. mpg123 can't handle 48Khz only audio so use xmms. + * + * If you need to force a specific rate set the clocking= option + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef PCI_DEVICE_ID_ALI_5455 +#define PCI_DEVICE_ID_ALI_5455 0x5455 +#endif + +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + +static int strict_clocking = 0; +static unsigned int clocking = 0; +static unsigned int codec_pcmout_share_spdif_locked = 0; +static unsigned int codec_independent_spdif_locked = 0; +static unsigned int controller_pcmout_share_spdif_locked = 0; +static unsigned int controller_independent_spdif_locked = 0; +static unsigned int globel = 0; + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 +#define CODEC_SPDIFOUT_RUNNING 8 +#define CONTROLLER_SPDIFOUT_RUNNING 4 + +#define SPDIF_ENABLE_OUTPUT 4 /* bits 0,1 are PCM */ + +#define ALI5455_FMT_16BIT 1 +#define ALI5455_FMT_STEREO 2 +#define ALI5455_FMT_MASK 3 + +#define SPDIF_ON 0x0004 +#define SURR_ON 0x0010 +#define CENTER_LFE_ON 0x0020 +#define VOL_MUTED 0x8000 + + +#define ALI_SPDIF_OUT_CH_STATUS 0xbf +/* the 810's array of pointers to data buffers */ + +struct sg_item { +#define BUSADDR_MASK 0xFFFFFFFE + u32 busaddr; +#define CON_IOC 0x80000000 /* interrupt on completion */ +#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */ +#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ + u32 control; +}; + +/* an instance of the ali channel */ +#define SG_LEN 32 +struct ali_channel { + /* these sg guys should probably be allocated + separately as nocache. Must be 8 byte aligned */ + struct sg_item sg[SG_LEN]; /* 32*8 */ + u32 offset; /* 4 */ + u32 port; /* 4 */ + u32 used; + u32 num; +}; + +/* + * we have 3 separate dma engines. pcm in, pcm out, and mic. + * each dma engine has controlling registers. These goofy + * names are from the datasheet, but make it easy to write + * code while leafing through it. + */ + +#define ENUM_ENGINE(PRE,DIG) \ +enum { \ + PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ + PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ + PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ + PRE##_SR = 0x##DIG##6, /* Status Register */ \ + PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ + PRE##_CR = 0x##DIG##b /* Control Register */ \ +} + +ENUM_ENGINE(OFF, 0); /* Offsets */ +ENUM_ENGINE(PI, 4); /* PCM In */ +ENUM_ENGINE(PO, 5); /* PCM Out */ +ENUM_ENGINE(MC, 6); /* Mic In */ +ENUM_ENGINE(CODECSPDIFOUT, 7); /* CODEC SPDIF OUT */ +ENUM_ENGINE(CONTROLLERSPDIFIN, A); /* CONTROLLER SPDIF In */ +ENUM_ENGINE(CONTROLLERSPDIFOUT, B); /* CONTROLLER SPDIF OUT */ + + +enum { + ALI_SCR = 0x00, /* System Control Register */ + ALI_SSR = 0x04, /* System Status Register */ + ALI_DMACR = 0x08, /* DMA Control Register */ + ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ + ALI_INTERFACECR = 0x10, /* Interface Control Register */ + ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ + ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ + ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ + ALI_CPR = 0x20, /* Command Port Register */ + ALI_SPR = 0x24, /* Status Port Register */ + ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ + ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ + ALI_RTSR = 0x34, /* Receive Tag Slot Register */ + ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ + ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ + ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ + ALI_SPDIFICS = 0xfc /* spdif interface control/status */ +}; + +// x-status register(x:pcm in ,pcm out, mic in,) +/* interrupts for a dma engine */ +#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ +#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ +#define DMA_INT_LVI (1<<2) /* last valid done */ +#define DMA_INT_CELV (1<<1) /* last valid is current */ +#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ //not eqult intel +#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) + +/* interrupts for the whole chip */// by interrupt status register finish + +#define INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */ +#define INT_SPDIFIN (1<<22) +#define INT_CODECSPDIFOUT (1<<19) +#define INT_MICIN (1<<18) +#define INT_PCMOUT (1<<17) +#define INT_PCMIN (1<<16) +#define INT_CPRAIS (1<<7) +#define INT_SPRAIS (1<<5) +#define INT_GPIO (1<<1) +#define INT_MASK (INT_SPDIFOUT|INT_CODECSPDIFOUT|INT_MICIN|INT_PCMOUT|INT_PCMIN) + +#define DRIVER_VERSION "0.02ac" + +/* magic numbers to protect our data structures */ +#define ALI5455_CARD_MAGIC 0x5072696E /* "Prin" */ +#define ALI5455_STATE_MAGIC 0x63657373 /* "cess" */ +#define ALI5455_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ +#define NR_HW_CH 5 //I think 5 channel + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit */ +/* stream at a minimum for this card to be happy */ +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift */ +/* values are one less than might be expected */ +static const unsigned sample_shift[] = { -1, 0, 0, 1 }; + +#define ALI5455 +static char *card_names[] = { + "ALI 5455" +}; + +static struct pci_device_id ali_pci_tbl[] = { + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5455, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI5455}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, ali_pci_tbl); + +#ifdef CONFIG_PM +#define PM_SUSPENDED(card) (card->pm_suspended) +#else +#define PM_SUSPENDED(card) (0) +#endif + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct ali_state { + unsigned int magic; + struct ali_card *card; /* Card info */ + + /* single open lock mechanism, only used for recording */ + struct mutex open_mutex; + wait_queue_head_t open_wait; + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + +#ifdef CONFIG_PM + unsigned int pm_saved_dac_rate, pm_saved_adc_rate; +#endif + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable, trigger; + + /* hardware channel */ + struct ali_channel *read_channel; + struct ali_channel *write_channel; + struct ali_channel *codec_spdifout_channel; + struct ali_channel *controller_spdifout_channel; + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be consumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + /* what the hardware uses */ + unsigned dmasize; + unsigned fragsize; + unsigned fragsamples; + + /* what we tell the user to expect */ + unsigned userfrags; + unsigned userfragsize; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned update_flag; + unsigned ossfragsize; + unsigned ossmaxfrags; + unsigned subdivision; + } dmabuf; +}; + + +struct ali_card { + struct ali_channel channel[5]; + unsigned int magic; + + /* We keep ali5455 cards in a linked list */ + struct ali_card *next; + + /* The ali has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + spinlock_t ac97_lock; + + /* PCI device stuff */ + struct pci_dev *pci_dev; + u16 pci_id; +#ifdef CONFIG_PM + u16 pm_suspended; + int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97]; +#endif + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels */ + struct ac97_codec *ac97_codec[NR_AC97]; + struct ali_state *states[NR_HW_CH]; + + u16 ac97_features; + u16 ac97_status; + u16 channels; + + /* hardware resources */ + unsigned long iobase; + + u32 irq; + + /* Function support */ + struct ali_channel *(*alloc_pcm_channel) (struct ali_card *); + struct ali_channel *(*alloc_rec_pcm_channel) (struct ali_card *); + struct ali_channel *(*alloc_rec_mic_channel) (struct ali_card *); + struct ali_channel *(*alloc_codec_spdifout_channel) (struct ali_card *); + struct ali_channel *(*alloc_controller_spdifout_channel) (struct ali_card *); + void (*free_pcm_channel) (struct ali_card *, int chan); + + /* We have a *very* long init time possibly, so use this to block */ + /* attempts to open our devices before we are ready (stops oops'es) */ + int initializing; +}; + + +static struct ali_card *devs = NULL; + +static int ali_open_mixdev(struct inode *inode, struct file *file); +static int ali_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg); +static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct ali_channel *ali_alloc_pcm_channel(struct ali_card *card) +{ + if (card->channel[1].used == 1) + return NULL; + card->channel[1].used = 1; + return &card->channel[1]; +} + +static struct ali_channel *ali_alloc_rec_pcm_channel(struct ali_card *card) +{ + if (card->channel[0].used == 1) + return NULL; + card->channel[0].used = 1; + return &card->channel[0]; +} + +static struct ali_channel *ali_alloc_rec_mic_channel(struct ali_card *card) +{ + if (card->channel[2].used == 1) + return NULL; + card->channel[2].used = 1; + return &card->channel[2]; +} + +static struct ali_channel *ali_alloc_codec_spdifout_channel(struct ali_card *card) +{ + if (card->channel[3].used == 1) + return NULL; + card->channel[3].used = 1; + return &card->channel[3]; +} + +static struct ali_channel *ali_alloc_controller_spdifout_channel(struct ali_card *card) +{ + if (card->channel[4].used == 1) + return NULL; + card->channel[4].used = 1; + return &card->channel[4]; +} +static void ali_free_pcm_channel(struct ali_card *card, int channel) +{ + card->channel[channel].used = 0; +} + + +//add support codec spdif out +static int ali_valid_spdif_rate(struct ac97_codec *codec, int rate) +{ + unsigned long id = 0L; + + id = (ali_ac97_get(codec, AC97_VENDOR_ID1) << 16); + id |= ali_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; + switch (id) { + case 0x41445361: /* AD1886 */ + if (rate == 48000) { + return 1; + } + break; + case 0x414c4720: /* ALC650 */ + if (rate == 48000) { + return 1; + } + break; + default: /* all other codecs, until we know otherwiae */ + if (rate == 48000 || rate == 44100 || rate == 32000) { + return 1; + } + break; + } + return (0); +} + +/* ali_set_spdif_output + * + * Configure the S/PDIF output transmitter. When we turn on + * S/PDIF, we turn off the analog output. This may not be + * the right thing to do. + * + * Assumptions: + * The DSP sample rate must already be set to a supported + * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. + */ +static void ali_set_spdif_output(struct ali_state *state, int slots, + int rate) +{ + int vol; + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 4)) { + state->card->ac97_status &= ~SPDIF_ON; + } else { + if (slots == -1) { /* Turn off S/PDIF */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + + /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ + if (!(state->card->ac97_status & VOL_MUTED)) { + aud_reg = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); + ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, + (aud_reg & ~VOL_MUTED)); + } + state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); + return; + } + + vol = ali_ac97_get(codec, AC97_MASTER_VOL_STEREO); + state->card->ac97_status = vol & VOL_MUTED; + + /* Set S/PDIF transmitter sample rate */ + aud_reg = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + switch (rate) { + case 32000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; + break; + case 44100: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; + break; + case 48000: + aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; + break; + default: + /* turn off S/PDIF */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + + ali_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); + + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_SPDIF; + ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + + aud_reg = ali_ac97_get(codec, AC97_POWER_CONTROL); + aud_reg |= 0x0002; + ali_ac97_set(codec, AC97_POWER_CONTROL, aud_reg); + udelay(1); + + state->card->ac97_status |= SPDIF_ON; + + /* Check to make sure the configuration is valid */ + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + if (!(aud_reg & 0x0400)) { + /* turn off S/PDIF */ + ali_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); + state->card->ac97_status &= ~SPDIF_ON; + return; + } + if (codec_independent_spdif_locked > 0) { + aud_reg = ali_ac97_get(codec, 0x6a); + ali_ac97_set(codec, 0x6a, (aud_reg & 0xefff)); + } + /* Mute the analog output */ + /* Should this only mute the PCM volume??? */ + } +} + +/* ali_set_dac_channels + * + * Configure the codec's multi-channel DACs + * + * The logic is backwards. Setting the bit to 1 turns off the DAC. + * + * What about the ICH? We currently configure it using the + * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, + * does that imply that we want the ICH set to support + * these channels? + * + * TODO: + * vailidate that the codec really supports these DACs + * before turning them on. + */ +static void ali_set_dac_channels(struct ali_state *state, int channel) +{ + int aud_reg; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + aud_reg = ali_ac97_get(codec, AC97_EXTENDED_STATUS); + aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; + state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); + + switch (channel) { + case 2: /* always enabled */ + break; + case 4: + aud_reg &= ~AC97_EA_PRJ; + state->card->ac97_status |= SURR_ON; + break; + case 6: + aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); + state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; + break; + default: + break; + } + ali_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); + +} + +/* set playback sample rate */ +static unsigned int ali_set_dac_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = (rate * clocking) / 48000; + + if (strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000) / clocking; + } + + new_rate = ac97_set_dac_rate(codec, rate); + if (new_rate != rate) { + dmabuf->rate = (new_rate * 48000) / clocking; + } + rate = new_rate; + return dmabuf->rate; +} + +/* set recording sample rate */ +static unsigned int ali_set_adc_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 new_rate; + struct ac97_codec *codec = state->card->ac97_codec[0]; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + /* + * Adjust for misclocked crap + */ + + rate = (rate * clocking) / 48000; + if (strict_clocking && rate < 8000) { + rate = 8000; + dmabuf->rate = (rate * 48000) / clocking; + } + + new_rate = ac97_set_adc_rate(codec, rate); + + if (new_rate != rate) { + dmabuf->rate = (new_rate * 48000) / clocking; + rate = new_rate; + } + return dmabuf->rate; +} + +/* set codec independent spdifout sample rate */ +static unsigned int ali_set_codecspdifout_rate(struct ali_state *state, + unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (!(state->card->ac97_features & 0x0001)) { + dmabuf->rate = clocking; + return clocking; + } + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + dmabuf->rate = rate; + + return dmabuf->rate; +} + +/* set controller independent spdif out function sample rate */ +static void ali_set_spdifout_rate(struct ali_state *state, + unsigned int rate) +{ + unsigned char ch_st_sel; + unsigned short status_rate; + + switch (rate) { + case 44100: + status_rate = 0; + break; + case 32000: + status_rate = 0x300; + break; + case 48000: + default: + status_rate = 0x200; + break; + } + + ch_st_sel = inb(state->card->iobase + ALI_SPDIFICS) & ALI_SPDIF_OUT_CH_STATUS; //select spdif_out + + ch_st_sel |= 0x80; //select right + outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); + outb(status_rate | 0x20, (state->card->iobase + ALI_SPDIFCSR + 2)); + + ch_st_sel &= (~0x80); //select left + outb(ch_st_sel, (state->card->iobase + ALI_SPDIFICS)); + outw(status_rate | 0x10, (state->card->iobase + ALI_SPDIFCSR + 2)); +} + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +static inline unsigned ali_get_dma_addr(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned int civ, offset, port, port_picb; + unsigned int data; + + if (!dmabuf->enable) + return 0; + + if (rec == 1) + port = state->card->iobase + dmabuf->read_channel->port; + else if (rec == 2) + port = state->card->iobase + dmabuf->codec_spdifout_channel->port; + else if (rec == 3) + port = state->card->iobase + dmabuf->controller_spdifout_channel->port; + else + port = state->card->iobase + dmabuf->write_channel->port; + + port_picb = port + OFF_PICB; + + do { + civ = inb(port + OFF_CIV) & 31; + offset = inw(port_picb); + /* Must have a delay here! */ + if (offset == 0) + udelay(1); + + /* Reread both registers and make sure that that total + * offset from the first reading to the second is 0. + * There is an issue with SiS hardware where it will count + * picb down to 0, then update civ to the next value, + * then set the new picb to fragsize bytes. We can catch + * it between the civ update and the picb update, making + * it look as though we are 1 fragsize ahead of where we + * are. The next to we get the address though, it will + * be back in thdelay is more than long enough + * that we won't have to worry about the chip still being + * out of sync with reality ;-) + */ + } while (civ != (inb(port + OFF_CIV) & 31) || offset != inw(port_picb)); + + data = ((civ + 1) * dmabuf->fragsize - (2 * offset)) % dmabuf->dmasize; + if (inw(port_picb) == 0) + data -= 2048; + + return data; +} + +/* Stop recording (lock held) */ +static inline void __stop_adc(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + + outl((1 << 18) | (1 << 16), card->iobase + ALI_DMACR); + udelay(1); + + outb(0, card->iobase + PI_CR); + while (inb(card->iobase + PI_CR) != 0); + + // now clear any latent interrupt bits (like the halt bit) + outb(inb(card->iobase + PI_SR) | 0x001e, card->iobase + PI_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMIN, card->iobase + ALI_INTERRUPTSR); +} + +static void stop_adc(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_adc(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (dmabuf->count < dmabuf->dmasize && dmabuf->ready + && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->enable |= ADC_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + PI_CR); + if (state->card->channel[0].used == 1) + outl(1, state->card->iobase + ALI_DMACR); // DMA CONTROL REGISTRER + udelay(100); + if (state->card->channel[2].used == 1) + outl((1 << 2), state->card->iobase + ALI_DMACR); //DMA CONTROL REGISTER + udelay(100); + } +} + +static void start_adc(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __start_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +static inline void __stop_dac(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + outl(0x00020000, card->iobase + 0x08); + outb(0, card->iobase + PO_CR); + while (inb(card->iobase + PO_CR) != 0) + cpu_relax(); + + outb(inb(card->iobase + PO_SR) | 0x001e, card->iobase + PO_SR); + + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_PCMOUT, card->iobase + ALI_INTERRUPTSR); +} + +static void stop_dac(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_dac(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->enable |= DAC_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + PO_CR); + outl((1 << 1), state->card->iobase + 0x08); //dma control register + } +} + +static void start_dac(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __start_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop codec and controller spdif out (lock held) */ +static inline void __stop_spdifout(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_card *card = state->card; + + if (codec_independent_spdif_locked > 0) { + dmabuf->enable &= ~CODEC_SPDIFOUT_RUNNING; + outl((1 << 19), card->iobase + 0x08); + outb(0, card->iobase + CODECSPDIFOUT_CR); + + while (inb(card->iobase + CODECSPDIFOUT_CR) != 0) + cpu_relax(); + + outb(inb(card->iobase + CODECSPDIFOUT_SR) | 0x001e, card->iobase + CODECSPDIFOUT_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_CODECSPDIFOUT, card->iobase + ALI_INTERRUPTSR); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->enable &= ~CONTROLLER_SPDIFOUT_RUNNING; + outl((1 << 23), card->iobase + 0x08); + outb(0, card->iobase + CONTROLLERSPDIFOUT_CR); + while (inb(card->iobase + CONTROLLERSPDIFOUT_CR) != 0) + cpu_relax(); + outb(inb(card->iobase + CONTROLLERSPDIFOUT_SR) | 0x001e, card->iobase + CONTROLLERSPDIFOUT_SR); + outl(inl(card->iobase + ALI_INTERRUPTSR) & INT_SPDIFOUT, card->iobase + ALI_INTERRUPTSR); + } + } +} + +static void stop_spdifout(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __stop_spdifout(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static inline void __start_spdifout(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && + (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + if (codec_independent_spdif_locked > 0) { + dmabuf->enable |= CODEC_SPDIFOUT_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + CODECSPDIFOUT_CR); + outl((1 << 3), state->card->iobase + 0x08); //dma control register + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->enable |= CONTROLLER_SPDIFOUT_RUNNING; + outb((1 << 4) | (1 << 2), state->card->iobase + CONTROLLERSPDIFOUT_CR); + outl((1 << 7), state->card->iobase + 0x08); //dma control register + } + } + } +} + +static void start_spdifout(struct ali_state *state) +{ + struct ali_card *card = state->card; + unsigned long flags; + spin_lock_irqsave(&card->lock, flags); + __start_spdifout(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback , recording,spdif out buffer should be allocated separately */ +static int alloc_dmabuf(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf = NULL; + int order, size; + struct page *page, *pend; + + /* If we don't have any oss frag params, then use our default ones */ + if (dmabuf->ossmaxfrags == 0) + dmabuf->ossmaxfrags = 4; + if (dmabuf->ossfragsize == 0) + dmabuf->ossfragsize = (PAGE_SIZE << DMABUF_DEFAULTORDER) / dmabuf->ossmaxfrags; + size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + + if (dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) + return 0; + /* alloc enough to satisfy the oss params */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + if ((PAGE_SIZE << order) > size) + continue; + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + } + if (!rawbuf) + return -ENOMEM; + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + SetPageReserved(page); + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct page *page, *pend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) + ClearPageReserved(page); + pci_free_consistent(state->card->pci_dev, + PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_handle); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ali_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct ali_channel *c = NULL; + struct sg_item *sg; + unsigned long flags; + int ret; + unsigned fragint; + int i; + + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + + dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer, let alloc_dmabuf determine if we are already + * allocated well enough or if we should replace the current buffer + * (assuming one is already allocated, if it isn't, then allocate it). + */ + if ((ret = alloc_dmabuf(state))) + return ret; + + /* FIXME: figure out all this OSS fragment stuff */ + /* I did, it now does what it should according to the OSS API. DL */ + /* We may not have realloced our dmabuf, but the fragment size to + * fragment number ratio may have changed, so go ahead and reprogram + * things + */ + + dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; + dmabuf->numfrag = SG_LEN; + dmabuf->fragsize = dmabuf->dmasize / dmabuf->numfrag; + dmabuf->fragsamples = dmabuf->fragsize >> 1; + dmabuf->userfragsize = dmabuf->ossfragsize; + dmabuf->userfrags = dmabuf->dmasize / dmabuf->ossfragsize; + + memset(dmabuf->rawbuf, 0, dmabuf->dmasize); + + if (dmabuf->ossmaxfrags == 4) { + fragint = 8; + dmabuf->fragshift = 2; + } else if (dmabuf->ossmaxfrags == 8) { + fragint = 4; + dmabuf->fragshift = 3; + } else if (dmabuf->ossmaxfrags == 16) { + fragint = 2; + dmabuf->fragshift = 4; + } else { + fragint = 1; + dmabuf->fragshift = 5; + } + /* + * Now set up the ring + */ + + if (rec == 1) + c = dmabuf->read_channel; + else if (rec == 2) + c = dmabuf->codec_spdifout_channel; + else if (rec == 3) + c = dmabuf->controller_spdifout_channel; + else if (rec == 0) + c = dmabuf->write_channel; + if (c != NULL) { + sg = &c->sg[0]; + /* + * Load up 32 sg entries and take an interrupt at half + * way (we might want more interrupts later..) + */ + for (i = 0; i < dmabuf->numfrag; i++) { + sg->busaddr = + virt_to_bus(dmabuf->rawbuf + + dmabuf->fragsize * i); + // the card will always be doing 16bit stereo + sg->control = dmabuf->fragsamples; + sg->control |= CON_BUFPAD; //I modify + // set us up to get IOC interrupts as often as needed to + // satisfy numfrag requirements, no more + if (((i + 1) % fragint) == 0) { + sg->control |= CON_IOC; + } + sg++; + } + spin_lock_irqsave(&state->card->lock, flags); + outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ + outl(virt_to_bus(&c->sg[0]), state->card->iobase + c->port + OFF_BDBAR); + outb(0, state->card->iobase + c->port + OFF_CIV); + outb(0, state->card->iobase + c->port + OFF_LVI); + spin_unlock_irqrestore(&state->card->lock, flags); + } + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + return 0; +} + +static void __ali_update_lvi(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int x, port; + port = state->card->iobase; + if (rec == 1) + port += dmabuf->read_channel->port; + else if (rec == 2) + port += dmabuf->codec_spdifout_channel->port; + else if (rec == 3) + port += dmabuf->controller_spdifout_channel->port; + else if (rec == 0) + port += dmabuf->write_channel->port; + /* if we are currently stopped, then our CIV is actually set to our + * *last* sg segment and we are ready to wrap to the next. However, + * if we set our LVI to the last sg segment, then it won't wrap to + * the next sg segment, it won't even get a start. So, instead, when + * we are stopped, we set both the LVI value and also we increment + * the CIV value to the next sg segment to be played so that when + * we call start_{dac,adc}, things will operate properly + */ + if (!dmabuf->enable && dmabuf->ready) { + if (rec && dmabuf->count < dmabuf->dmasize && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_adc(state); + while (! (inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else if (!rec && dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_dac(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else if (rec && dmabuf->count && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + if (codec_independent_spdif_locked > 0) { + // outb((inb(port+OFF_CIV))&31, port+OFF_LVI); + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_spdifout(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } else { + if (controller_independent_spdif_locked > 0) { + outb((inb(port + OFF_CIV) + 1) & 31, port + OFF_LVI); + __start_spdifout(state); + while (!(inb(port + OFF_CR) & ((1 << 4) | (1 << 2)))) + cpu_relax(); + } + } + } + } + + /* swptr - 1 is the tail of our transfer */ + x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; + x /= dmabuf->fragsize; + outb(x, port + OFF_LVI); +} + +static void ali_update_lvi(struct ali_state *state, int rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + if (!dmabuf->ready) + return; + spin_lock_irqsave(&state->card->lock, flags); + __ali_update_lvi(state, rec); + spin_unlock_irqrestore(&state->card->lock, flags); +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void ali_update_ptr(struct ali_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr; + int diff; + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == ADC_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + if (dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a read */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + PI_CIV) & 31) != (inb(state->card->iobase + PI_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on read\n"); + dmabuf->error++; + } + } + if (dmabuf->count > dmabuf->userfragsize) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 0); + diff = + (dmabuf->dmasize + hwptr - + dmabuf->hwptr) % dmabuf->dmasize; +#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) + printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); +#endif + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + PO_CIV) & 31) != (inb(state->card->iobase + PO_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); + printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", + inb(state->card->iobase + PO_CIV) & 31, + inb(state->card->iobase + PO_LVI) & 31, + dmabuf->hwptr, + dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } + + /* error handling and process wake up for CODEC SPDIF OUT */ + if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 2); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31)) { + printk(KERN_WARNING "ali_audio: DMA overrun on write\n"); + printk(KERN_DEBUG "ali_audio: CIV %d, LVI %d, hwptr %x, count %d\n", + inb(state->card->iobase + CODECSPDIFOUT_CIV) & 31, + inb(state->card->iobase + CODECSPDIFOUT_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } + /* error handling and process wake up for CONTROLLER SPDIF OUT */ + if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + /* update hardware pointer */ + hwptr = ali_get_dma_addr(state, 3); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count -= diff; + if (dmabuf->count < 0) { + /* buffer underrun or buffer overrun */ + /* this is normal for the end of a write */ + /* only give an error if we went past the */ + /* last valid sg entry */ + if ((inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31) != (inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31)) { + printk(KERN_WARNING + "ali_audio: DMA overrun on write\n"); + printk("ali_audio: CIV %d, LVI %d, hwptr %x, " + "count %d\n", + inb(state->card->iobase + CONTROLLERSPDIFOUT_CIV) & 31, + inb(state->card->iobase + CONTROLLERSPDIFOUT_LVI) & 31, + dmabuf->hwptr, dmabuf->count); + dmabuf->error++; + } + } + if (dmabuf->count < (dmabuf->dmasize - dmabuf->userfragsize)) + wake_up(&dmabuf->wait); + } +} + +static inline int ali_get_free_write_space(struct + ali_state + *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int free; + + if (dmabuf->count < 0) { + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + free = dmabuf->dmasize - dmabuf->swptr; + if ((dmabuf->count + free) > dmabuf->dmasize){ + free = dmabuf->dmasize - dmabuf->count; + } + return free; +} + +static inline int ali_get_available_read_data(struct + ali_state + *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + int avail; + ali_update_ptr(state); + // catch overruns during record + if (dmabuf->count > dmabuf->dmasize) { + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + avail = dmabuf->count; + avail -= (dmabuf->hwptr % dmabuf->fragsize); + if (avail < 0) + return (0); + return (avail); +} + +static int drain_dac(struct ali_state *state, int signals_allowed) +{ + + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + if (!dmabuf->ready) + return 0; + if (dmabuf->mapped) { + stop_dac(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + if (count <= 0) + break; + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if (!dmabuf->enable) { + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_update_lvi(state, 0); + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR "ali_audio: drain_dac, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if (count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_dac(state); + return 0; +} + + +static int drain_spdifout(struct ali_state *state, int signals_allowed) +{ + + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + if (!dmabuf->ready) + return 0; + if (dmabuf->mapped) { + stop_spdifout(state); + return 0; + } + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + if (count <= 0) + break; + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + if (!dmabuf->enable) { + if (codec_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 3); + } + } + } + if (signal_pending(current) && signals_allowed) { + break; + } + + /* It seems that we have to set the current state to + * TASK_INTERRUPTIBLE every time to make the process + * really go to sleep. This also has to be *after* the + * update_ptr() call because update_ptr is likely to + * do a wake_up() which will unset this before we ever + * try to sleep, resuling in a tight loop in this code + * instead of actually sleeping and waiting for an + * interrupt to wake us up! + */ + set_current_state(TASK_INTERRUPTIBLE); + /* + * set the timeout to significantly longer than it *should* + * take for the DAC to drain the DMA buffer + */ + tmo = (count * HZ) / (dmabuf->rate); + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR "ali_audio: drain_spdifout, dma timeout?\n"); + count = 0; + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &wait); + if (count > 0 && signal_pending(current) && signals_allowed) + return -ERESTARTSYS; + stop_spdifout(state); + return 0; +} + +static void ali_channel_interrupt(struct ali_card *card) +{ + int i, count; + + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + struct ali_channel *c = NULL; + struct dmabuf *dmabuf; + unsigned long port = card->iobase; + u16 status; + if (!state) + continue; + if (!state->dmabuf.ready) + continue; + dmabuf = &state->dmabuf; + if (codec_independent_spdif_locked > 0) { + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + c = dmabuf->codec_spdifout_channel; + } + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + c = dmabuf->controller_spdifout_channel; + } else { + if (dmabuf->enable & DAC_RUNNING) { + c = dmabuf->write_channel; + } else if (dmabuf->enable & ADC_RUNNING) { + c = dmabuf->read_channel; + } else + continue; + } + } + port += c->port; + + status = inw(port + OFF_SR); + + if (status & DMA_INT_COMPLETE) { + /* only wake_up() waiters if this interrupt signals + * us being beyond a userfragsize of data open or + * available, and ali_update_ptr() does that for + * us + */ + ali_update_ptr(state); + } + + if (status & DMA_INT_LVI) { + ali_update_ptr(state); + wake_up(&dmabuf->wait); + + if (dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - dmabuf->count; + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + count = dmabuf->count; + else count = 0; + + if (count > 0) { + if (dmabuf->enable & DAC_RUNNING) + outl((1 << 1), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + outl((1 << 3), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + outl((1 << 7), state->card->iobase + ALI_DMACR); + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); + } + + } + if (!(status & DMA_INT_DCH)) { + ali_update_ptr(state); + wake_up(&dmabuf->wait); + if (dmabuf->enable & DAC_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - dmabuf->count; + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + count = dmabuf->count; + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + count = dmabuf->count; + else + count = 0; + + if (count > 0) { + if (dmabuf->enable & DAC_RUNNING) + outl((1 << 1), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + outl((1 << 3), state->card->iobase + ALI_DMACR); + else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + outl((1 << 7), state->card->iobase + ALI_DMACR); + } else { + if (dmabuf->enable & DAC_RUNNING) + __stop_dac(state); + if (dmabuf->enable & ADC_RUNNING) + __stop_adc(state); + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) + __stop_spdifout(state); + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) + __stop_spdifout(state); + dmabuf->enable = 0; + wake_up(&dmabuf->wait); + } + } + outw(status & DMA_INT_MASK, port + OFF_SR); + } +} + +static irqreturn_t ali_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ali_card *card = (struct ali_card *) dev_id; + u32 status; + u16 status2; + + spin_lock(&card->lock); + status = inl(card->iobase + ALI_INTERRUPTSR); + if (!(status & INT_MASK)) { + spin_unlock(&card->lock); + return IRQ_NONE; /* not for us */ + } + + if (codec_independent_spdif_locked > 0) { + if (globel == 0) { + globel += 1; + status2 = inw(card->iobase + 0x76); + outw(status2 | 0x000c, card->iobase + 0x76); + } else { + if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) + ali_channel_interrupt(card); + } + } else { + if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT)) + ali_channel_interrupt(card); + } + + /* clear 'em */ + outl(status & INT_MASK, card->iobase + ALI_INTERRUPTSR); + spin_unlock(&card->lock); + return IRQ_HANDLED; +} + +/* in this loop, dmabuf.count signifies the amount of data that is + waiting to be copied to the user's buffer. It is filled by the dma + machine and drained by this loop. */ + +static ssize_t ali_read(struct file *file, char __user *buffer, + size_t count, loff_t * ppos) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state ? state->card : NULL; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; + DECLARE_WAITQUEUE(waita, current); +#ifdef DEBUG2 + printk("ali_audio: ali_read called, count = %d\n", count); +#endif + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & DAC_RUNNING) + return -ENODEV; + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = card->alloc_rec_pcm_channel(card); + if (!dmabuf->read_channel) { + return -EBUSY; + } + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = ali_get_available_read_data(state); + // this is to make the copy_to_user simpler below + if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&card->lock, flags); + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + /* + * Don't let us deadlock. The ADC won't start if + * dmabuf->trigger isn't set. A call to SETTRIGGER + * could have turned it off after we set it to on + * previously. + */ + dmabuf->trigger = PCM_ENABLE_INPUT; + /* + * This does three things. Updates LVI to be correct, + * makes sure the ADC is running, and updates the + * hwptr. + */ + ali_update_lvi(state, 1); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto done; + } + /* Set the timeout to how long it would take to fill + * two of our buffers. If we haven't been woke up + * by then, then we know something is wrong. + */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!schedule_timeout(tmo >= 2 ? tmo : 2)) { + printk(KERN_ERR + "ali_audio: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, + dmabuf->count, dmabuf->hwptr, + dmabuf->swptr); + /* a buffer overrun, we delay the recovery until next time the + while loop begin and we REALLY have space to record */ + } + if (signal_pending(current)) { + ret = ret ? ret : -ERESTARTSYS; + goto done; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto done; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&card->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } +done: + ali_update_lvi(state, 1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ +static ssize_t ali_write(struct file *file, + const char __user *buffer, size_t count, loff_t * ppos) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state ? state->card : NULL; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned int swptr = 0; + int cnt, x; + DECLARE_WAITQUEUE(waita, current); +#ifdef DEBUG2 + printk("ali_audio: ali_write called, count = %d\n", count); +#endif + if (dmabuf->mapped) + return -ENXIO; + if (dmabuf->enable & ADC_RUNNING) + return -ENODEV; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->codec_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card); + if (!dmabuf->codec_spdifout_channel) + return -EBUSY; + } + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->controller_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card); + if (!dmabuf->controller_spdifout_channel) + return -EBUSY; + } + } else { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = + card->alloc_pcm_channel(card); + if (!dmabuf->write_channel) + return -EBUSY; + } + } + } + + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) + return ret; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) + return ret; + } else { + + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + } + } + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&dmabuf->wait, &waita); + while (count > 0) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -EAGAIN; + break; + } + continue; + } + + swptr = dmabuf->swptr; + cnt = ali_get_free_write_space(state); + /* Bound the maximum size to how much we can copy to the + * dma buffer before we hit the end. If we have more to + * copy then it will get done in a second pass of this + * loop starting from the beginning of the buffer. + */ + if (cnt > (dmabuf->dmasize - swptr)) + cnt = dmabuf->dmasize - swptr; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG2 + printk(KERN_INFO + "ali_audio: ali_write: %d bytes available space\n", + cnt); +#endif + if (cnt > count) + cnt = count; + /* Lop off the last two bits to force the code to always + * write in full samples. This keeps software that sets + * O_NONBLOCK but doesn't check the return value of the + * write call from getting things out of state where they + * think a full 4 byte sample was written when really only + * a portion was, resulting in odd sound and stereo + * hysteresis. + */ + cnt &= ~0x3; + if (cnt <= 0) { + unsigned long tmo; + // There is data waiting to be played + /* + * Force the trigger setting since we would + * deadlock with it set any other way + */ + if (codec_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + dmabuf->trigger = SPDIF_ENABLE_OUTPUT; + ali_update_lvi(state, 3); + } else { + + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_update_lvi(state, 0); + } + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto ret; + } + /* Not strictly correct but works */ + tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4); + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer underrun. And worse, there is + NOTHING we can do to prevent it. */ + + /* FIXME - do timeout handling here !! */ + schedule_timeout(tmo >= 2 ? tmo : 2); + + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto ret; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto ret; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); + if (PM_SUSPENDED(card)) { + spin_unlock_irqrestore(&card->lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count += cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (swptr % dmabuf->fragsize) { + x = dmabuf->fragsize - (swptr % dmabuf->fragsize); + memset(dmabuf->rawbuf + swptr, '\0', x); + } +ret: + if (codec_independent_spdif_locked > 0) { + ali_update_lvi(state, 2); + } else { + if (controller_independent_spdif_locked > 0) { + ali_update_lvi(state, 3); + } else { + ali_update_lvi(state, 0); + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int ali_poll(struct file *file, struct poll_table_struct + *wait) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + if (!dmabuf->ready) + return 0; + poll_wait(file, &dmabuf->wait, wait); + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) { + if (dmabuf->count >= (signed) dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE && (dmabuf->enable & (DAC_RUNNING|CODEC_SPDIFOUT_RUNNING|CONTROLLER_SPDIFOUT_RUNNING))) { + if ((signed) dmabuf->dmasize >= dmabuf->count + (signed) dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&state->card->lock, flags); + return mask; +} + +static int ali_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret = -EINVAL; + unsigned long size; + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if (!dmabuf->write_channel && (dmabuf->write_channel = state->card->alloc_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if (vma->vm_flags & VM_READ) { + if (!dmabuf->read_channel && (dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card)) == NULL) { + ret = -EBUSY; + goto out; + } + } + if ((ret = prog_dmabuf(state, 0)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + goto out; + ret = -EAGAIN; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + dmabuf->mapped = 1; + dmabuf->trigger = 0; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_channel *c = NULL; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned int i_scr; + int val = 0, ret; + struct ac97_codec *codec = state->card->ac97_codec[0]; + void __user *argp = (void __user *)arg; + int __user *p = argp; + +#ifdef DEBUG + printk("ali_audio: ali_ioctl, arg=0x%x, cmd=", + arg ? *p : 0); +#endif + switch (cmd) { + case OSS_GETVERSION: +#ifdef DEBUG + printk("OSS_GETVERSION\n"); +#endif + return put_user(SOUND_VERSION, p); + case SNDCTL_DSP_RESET: +#ifdef DEBUG + printk("SNDCTL_DSP_RESET\n"); +#endif + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->enable == DAC_RUNNING) { + c = dmabuf->write_channel; + __stop_dac(state); + } + if (dmabuf->enable == ADC_RUNNING) { + c = dmabuf->read_channel; + __stop_adc(state); + } + if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + c = dmabuf->codec_spdifout_channel; + __stop_spdifout(state); + } + if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + c = dmabuf->controller_spdifout_channel; + __stop_spdifout(state); + } + if (c != NULL) { + outb(2, state->card->iobase + c->port + OFF_CR); /* reset DMA machine */ + outl(virt_to_bus(&c->sg[0]), + state->card->iobase + c->port + OFF_BDBAR); + outb(0, state->card->iobase + c->port + OFF_CIV); + outb(0, state->card->iobase + c->port + OFF_LVI); + } + + spin_unlock_irqrestore(&state->card->lock, flags); + synchronize_irq(state->card->pci_dev->irq); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + return 0; + case SNDCTL_DSP_SYNC: +#ifdef DEBUG + printk("SNDCTL_DSP_SYNC\n"); +#endif + if (codec_independent_spdif_locked > 0) { + if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_spdifout(state, 1))) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->enable != + CONTROLLER_SPDIFOUT_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_spdifout(state, 1))) + return val; + } else { + if (dmabuf->enable != DAC_RUNNING + || file->f_flags & O_NONBLOCK) + return 0; + if ((val = drain_dac(state, 1))) + return val; + } + } + dmabuf->total_bytes = 0; + return 0; + case SNDCTL_DSP_SPEED: /* set smaple rate */ +#ifdef DEBUG + printk("SNDCTL_DSP_SPEED\n"); +#endif + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + if ((state->card->ac97_status & SPDIF_ON)) { /* S/PDIF Enabled */ + /* RELTEK ALC650 only support 48000, need to check that */ + if (ali_valid_spdif_rate(codec, val)) { + if (codec_independent_spdif_locked > 0) { + ali_set_spdif_output(state, -1, 0); + stop_spdifout(state); + dmabuf->ready = 0; + /* I add test codec independent spdif out */ + spin_lock_irqsave(&state->card->lock, flags); + ali_set_codecspdifout_rate(state, val); // I modified + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + i_scr = inl(state->card->iobase + ALI_SCR); + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr&0x00300000) == 0x00200000) + { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } else { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } + } + } + + if (!(state->card->ac97_status & SPDIF_ON)) { + val = dmabuf->rate; + } + } else { + if (controller_independent_spdif_locked > 0) + { + stop_spdifout(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_spdifout_rate(state, controller_independent_spdif_locked); + spin_unlock_irqrestore(&state->card->lock, flags); + } else { + /* Set DAC rate */ + ali_set_spdif_output(state, -1, 0); + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + /* Set S/PDIF transmitter rate. */ + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val); + if (!(state->card->ac97_status & SPDIF_ON)) + { + val = dmabuf->rate; + } + } + } + } else { /* Not a valid rate for S/PDIF, ignore it */ + val = dmabuf->rate; + } + } else { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + ali_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + return put_user(dmabuf->rate, p); + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ +#ifdef DEBUG + printk("SNDCTL_DSP_STEREO\n"); +#endif + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + return put_user(1, p); + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2))) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3))) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0))) + return val; + } + } + } + + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 1))) + return val; + } +#ifdef DEBUG + printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize); +#endif + return put_user(dmabuf->userfragsize, p); + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETFMTS\n"); +#endif + return put_user(AFMT_S16_LE, p); + case SNDCTL_DSP_SETFMT: /* Select sample format */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETFMT\n"); +#endif + return put_user(AFMT_S16_LE, p); + case SNDCTL_DSP_CHANNELS: // add support 4,6 channel +#ifdef DEBUG + printk("SNDCTL_DSP_CHANNELS\n"); +#endif + if (get_user(val, p)) + return -EFAULT; + if (val > 0) { + if (dmabuf->enable & DAC_RUNNING) { + stop_dac(state); + } + if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (dmabuf->enable & ADC_RUNNING) { + stop_adc(state); + } + } else { + return put_user(state->card->channels, p); + } + + i_scr = inl(state->card->iobase + ALI_SCR); + /* Current # of channels enabled */ + if (i_scr & 0x00000100) + ret = 4; + else if (i_scr & 0x00000200) + ret = 6; + else + ret = 2; + switch (val) { + case 2: /* 2 channels is always supported */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR)); + } else + outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR)); + /* Do we need to change mixer settings???? */ + break; + case 4: /* Supported on some chipsets, better check first */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR)); + } else + outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR)); + break; + case 6: /* Supported on some chipsets, better check first */ + if (codec_independent_spdif_locked > 0) { + outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR)); + } else + outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR)); + break; + default: /* nothing else is ever supported by the chipset */ + val = ret; + break; + } + return put_user(val, p); + case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */ + /* we update the swptr to the end of the last sg segment then return */ +#ifdef DEBUG + printk("SNDCTL_DSP_POST\n"); +#endif + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING)) + return 0; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING)) + return 0; + } else { + if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING)) + return 0; + } + } + if ((dmabuf->swptr % dmabuf->fragsize) != 0) { + val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize); + dmabuf->swptr += val; + dmabuf->count += val; + } + return 0; + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; +#ifdef DEBUG + printk("SNDCTL_DSP_SUBDIVIDE %d\n", val); +#endif + dmabuf->subdivision = val; + dmabuf->ready = 0; + return 0; + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + dmabuf->ossfragsize = 1 << (val & 0xffff); + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags) + return -EINVAL; + /* + * Bound the frag size into our allowed range of 256 - 4096 + */ + if (dmabuf->ossfragsize < 256) + dmabuf->ossfragsize = 256; + else if (dmabuf->ossfragsize > 4096) + dmabuf->ossfragsize = 4096; + /* + * The numfrags could be something reasonable, or it could + * be 0xffff meaning "Give me as much as possible". So, + * we check the numfrags * fragsize doesn't exceed our + * 64k buffer limit, nor is it less than our 8k minimum. + * If it fails either one of these checks, then adjust the + * number of fragments, not the size of them. It's OK if + * our number of fragments doesn't equal 32 or anything + * like our hardware based number now since we are using + * a different frag count for the hardware. Before we get + * into this though, bound the maxfrags to avoid overflow + * issues. A reasonable bound would be 64k / 256 since our + * maximum buffer size is 64k and our minimum frag size is + * 256. On the other end, our minimum buffer size is 8k and + * our maximum frag size is 4k, so the lower bound should + * be 2. + */ + if (dmabuf->ossmaxfrags > 256) + dmabuf->ossmaxfrags = 256; + else if (dmabuf->ossmaxfrags < 2) + dmabuf->ossmaxfrags = 2; + val = dmabuf->ossfragsize * dmabuf->ossmaxfrags; + while (val < 8192) { + val <<= 1; + dmabuf->ossmaxfrags <<= 1; + } + while (val > 65536) { + val >>= 1; + dmabuf->ossmaxfrags >>= 1; + } + dmabuf->ready = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val, + dmabuf->ossfragsize, dmabuf->ossmaxfrags); +#endif + return 0; + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + } + } + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + if (dmabuf->mapped) + abinfo.bytes = dmabuf->dmasize; + else + abinfo.bytes = ali_get_free_write_space(state); + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", + abinfo.bytes, abinfo.fragsize, abinfo.fragments, + abinfo.fragstotal); +#endif + return copy_to_user(argp, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (codec_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0) + return val; + } else { + if (controller_independent_spdif_locked > 0) { + if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0) + return val; + } else { + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + } + } + spin_lock_irqsave(&state->card->lock, flags); + val = ali_get_free_write_space(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.ptr = dmabuf->hwptr; + cinfo.blocks = val / dmabuf->userfragsize; + if (codec_independent_spdif_locked > 0) { + if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 2); + } + } else { + if (controller_independent_spdif_locked > 0) { + if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 3); + } + } else { + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { + dmabuf->count += val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 0); + } + } + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT : 0; + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + abinfo.bytes = ali_get_available_read_data(state); + abinfo.fragsize = dmabuf->userfragsize; + abinfo.fragstotal = dmabuf->userfrags; + abinfo.fragments = abinfo.bytes / dmabuf->userfragsize; + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", + abinfo.bytes, abinfo.fragsize, abinfo.fragments, + abinfo.fragstotal); +#endif + return copy_to_user(argp, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + val = ali_get_available_read_data(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = val / dmabuf->userfragsize; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) { + dmabuf->count -= val; + dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize; + __ali_update_lvi(state, 1); + } + spin_unlock_irqrestore(&state->card->lock, flags); +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes, + cinfo.blocks, cinfo.ptr, dmabuf->count); +#endif + return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT: 0; + case SNDCTL_DSP_NONBLOCK: +#ifdef DEBUG + printk("SNDCTL_DSP_NONBLOCK\n"); +#endif + file->f_flags |= O_NONBLOCK; + return 0; + case SNDCTL_DSP_GETCAPS: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCAPS\n"); +#endif + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + DSP_CAP_MMAP | DSP_CAP_BIND, p); + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#ifdef DEBUG + printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger); +#endif + return put_user(dmabuf->trigger, p); + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; +#if defined(DEBUG) || defined(DEBUG_MMAP) + printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val); +#endif + if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) { + stop_adc(state); + } + if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) { + stop_spdifout(state); + } + dmabuf->trigger = val; + if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) { + if (!dmabuf->write_channel) { + dmabuf->ready = 0; + dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); + if (!dmabuf->write_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 0); + spin_unlock_irqrestore(&state->card->lock, + flags); + } else + start_dac(state); + } + if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) { + if (!dmabuf->codec_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card); + if (!dmabuf->codec_spdifout_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 2))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 2); + spin_unlock_irqrestore(&state->card->lock, + flags); + } else + start_spdifout(state); + } + if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) { + if (!dmabuf->controller_spdifout_channel) { + dmabuf->ready = 0; + dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card); + if (!dmabuf->controller_spdifout_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 3))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = ali_get_free_write_space(state); + dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; + __ali_update_lvi(state, 3); + spin_unlock_irqrestore(&state->card->lock, flags); + } else + start_spdifout(state); + } + if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) { + if (!dmabuf->read_channel) { + dmabuf->ready = 0; + dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); + if (!dmabuf->read_channel) + return -EBUSY; + } + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (dmabuf->mapped) { + spin_lock_irqsave(&state->card->lock, + flags); + ali_update_ptr(state); + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + } + ali_update_lvi(state, 1); + start_adc(state); + } + return 0; + case SNDCTL_DSP_SETDUPLEX: +#ifdef DEBUG + printk("SNDCTL_DSP_SETDUPLEX\n"); +#endif + return -EINVAL; + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + ali_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); +#ifdef DEBUG + printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count); +#endif + return put_user(val, p); + case SOUND_PCM_READ_RATE: +#ifdef DEBUG + printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate); +#endif + return put_user(dmabuf->rate, p); + case SOUND_PCM_READ_CHANNELS: +#ifdef DEBUG + printk("SOUND_PCM_READ_CHANNELS\n"); +#endif + return put_user(2, p); + case SOUND_PCM_READ_BITS: +#ifdef DEBUG + printk("SOUND_PCM_READ_BITS\n"); +#endif + return put_user(AFMT_S16_LE, p); + case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_SETSPDIF\n"); +#endif + if (get_user(val, p)) + return -EFAULT; + /* Check to make sure the codec supports S/PDIF transmitter */ + if ((state->card->ac97_features & 4)) { + /* mask out the transmitter speed bits so the user can't set them */ + val &= ~0x3000; + /* Add the current transmitter speed bits to the passed value */ + ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + val |= (ret & 0x3000); + ali_ac97_set(codec, AC97_SPDIF_CONTROL, val); + if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) { + printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val); + return -EFAULT; + } + } +#ifdef DEBUG + else + printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); +#endif + return put_user(val, p); + case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */ +#ifdef DEBUG + printk("SNDCTL_DSP_GETSPDIF\n"); +#endif + if (get_user(val, p)) + return -EFAULT; + /* Check to make sure the codec supports S/PDIF transmitter */ + if (!(state->card->ac97_features & 4)) { +#ifdef DEBUG + printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n"); +#endif + val = 0; + } else { + val = ali_ac97_get(codec, AC97_SPDIF_CONTROL); + } + + return put_user(val, p); +//end add support spdif out +//add support 4,6 channel + case SNDCTL_DSP_GETCHANNELMASK: +#ifdef DEBUG + printk("SNDCTL_DSP_GETCHANNELMASK\n"); +#endif + if (get_user(val, p)) + return -EFAULT; + /* Based on AC'97 DAC support, not ICH hardware */ + val = DSP_BIND_FRONT; + if (state->card->ac97_features & 0x0004) + val |= DSP_BIND_SPDIF; + if (state->card->ac97_features & 0x0080) + val |= DSP_BIND_SURR; + if (state->card->ac97_features & 0x0140) + val |= DSP_BIND_CENTER_LFE; + return put_user(val, p); + case SNDCTL_DSP_BIND_CHANNEL: +#ifdef DEBUG + printk("SNDCTL_DSP_BIND_CHANNEL\n"); +#endif + if (get_user(val, p)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; /* Always report this as being enabled */ + if (state->card->ac97_status & SPDIF_ON) + val |= DSP_BIND_SPDIF; + else { + if (state->card->ac97_status & SURR_ON) + val |= DSP_BIND_SURR; + if (state->card-> + ac97_status & CENTER_LFE_ON) + val |= DSP_BIND_CENTER_LFE; + } + } else { /* Not a query, set it */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (dmabuf->enable == DAC_RUNNING) { + stop_dac(state); + } + if (val & DSP_BIND_SPDIF) { /* Turn on SPDIF */ + /* Ok, this should probably define what slots + * to use. For now, we'll only set it to the + * defaults: + * + * non multichannel codec maps to slots 3&4 + * 2 channel codec maps to slots 7&8 + * 4 channel codec maps to slots 6&9 + * 6 channel codec maps to slots 10&11 + * + * there should be some way for the app to + * select the slot assignment. + */ + i_scr = inl(state->card->iobase + ALI_SCR); + if (codec_independent_spdif_locked > 0) { + + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00200000) { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } + } + } + } else { /* codec spdif out (pcm out share ) */ + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate); //I do not modify + } + + if (!(state->card->ac97_status & SPDIF_ON)) + val &= ~DSP_BIND_SPDIF; + } else { + int mask; + int channels; + /* Turn off S/PDIF if it was on */ + if (state->card->ac97_status & SPDIF_ON) + ali_set_spdif_output(state, -1, 0); + mask = + val & (DSP_BIND_FRONT | DSP_BIND_SURR | + DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT | DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + val = DSP_BIND_FRONT; + channels = 2; + break; + } + ali_set_dac_channels(state, channels); + /* check that they really got turned on */ + if (!state->card->ac97_status & SURR_ON) + val &= ~DSP_BIND_SURR; + if (!state->card-> + ac97_status & CENTER_LFE_ON) + val &= ~DSP_BIND_CENTER_LFE; + } + } + return put_user(val, p); + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + +static int ali_open(struct inode *inode, struct file *file) +{ + int i = 0; + struct ali_card *card = devs; + struct ali_state *state = NULL; + struct dmabuf *dmabuf = NULL; + unsigned int i_scr; + + /* find an available virtual channel (instance of /dev/dsp) */ + + while (card != NULL) { + + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + + for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct ali_state *) kmalloc(sizeof(struct ali_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct ali_state)); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + card = card->next; + } + + /* no more virtual channel avaiable */ + if (!state) + return -ENODEV; +found_virt: + /* initialize the virtual channel */ + + state->virt = i; + state->card = card; + state->magic = ALI5455_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + mutex_init(&state->open_mutex); + file->private_data = state; + dmabuf->trigger = 0; + /* allocate hardware channels */ + if (file->f_mode & FMODE_READ) { + if ((dmabuf->read_channel = + card->alloc_rec_pcm_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= PCM_ENABLE_INPUT; + ali_set_adc_rate(state, 8000); + } + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + if ((dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; + ali_set_codecspdifout_rate(state, codec_independent_spdif_locked); //It must add + i_scr = inl(state->card->iobase + ALI_SCR); + if ((i_scr & 0x00300000) == 0x00100000) { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00200000) { + ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked); + } else { + if ((i_scr & 0x00300000) == 0x00300000) { + ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked); + } else { + ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked); + } + } + + } + } else { + if (controller_independent_spdif_locked > 0) { + if ((dmabuf->controller_spdifout_channel = card->alloc_controller_spdifout_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + dmabuf->trigger |= SPDIF_ENABLE_OUTPUT; + ali_set_spdifout_rate(state, controller_independent_spdif_locked); + } else { + if ((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) { + kfree(card->states[i]); + card->states[i] = NULL; + return -EBUSY; + } + /* Initialize to 8kHz? What if we don't support 8kHz? */ + /* Let's change this to check for S/PDIF stuff */ + + dmabuf->trigger |= PCM_ENABLE_OUTPUT; + if (codec_pcmout_share_spdif_locked) { + ali_set_dac_rate(state, codec_pcmout_share_spdif_locked); + ali_set_spdif_output(state, AC97_EA_SPSA_3_4, codec_pcmout_share_spdif_locked); + } else { + ali_set_dac_rate(state, 8000); + } + } + + } + } + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample, but we don't support those so we + set it immediately to stereo and 16bit, which is all we do support */ + dmabuf->fmt |= ALI5455_FMT_16BIT | ALI5455_FMT_STEREO; + dmabuf->ossfragsize = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + outl(0x00000000, card->iobase + ALI_INTERRUPTCR); + outl(0x00000000, card->iobase + ALI_INTERRUPTSR); + return nonseekable_open(inode, file); +} + +static int ali_release(struct inode *inode, struct file *file) +{ + struct ali_state *state = (struct ali_state *) file->private_data; + struct ali_card *card = state->card; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + lock_kernel(); + + /* stop DMA state machine and free DMA buffers/channels */ + if (dmabuf->trigger & PCM_ENABLE_OUTPUT) + drain_dac(state, 0); + + if (dmabuf->trigger & SPDIF_ENABLE_OUTPUT) + drain_spdifout(state, 0); + + if (dmabuf->trigger & PCM_ENABLE_INPUT) + stop_adc(state); + + spin_lock_irqsave(&card->lock, flags); + dealloc_dmabuf(state); + if (file->f_mode & FMODE_WRITE) { + if (codec_independent_spdif_locked > 0) { + state->card->free_pcm_channel(state->card, dmabuf->codec_spdifout_channel->num); + } else { + if (controller_independent_spdif_locked > 0) + state->card->free_pcm_channel(state->card, + dmabuf->controller_spdifout_channel->num); + else state->card->free_pcm_channel(state->card, + dmabuf->write_channel->num); + } + } + if (file->f_mode & FMODE_READ) + state->card->free_pcm_channel(state->card, dmabuf->read_channel->num); + + state->card->states[state->virt] = NULL; + kfree(state); + spin_unlock_irqrestore(&card->lock, flags); + unlock_kernel(); + return 0; +} + +static /*const */ struct file_operations ali_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ali_read, + .write = ali_write, + .poll = ali_poll, + .ioctl = ali_ioctl, + .mmap = ali_mmap, + .open = ali_open, + .release = ali_release, +}; + +/* Read AC97 codec registers */ +static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct ali_card *card = dev->private_data; + int count1 = 100; + char val; + unsigned short int data = 0, count, addr1, addr2 = 0; + + spin_lock(&card->ac97_lock); + while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) + udelay(1); + + addr1 = reg; + reg |= 0x0080; + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x08) + break; + } + if (count == 0x7f) + { + spin_unlock(&card->ac97_lock); + return -1; + } + outw(reg, (card->iobase + ALI_CPR) + 2); + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x02) { + data = inw(card->iobase + ALI_SPR); + addr2 = inw((card->iobase + ALI_SPR) + 2); + break; + } + } + spin_unlock(&card->ac97_lock); + if (count == 0x7f) + return -1; + if (addr2 != addr1) + return -1; + return ((u16) data); +} + +/* write ac97 codec register */ + +static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct ali_card *card = dev->private_data; + int count1 = 100; + char val; + unsigned short int count; + + spin_lock(&card->ac97_lock); + while (count1-- && (inl(card->iobase + ALI_CAS) & 0x80000000)) + udelay(1); + + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x08) + break; + } + if (count == 0x7f) { + printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); + spin_unlock(&card->ac97_lock); + return; + } + outw(data, (card->iobase + ALI_CPR)); + outb(reg, (card->iobase + ALI_CPR) + 2); + for (count = 0; count < 0x7f; count++) { + val = inb(card->iobase + ALI_CSPSR); + if (val & 0x01) + break; + } + spin_unlock(&card->ac97_lock); + if (count == 0x7f) + printk(KERN_WARNING "ali_ac97_set: AC97 codec register access timed out. \n"); + return; +} + +/* OSS /dev/mixer file operation methods */ + +static int ali_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = iminor(inode); + struct ali_card *card = devs; + for (card = devs; card != NULL; card = card->next) { + /* + * If we are initializing and then fail, card could go + * away unuexpectedly while we are in the for() loop. + * So, check for card on each iteration before we check + * for card->initializing to avoid a possible oops. + * This usually only matters for times when the driver is + * autoloaded by kmod. + */ + for (i = 0; i < 50 && card && card->initializing; i++) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + for (i = 0; i < NR_AC97 && card && !card->initializing; i++) + if (card->ac97_codec[i] != NULL + && card->ac97_codec[i]->dev_mixer == minor) { + file->private_data = card->ac97_codec[i]; + return nonseekable_open(inode, file); + } + } + return -ENODEV; +} + +static int ali_ioctl_mixdev(struct inode *inode, + struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *) file->private_data; + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const */ struct file_operations ali_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = ali_ioctl_mixdev, + .open = ali_open_mixdev, +}; + +/* AC97 codec initialisation. These small functions exist so we don't + duplicate code between module init and apm resume */ + +static inline int ali_ac97_exists(struct ali_card *card, int ac97_number) +{ + unsigned int i = 1; + u32 reg = inl(card->iobase + ALI_RTSR); + if (ac97_number) { + while (i < 100) { + + reg = inl(card->iobase + ALI_RTSR); + if (reg & 0x40) { + break; + } else { + outl(reg | 0x00000040, + card->iobase + 0x34); + udelay(1); + } + i++; + } + + } else { + while (i < 100) { + reg = inl(card->iobase + ALI_RTSR); + if (reg & 0x80) { + break; + } else { + outl(reg | 0x00000080, + card->iobase + 0x34); + udelay(1); + } + i++; + } + } + + if (ac97_number) + return reg & 0x40; + else + return reg & 0x80; +} + +static inline int ali_ac97_enable_variable_rate(struct ac97_codec *codec) +{ + ali_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + ali_ac97_set(codec, AC97_EXTENDED_STATUS, ali_ac97_get(codec, AC97_EXTENDED_STATUS) | 0xE800); + return (ali_ac97_get(codec, AC97_EXTENDED_STATUS) & 1); +} + + +static int ali_ac97_probe_and_powerup(struct ali_card *card, struct ac97_codec *codec) +{ + /* Returns 0 on failure */ + int i; + u16 addr; + if (ac97_probe_codec(codec) == 0) + return 0; + /* ac97_probe_codec is success ,then begin to init codec */ + ali_ac97_set(codec, AC97_RESET, 0xffff); + if (card->channel[0].used == 1) { + ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); + ali_ac97_set(codec, AC97_LINEIN_VOL, 0x0808); + ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); + } + + if (card->channel[2].used == 1) //if MICin then init codec + { + ali_ac97_set(codec, AC97_RECORD_SELECT, 0x0000); + ali_ac97_set(codec, AC97_MIC_VOL, 0x8808); + ali_ac97_set(codec, AC97_RECORD_GAIN, 0x0F0F); + ali_ac97_set(codec, AC97_RECORD_GAIN_MIC, 0x0000); + } + + ali_ac97_set(codec, AC97_MASTER_VOL_STEREO, 0x0000); + ali_ac97_set(codec, AC97_HEADPHONE_VOL, 0x0000); + ali_ac97_set(codec, AC97_PCMOUT_VOL, 0x0000); + ali_ac97_set(codec, AC97_CD_VOL, 0x0808); + ali_ac97_set(codec, AC97_VIDEO_VOL, 0x0808); + ali_ac97_set(codec, AC97_AUX_VOL, 0x0808); + ali_ac97_set(codec, AC97_PHONE_VOL, 0x8048); + ali_ac97_set(codec, AC97_PCBEEP_VOL, 0x0000); + ali_ac97_set(codec, AC97_GENERAL_PURPOSE, AC97_GP_MIX); + ali_ac97_set(codec, AC97_MASTER_VOL_MONO, 0x0000); + ali_ac97_set(codec, 0x38, 0x0000); + addr = ali_ac97_get(codec, 0x2a); + ali_ac97_set(codec, 0x2a, addr | 0x0001); + addr = ali_ac97_get(codec, 0x2a); + addr = ali_ac97_get(codec, 0x28); + ali_ac97_set(codec, 0x2c, 0xbb80); + addr = ali_ac97_get(codec, 0x2c); + /* power it all up */ + ali_ac97_set(codec, AC97_POWER_CONTROL, + ali_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i = 10; i && ((ali_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); i--) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 20); + } + /* FIXME !! */ + i++; + return i; +} + + +/* I clone ali5455(2.4.7 ) not clone i810_audio(2.4.18) */ + +static int ali_reset_5455(struct ali_card *card) +{ + outl(0x80000003, card->iobase + ALI_SCR); + outl(0x83838383, card->iobase + ALI_FIFOCR1); + outl(0x83838383, card->iobase + ALI_FIFOCR2); + if (controller_pcmout_share_spdif_locked > 0) { + outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), + card->iobase + ALI_SPDIFICS); + outl(0x0408000a, card->iobase + ALI_INTERFACECR); + } else { + if (codec_independent_spdif_locked > 0) { + outl((inl(card->iobase + ALI_SCR) | 0x00100000), card->iobase + ALI_SCR); // now I select slot 7 & 8 + outl(0x00200000, card->iobase + ALI_INTERFACECR); //enable codec independent spdifout + } else + outl(0x04080002, card->iobase + ALI_INTERFACECR); + } + + outl(0x00000000, card->iobase + ALI_INTERRUPTCR); + outl(0x00000000, card->iobase + ALI_INTERRUPTSR); + if (controller_independent_spdif_locked > 0) + outl((inl(card->iobase + ALI_SPDIFICS) | 0x00000001), + card->iobase + ALI_SPDIFICS); + return 1; +} + + +static int ali_ac97_random_init_stuff(struct ali_card + *card) +{ + u32 reg = inl(card->iobase + ALI_SCR); + int i = 0; + reg = inl(card->iobase + ALI_SCR); + if ((reg & 2) == 0) /* Cold required */ + reg |= 2; + else + reg |= 1; /* Warm */ + reg &= ~0x80000000; /* ACLink on */ + outl(reg, card->iobase + ALI_SCR); + + while (i < 10) { + if ((inl(card->iobase + 0x18) & (1 << 1)) == 0) + break; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ / 20); + i++; + } + if (i == 10) { + printk(KERN_ERR "ali_audio: AC'97 reset failed.\n"); + return 0; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 2); + return 1; +} + +/* AC97 codec initialisation. */ + +static int __devinit ali_ac97_init(struct ali_card *card) +{ + int num_ac97 = 0; + int total_channels = 0; + struct ac97_codec *codec; + u16 eid; + + if (!ali_ac97_random_init_stuff(card)) + return 0; + + /* Number of channels supported */ + /* What about the codec? Just because the ICH supports */ + /* multiple channels doesn't mean the codec does. */ + /* we'll have to modify this in the codec section below */ + /* to reflect what the codec has. */ + /* ICH and ICH0 only support 2 channels so don't bother */ + /* to check.... */ + inl(card->iobase + ALI_CPR); + card->channels = 2; + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + + /* Assume codec isn't available until we go through the + * gauntlet below */ + card->ac97_codec[num_ac97] = NULL; + /* The ICH programmer's reference says you should */ + /* check the ready status before probing. So we chk */ + /* What do we do if it's not ready? Wait and try */ + /* again, or abort? */ + if (!ali_ac97_exists(card, num_ac97)) { + if (num_ac97 == 0) + printk(KERN_ERR "ali_audio: Primary codec not ready.\n"); + break; + } + + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + codec->codec_read = ali_ac97_get; + codec->codec_write = ali_ac97_set; + if (!ali_ac97_probe_and_powerup(card, codec)) { + printk(KERN_ERR "ali_audio: timed out waiting for codec %d analog ready", + num_ac97); + kfree(codec); + break; /* it didn't work */ + } + + /* Store state information about S/PDIF transmitter */ + card->ac97_status = 0; + /* Don't attempt to get eid until powerup is complete */ + eid = ali_ac97_get(codec, AC97_EXTENDED_ID); + if (eid == 0xFFFF) { + printk(KERN_ERR "ali_audio: no codec attached ?\n"); + kfree(codec); + break; + } + + card->ac97_features = eid; + /* Now check the codec for useful features to make up for + the dumbness of the ali5455 hardware engine */ + if (!(eid & 0x0001)) + printk(KERN_WARNING + "ali_audio: only 48Khz playback available.\n"); + else { + if (!ali_ac97_enable_variable_rate(codec)) { + printk(KERN_WARNING + "ali_audio: Codec refused to allow VRA, using 48Khz only.\n"); + card->ac97_features &= ~1; + } + } + + /* Determine how many channels the codec(s) support */ + /* - The primary codec always supports 2 */ + /* - If the codec supports AMAP, surround DACs will */ + /* automaticlly get assigned to slots. */ + /* * Check for surround DACs and increment if */ + /* found. */ + /* - Else check if the codec is revision 2.2 */ + /* * If surround DACs exist, assign them to slots */ + /* and increment channel count. */ + + /* All of this only applies to ICH2 and above. ICH */ + /* and ICH0 only support 2 channels. ICH2 will only */ + /* support multiple codecs in a "split audio" config. */ + /* as described above. */ + + /* TODO: Remove all the debugging messages! */ + + if ((eid & 0xc000) == 0) /* primary codec */ + total_channels += 2; + if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) { + printk(KERN_ERR "ali_audio: couldn't register mixer!\n"); + kfree(codec); + break; + } + card->ac97_codec[num_ac97] = codec; + } + /* pick the minimum of channels supported by ICHx or codec(s) */ + card->channels = (card->channels > total_channels) ? total_channels : card->channels; + return num_ac97; +} + +static void __devinit ali_configure_clocking(void) +{ + struct ali_card *card; + struct ali_state *state; + struct dmabuf *dmabuf; + unsigned int i, offset, new_offset; + unsigned long flags; + card = devs; + + /* We could try to set the clocking for multiple cards, but can you even have + * more than one ali in a machine? Besides, clocking is global, so unless + * someone actually thinks more than one ali in a machine is possible and + * decides to rewrite that little bit, setting the rate for more than one card + * is a waste of time. + */ + if (card != NULL) { + state = card->states[0] = (struct ali_state *) + kmalloc(sizeof(struct ali_state), GFP_KERNEL); + if (state == NULL) + return; + memset(state, 0, sizeof(struct ali_state)); + dmabuf = &state->dmabuf; + dmabuf->write_channel = card->alloc_pcm_channel(card); + state->virt = 0; + state->card = card; + state->magic = ALI5455_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + mutex_init(&state->open_mutex); + dmabuf->fmt = ALI5455_FMT_STEREO | ALI5455_FMT_16BIT; + dmabuf->trigger = PCM_ENABLE_OUTPUT; + ali_set_dac_rate(state, 48000); + if (prog_dmabuf(state, 0) != 0) + goto config_out_nodmabuf; + + if (dmabuf->dmasize < 16384) + goto config_out; + + dmabuf->count = dmabuf->dmasize; + outb(31, card->iobase + dmabuf->write_channel->port + OFF_LVI); + + local_irq_save(flags); + start_dac(state); + offset = ali_get_dma_addr(state, 0); + mdelay(50); + new_offset = ali_get_dma_addr(state, 0); + stop_dac(state); + + outb(2, card->iobase + dmabuf->write_channel->port + OFF_CR); + local_irq_restore(flags); + + i = new_offset - offset; + + if (i == 0) + goto config_out; + i = i / 4 * 20; + if (i > 48500 || i < 47500) { + clocking = clocking * clocking / i; + } +config_out: + dealloc_dmabuf(state); +config_out_nodmabuf: + state->card->free_pcm_channel(state->card, state->dmabuf. write_channel->num); + kfree(state); + card->states[0] = NULL; + } +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ + +static int __devinit ali_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct ali_card *card; + if (pci_enable_device(pci_dev)) + return -EIO; + if (pci_set_dma_mask(pci_dev, ALI5455_DMA_MASK)) { + printk(KERN_ERR "ali5455: architecture does not support" + " 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if ((card = kmalloc(sizeof(struct ali_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ali_audio: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + card->initializing = 1; + card->iobase = pci_resource_start(pci_dev, 0); + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = ALI5455_CARD_MAGIC; +#ifdef CONFIG_PM + card->pm_suspended = 0; +#endif + spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); + devs = card; + pci_set_master(pci_dev); + printk(KERN_INFO "ali: %s found at IO 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->irq); + card->alloc_pcm_channel = ali_alloc_pcm_channel; + card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; + card->alloc_rec_mic_channel = ali_alloc_rec_mic_channel; + card->alloc_codec_spdifout_channel = ali_alloc_codec_spdifout_channel; + card->alloc_controller_spdifout_channel = ali_alloc_controller_spdifout_channel; + card->free_pcm_channel = ali_free_pcm_channel; + card->channel[0].offset = 0; + card->channel[0].port = 0x40; + card->channel[0].num = 0; + card->channel[1].offset = 0; + card->channel[1].port = 0x50; + card->channel[1].num = 1; + card->channel[2].offset = 0; + card->channel[2].port = 0x60; + card->channel[2].num = 2; + card->channel[3].offset = 0; + card->channel[3].port = 0x70; + card->channel[3].num = 3; + card->channel[4].offset = 0; + card->channel[4].port = 0xb0; + card->channel[4].num = 4; + /* claim our iospace and irq */ + request_region(card->iobase, 256, card_names[pci_id->driver_data]); + if (request_irq(card->irq, &ali_interrupt, IRQF_SHARED, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "ali_audio: unable to allocate irq %d\n", + card->irq); + release_region(card->iobase, 256); + kfree(card); + return -ENODEV; + } + + if (ali_reset_5455(card) <= 0) { + unregister_sound_dsp(card->dev_audio); + release_region(card->iobase, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + /* initialize AC97 codec and register /dev/mixer */ + if (ali_ac97_init(card) < 0) { + release_region(card->iobase, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + pci_set_drvdata(pci_dev, card); + + if (clocking == 0) { + clocking = 48000; + ali_configure_clocking(); + } + + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) { + int i; + printk(KERN_ERR"ali_audio: couldn't register DSP device!\n"); + release_region(card->iobase, 256); + free_irq(card->irq, card); + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree(card->ac97_codec[i]); + } + kfree(card); + return -ENODEV; + } + card->initializing = 0; + return 0; +} + +static void __devexit ali_remove(struct pci_dev *pci_dev) +{ + int i; + struct ali_card *card = pci_get_drvdata(pci_dev); + /* free hardware resources */ + free_irq(card->irq, devs); + release_region(card->iobase, 256); + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]-> + dev_mixer); + ac97_release_codec(card->ac97_codec[i]); + card->ac97_codec[i] = NULL; + } + unregister_sound_dsp(card->dev_audio); + kfree(card); +} + +#ifdef CONFIG_PM +static int ali_pm_suspend(struct pci_dev *dev, pm_message_t pm_state) +{ + struct ali_card *card = pci_get_drvdata(dev); + struct ali_state *state; + unsigned long flags; + struct dmabuf *dmabuf; + int i, num_ac97; + + if (!card) + return 0; + spin_lock_irqsave(&card->lock, flags); + card->pm_suspended = 1; + for (i = 0; i < NR_HW_CH; i++) { + state = card->states[i]; + if (!state) + continue; + /* this happens only if there are open files */ + dmabuf = &state->dmabuf; + if (dmabuf->enable & DAC_RUNNING || + (dmabuf->count + && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) { + state->pm_saved_dac_rate = dmabuf->rate; + stop_dac(state); + } else { + state->pm_saved_dac_rate = 0; + } + if (dmabuf->enable & ADC_RUNNING) { + state->pm_saved_adc_rate = dmabuf->rate; + stop_adc(state); + } else { + state->pm_saved_adc_rate = 0; + } + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + + spin_unlock_irqrestore(&card->lock, flags); + /* save mixer settings */ + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + if (!codec) + continue; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if ((supported_mixer(codec, i)) && (codec->read_mixer)) { + card->pm_saved_mixer_settings[i][num_ac97] = codec->read_mixer(codec, i); + } + } + } + pci_save_state(dev); /* XXX do we need this? */ + pci_disable_device(dev); /* disable busmastering */ + pci_set_power_state(dev, 3); /* Zzz. */ + return 0; +} + + +static int ali_pm_resume(struct pci_dev *dev) +{ + int num_ac97, i = 0; + struct ali_card *card = pci_get_drvdata(dev); + pci_enable_device(dev); + pci_restore_state(dev); + /* observation of a toshiba portege 3440ct suggests that the + hardware has to be more or less completely reinitialized from + scratch after an apm suspend. Works For Me. -dan */ + ali_ac97_random_init_stuff(card); + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + struct ac97_codec *codec = card->ac97_codec[num_ac97]; + /* check they haven't stolen the hardware while we were + away */ + if (!codec || !ali_ac97_exists(card, num_ac97)) { + if (num_ac97) + continue; + else + BUG(); + } + if (!ali_ac97_probe_and_powerup(card, codec)) + BUG(); + if ((card->ac97_features & 0x0001)) { + /* at probe time we found we could do variable + rates, but APM suspend has made it forget + its magical powers */ + if (!ali_ac97_enable_variable_rate(codec)) + BUG(); + } + /* we lost our mixer settings, so restore them */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (supported_mixer(codec, i)) { + int val = card->pm_saved_mixer_settings[i][num_ac97]; + codec->mixer_state[i] = val; + codec->write_mixer(codec, i, + (val & 0xff), + ((val >> 8) & 0xff)); + } + } + } + + /* we need to restore the sample rate from whatever it was */ + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + if (state) { + if (state->pm_saved_adc_rate) + ali_set_adc_rate(state, state->pm_saved_adc_rate); + if (state->pm_saved_dac_rate) + ali_set_dac_rate(state, state->pm_saved_dac_rate); + } + } + + card->pm_suspended = 0; + /* any processes that were reading/writing during the suspend + probably ended up here */ + for (i = 0; i < NR_HW_CH; i++) { + struct ali_state *state = card->states[i]; + if (state) + wake_up(&state->dmabuf.wait); + } + return 0; +} +#endif /* CONFIG_PM */ + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("ALI 5455 audio support"); +MODULE_LICENSE("GPL"); +module_param(clocking, int, 0); +/* FIXME: bool? */ +module_param(strict_clocking, uint, 0); +module_param(codec_pcmout_share_spdif_locked, uint, 0); +module_param(codec_independent_spdif_locked, uint, 0); +module_param(controller_pcmout_share_spdif_locked, uint, 0); +module_param(controller_independent_spdif_locked, uint, 0); +#define ALI5455_MODULE_NAME "ali5455" +static struct pci_driver ali_pci_driver = { + .name = ALI5455_MODULE_NAME, + .id_table = ali_pci_tbl, + .probe = ali_probe, + .remove = __devexit_p(ali_remove), +#ifdef CONFIG_PM + .suspend = ali_pm_suspend, + .resume = ali_pm_resume, +#endif /* CONFIG_PM */ +}; + +static int __init ali_init_module(void) +{ + printk(KERN_INFO "ALI 5455 + AC97 Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (codec_independent_spdif_locked > 0) { + if (codec_independent_spdif_locked == 32000 + || codec_independent_spdif_locked == 44100 + || codec_independent_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_independent_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + codec_independent_spdif_locked = 0; + } + } + if (controller_independent_spdif_locked > 0) { + if (controller_independent_spdif_locked == 32000 + || controller_independent_spdif_locked == 44100 + || controller_independent_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", controller_independent_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + controller_independent_spdif_locked = 0; + } + } + + if (codec_pcmout_share_spdif_locked > 0) { + if (codec_pcmout_share_spdif_locked == 32000 + || codec_pcmout_share_spdif_locked == 44100 + || codec_pcmout_share_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling S/PDIF at sample rate %dHz.\n", codec_pcmout_share_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + codec_pcmout_share_spdif_locked = 0; + } + } + if (controller_pcmout_share_spdif_locked > 0) { + if (controller_pcmout_share_spdif_locked == 32000 + || controller_pcmout_share_spdif_locked == 44100 + || controller_pcmout_share_spdif_locked == 48000) { + printk(KERN_INFO "ali_audio: Enabling controller S/PDIF at sample rate %dHz.\n", controller_pcmout_share_spdif_locked); + } else { + printk(KERN_INFO "ali_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n"); + controller_pcmout_share_spdif_locked = 0; + } + } + return pci_register_driver(&ali_pci_driver); +} + +static void __exit ali_cleanup_module(void) +{ + pci_unregister_driver(&ali_pci_driver); +} + +module_init(ali_init_module); +module_exit(ali_cleanup_module); +/* +Local Variables: +c-basic-offset: 8 +End: +*/ diff --git a/trunk/sound/oss/au1000.c b/trunk/sound/oss/au1000.c new file mode 100644 index 000000000000..e3796231452a --- /dev/null +++ b/trunk/sound/oss/au1000.c @@ -0,0 +1,2216 @@ +/* + * au1000.c -- Sound driver for Alchemy Au1000 MIPS Internet Edge + * Processor. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + * + * + * Module command line parameters: + * + * Supported devices: + * /dev/dsp standard OSS /dev/dsp device + * /dev/mixer standard OSS /dev/mixer device + * + * Notes: + * + * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are + * taken, slightly modified or not at all, from the ES1371 driver, + * so refer to the credits in es1371.c for those. The rest of the + * code (probe, open, read, write, the ISR, etc.) is new. + * + * Revision history + * 06.27.2001 Initial version + * 03.20.2002 Added mutex locks around read/write methods, to prevent + * simultaneous access on SMP or preemptible kernels. Also + * removed the counter/pointer fragment aligning at the end + * of read/write methods [stevel]. + * 03.21.2002 Add support for coherent DMA on the audio read/write DMA + * channels [stevel]. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#undef AU1000_DEBUG +#undef AU1000_VERBOSE_DEBUG + +#define AU1000_MODULE_NAME "Au1000 audio" +#define PFX AU1000_MODULE_NAME + +#ifdef AU1000_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg) + + +/* misc stuff */ +#define POLL_COUNT 0x5000 +#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC) + +/* Boot options */ +static int vra = 0; // 0 = no VRA, 1 = use VRA if codec supports it +module_param(vra, bool, 0); +MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it"); + + +/* --------------------------------------------------------------------- */ + +struct au1000_state { + /* soundcore stuff */ + int dev_audio; + +#ifdef AU1000_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; +#endif /* AU1000_DEBUG */ + + struct ac97_codec codec; + unsigned codec_base_caps;// AC'97 reg 00h, "Reset Register" + unsigned codec_ext_caps; // AC'97 reg 28h, "Extended Audio ID" + int no_vra; // do not use VRA + + spinlock_t lock; + struct mutex open_mutex; + struct mutex sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + unsigned int dmanr; // DMA Channel number + unsigned sample_rate; // Hz + unsigned src_factor; // SRC interp/decimation (no vra) + unsigned sample_size; // 8 or 16 + int num_channels; // 1 = mono, 2 = stereo, 4, 6 + int dma_bytes_per_sample;// DMA bytes per audio sample frame + int user_bytes_per_sample;// User bytes per audio sample frame + int cnt_factor; // user-to-DMA bytes per audio + // sample frame + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; // # of DMA fragments in DMA buffer + unsigned fragshift; + void *nextIn; // ptr to next-in to DMA buffer + void *nextOut;// ptr to next-out from DMA buffer + int count; // current byte count in DMA buffer + unsigned total_bytes; // total bytes written or read + unsigned error; // over/underrun + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; // user perception of fragment size + unsigned dma_fragsize; // DMA (real) fragment size + unsigned dmasize; // Total DMA buffer size + // (mult. of DMA fragsize) + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned stopped:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac , dma_adc; +} au1000_state; + +/* --------------------------------------------------------------------- */ + + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void au1000_delay(int msec) +{ + unsigned long tmo; + signed long tmo2; + + if (in_interrupt()) + return; + + tmo = jiffies + (msec * HZ) / 1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } +} + + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct au1000_state *s = (struct au1000_state *)codec->private_data; + unsigned long flags; + u32 cmd; + u16 data; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) + err("rdcodec: codec cmd pending expired!"); + + cmd = (u32) addr & AC97C_INDEX_MASK; + cmd |= AC97C_READ; // read command + au_writel(cmd, AC97C_CMD); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) { + err("rdcodec: read poll expired!"); + return 0; + } + + data = au_readl(AC97C_CMD) & 0xffff; + + spin_unlock_irqrestore(&s->lock, flags); + + return data; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct au1000_state *s = (struct au1000_state *)codec->private_data; + unsigned long flags; + u32 cmd; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(au_readl(AC97C_STATUS) & AC97C_CP)) + break; + if (i == POLL_COUNT) + err("wrcodec: codec cmd pending expired!"); + + cmd = (u32) addr & AC97C_INDEX_MASK; + cmd &= ~AC97C_READ; // write command + cmd |= ((u32) data << AC97C_WD_BIT); // OR in the data word + au_writel(cmd, AC97C_CMD); + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void waitcodec(struct ac97_codec *codec) +{ + u16 temp; + int i; + + /* codec_wait is used to wait for a ready state after + an AC97C_RESET. */ + au1000_delay(10); + + // first poll the CODEC_READY tag bit + for (i = 0; i < POLL_COUNT; i++) + if (au_readl(AC97C_STATUS) & AC97C_READY) + break; + if (i == POLL_COUNT) { + err("waitcodec: CODEC_READY poll expired!"); + return; + } + // get AC'97 powerdown control/status register + temp = rdcodec(codec, AC97_POWER_CONTROL); + + // If anything is powered down, power'em up + if (temp & 0x7f00) { + // Power on + wrcodec(codec, AC97_POWER_CONTROL, 0); + au1000_delay(100); + // Reread + temp = rdcodec(codec, AC97_POWER_CONTROL); + } + + // Check if Codec REF,ANL,DAC,ADC ready + if ((temp & 0x7f0f) != 0x000f) + err("codec reg 26 status (0x%x) not ready!!", temp); +} + + +/* --------------------------------------------------------------------- */ + +/* stop the ADC before calling */ +static void set_adc_rate(struct au1000_state *s, unsigned rate) +{ + struct dmabuf *adc = &s->dma_adc; + struct dmabuf *dac = &s->dma_dac; + unsigned adc_rate, dac_rate; + u16 ac97_extstat; + + if (s->no_vra) { + // calc SRC factor + adc->src_factor = ((96000 / rate) + 1) >> 1; + adc->sample_rate = 48000 / adc->src_factor; + return; + } + + adc->src_factor = 1; + + ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS); + + rate = rate > 48000 ? 48000 : rate; + + // enable VRA + wrcodec(&s->codec, AC97_EXTENDED_STATUS, + ac97_extstat | AC97_EXTSTAT_VRA); + // now write the sample rate + wrcodec(&s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate); + // read it back for actual supported rate + adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE); + +#ifdef AU1000_VERBOSE_DEBUG + dbg("%s: set to %d Hz", __FUNCTION__, adc_rate); +#endif + + // some codec's don't allow unequal DAC and ADC rates, in which case + // writing one rate reg actually changes both. + dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE); + if (dac->num_channels > 2) + wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate); + if (dac->num_channels > 4) + wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate); + + adc->sample_rate = adc_rate; + dac->sample_rate = dac_rate; +} + +/* stop the DAC before calling */ +static void set_dac_rate(struct au1000_state *s, unsigned rate) +{ + struct dmabuf *dac = &s->dma_dac; + struct dmabuf *adc = &s->dma_adc; + unsigned adc_rate, dac_rate; + u16 ac97_extstat; + + if (s->no_vra) { + // calc SRC factor + dac->src_factor = ((96000 / rate) + 1) >> 1; + dac->sample_rate = 48000 / dac->src_factor; + return; + } + + dac->src_factor = 1; + + ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS); + + rate = rate > 48000 ? 48000 : rate; + + // enable VRA + wrcodec(&s->codec, AC97_EXTENDED_STATUS, + ac97_extstat | AC97_EXTSTAT_VRA); + // now write the sample rate + wrcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate); + // I don't support different sample rates for multichannel, + // so make these channels the same. + if (dac->num_channels > 2) + wrcodec(&s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate); + if (dac->num_channels > 4) + wrcodec(&s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate); + // read it back for actual supported rate + dac_rate = rdcodec(&s->codec, AC97_PCM_FRONT_DAC_RATE); + +#ifdef AU1000_VERBOSE_DEBUG + dbg("%s: set to %d Hz", __FUNCTION__, dac_rate); +#endif + + // some codec's don't allow unequal DAC and ADC rates, in which case + // writing one rate reg actually changes both. + adc_rate = rdcodec(&s->codec, AC97_PCM_LR_ADC_RATE); + + dac->sample_rate = dac_rate; + adc->sample_rate = adc_rate; +} + +static void stop_dac(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_dac; + unsigned long flags; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + disable_dma(db->dmanr); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void stop_adc(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_adc; + unsigned long flags; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + disable_dma(db->dmanr); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void set_xmit_slots(int num_channels) +{ + u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_XMIT_SLOTS_MASK; + + switch (num_channels) { + case 1: // mono + case 2: // stereo, slots 3,4 + ac97_config |= (0x3 << AC97C_XMIT_SLOTS_BIT); + break; + case 4: // stereo with surround, slots 3,4,7,8 + ac97_config |= (0x33 << AC97C_XMIT_SLOTS_BIT); + break; + case 6: // stereo with surround and center/LFE, slots 3,4,6,7,8,9 + ac97_config |= (0x7b << AC97C_XMIT_SLOTS_BIT); + break; + } + + au_writel(ac97_config, AC97C_CONFIG); +} + +static void set_recv_slots(int num_channels) +{ + u32 ac97_config = au_readl(AC97C_CONFIG) & ~AC97C_RECV_SLOTS_MASK; + + /* + * Always enable slots 3 and 4 (stereo). Slot 6 is + * optional Mic ADC, which I don't support yet. + */ + ac97_config |= (0x3 << AC97C_RECV_SLOTS_BIT); + + au_writel(ac97_config, AC97C_CONFIG); +} + +static void start_dac(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_dac; + unsigned long flags; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + au_readl(AC97C_STATUS); // read status to clear sticky bits + + // reset Buffer 1 and 2 pointers to nextOut and nextOut+dma_fragsize + buf1 = virt_to_phys(db->nextOut); + buf2 = buf1 + db->dma_fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + set_xmit_slots(db->num_channels); + + init_dma(db->dmanr); + if (get_dma_active_buffer(db->dmanr) == 0) { + clear_dma_done0(db->dmanr); // clear DMA done bit + set_dma_addr0(db->dmanr, buf1); + set_dma_addr1(db->dmanr, buf2); + } else { + clear_dma_done1(db->dmanr); // clear DMA done bit + set_dma_addr1(db->dmanr, buf1); + set_dma_addr0(db->dmanr, buf2); + } + set_dma_count(db->dmanr, db->dma_fragsize>>1); + enable_dma_buffers(db->dmanr); + + start_dma(db->dmanr); + +#ifdef AU1000_VERBOSE_DEBUG + dump_au1000_dma_channel(db->dmanr); +#endif + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct au1000_state *s) +{ + struct dmabuf *db = &s->dma_adc; + unsigned long flags; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + au_readl(AC97C_STATUS); // read status to clear sticky bits + + // reset Buffer 1 and 2 pointers to nextIn and nextIn+dma_fragsize + buf1 = virt_to_phys(db->nextIn); + buf2 = buf1 + db->dma_fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + set_recv_slots(db->num_channels); + + init_dma(db->dmanr); + if (get_dma_active_buffer(db->dmanr) == 0) { + clear_dma_done0(db->dmanr); // clear DMA done bit + set_dma_addr0(db->dmanr, buf1); + set_dma_addr1(db->dmanr, buf2); + } else { + clear_dma_done1(db->dmanr); // clear DMA done bit + set_dma_addr1(db->dmanr, buf1); + set_dma_addr0(db->dmanr, buf2); + } + set_dma_count(db->dmanr, db->dma_fragsize>>1); + enable_dma_buffers(db->dmanr); + + start_dma(db->dmanr); + +#ifdef AU1000_VERBOSE_DEBUG + dump_au1000_dma_channel(db->dmanr); +#endif + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct au1000_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + ClearPageReserved(page); + dma_free_noncoherent(NULL, + PAGE_SIZE << db->buforder, + db->rawbuf, + db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct au1000_state *s, struct dmabuf *db) +{ + int order; + unsigned user_bytes_per_sec; + unsigned bufs; + struct page *page, *pend; + unsigned rate = db->sample_rate; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = dma_alloc_noncoherent(NULL, + PAGE_SIZE << order, + &db->dmaaddr, + 0))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + SetPageReserved(page); + } + + db->cnt_factor = 1; + if (db->sample_size == 8) + db->cnt_factor *= 2; + if (db->num_channels == 1) + db->cnt_factor *= 2; + db->cnt_factor *= db->src_factor; + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels; + db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ? + 2 : db->num_channels); + + user_bytes_per_sec = rate * db->user_bytes_per_sample; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < user_bytes_per_sec) + db->fragshift = ld2(user_bytes_per_sec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(user_bytes_per_sec / 100 / + (db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + + db->fragsize = 1 << db->fragshift; + db->dma_fragsize = db->fragsize * db->cnt_factor; + db->numfrag = bufs / db->dma_fragsize; + + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->fragsize = 1 << db->fragshift; + db->dma_fragsize = db->fragsize * db->cnt_factor; + db->numfrag = bufs / db->dma_fragsize; + } + + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + + db->dmasize = db->dma_fragsize * db->numfrag; + memset(db->rawbuf, 0, bufs); + +#ifdef AU1000_VERBOSE_DEBUG + dbg("rate=%d, samplesize=%d, channels=%d", + rate, db->sample_size, db->num_channels); + dbg("fragsize=%d, cnt_factor=%d, dma_fragsize=%d", + db->fragsize, db->cnt_factor, db->dma_fragsize); + dbg("numfrag=%d, dmasize=%d", db->numfrag, db->dmasize); +#endif + + db->ready = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct au1000_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc); + +} + +static inline int prog_dmabuf_dac(struct au1000_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac); +} + + +/* hold spinlock for the following */ +static irqreturn_t dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct au1000_state *s = (struct au1000_state *) dev_id; + struct dmabuf *dac = &s->dma_dac; + unsigned long newptr; + u32 ac97c_stat, buff_done; + + ac97c_stat = au_readl(AC97C_STATUS); +#ifdef AU1000_VERBOSE_DEBUG + if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE)) + dbg("AC97C status = 0x%08x", ac97c_stat); +#endif + + if ((buff_done = get_dma_buffer_done(dac->dmanr)) == 0) { + /* fastpath out, to ease interrupt sharing */ + return IRQ_HANDLED; + } + + spin_lock(&s->lock); + + if (buff_done != (DMA_D0 | DMA_D1)) { + dac->nextOut += dac->dma_fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + /* update playback pointers */ + newptr = virt_to_phys(dac->nextOut) + dac->dma_fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; + + dac->count -= dac->dma_fragsize; + dac->total_bytes += dac->dma_fragsize; + + if (dac->count <= 0) { +#ifdef AU1000_VERBOSE_DEBUG + dbg("dac underrun"); +#endif + spin_unlock(&s->lock); + stop_dac(s); + spin_lock(&s->lock); + dac->count = 0; + dac->nextIn = dac->nextOut; + } else if (buff_done == DMA_D0) { + clear_dma_done0(dac->dmanr); // clear DMA done bit + set_dma_count0(dac->dmanr, dac->dma_fragsize>>1); + set_dma_addr0(dac->dmanr, newptr); + enable_dma_buffer0(dac->dmanr); // reenable + } else { + clear_dma_done1(dac->dmanr); // clear DMA done bit + set_dma_count1(dac->dmanr, dac->dma_fragsize>>1); + set_dma_addr1(dac->dmanr, newptr); + enable_dma_buffer1(dac->dmanr); // reenable + } + } else { + // both done bits set, we missed an interrupt + spin_unlock(&s->lock); + stop_dac(s); + spin_lock(&s->lock); + + dac->nextOut += 2*dac->dma_fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + dac->count -= 2*dac->dma_fragsize; + dac->total_bytes += 2*dac->dma_fragsize; + + if (dac->count > 0) { + spin_unlock(&s->lock); + start_dac(s); + spin_lock(&s->lock); + } + } + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up(&dac->wait); + + spin_unlock(&s->lock); + + return IRQ_HANDLED; +} + + +static irqreturn_t adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct au1000_state *s = (struct au1000_state *) dev_id; + struct dmabuf *adc = &s->dma_adc; + unsigned long newptr; + u32 ac97c_stat, buff_done; + + ac97c_stat = au_readl(AC97C_STATUS); +#ifdef AU1000_VERBOSE_DEBUG + if (ac97c_stat & (AC97C_RU | AC97C_RO)) + dbg("AC97C status = 0x%08x", ac97c_stat); +#endif + + if ((buff_done = get_dma_buffer_done(adc->dmanr)) == 0) { + /* fastpath out, to ease interrupt sharing */ + return IRQ_HANDLED; + } + + spin_lock(&s->lock); + + if (buff_done != (DMA_D0 | DMA_D1)) { + if (adc->count + adc->dma_fragsize > adc->dmasize) { + // Overrun. Stop ADC and log the error + spin_unlock(&s->lock); + stop_adc(s); + adc->error++; + err("adc overrun"); + return IRQ_NONE; + } + + adc->nextIn += adc->dma_fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + /* update capture pointers */ + newptr = virt_to_phys(adc->nextIn) + adc->dma_fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; + + adc->count += adc->dma_fragsize; + adc->total_bytes += adc->dma_fragsize; + + if (buff_done == DMA_D0) { + clear_dma_done0(adc->dmanr); // clear DMA done bit + set_dma_count0(adc->dmanr, adc->dma_fragsize>>1); + set_dma_addr0(adc->dmanr, newptr); + enable_dma_buffer0(adc->dmanr); // reenable + } else { + clear_dma_done1(adc->dmanr); // clear DMA done bit + set_dma_count1(adc->dmanr, adc->dma_fragsize>>1); + set_dma_addr1(adc->dmanr, newptr); + enable_dma_buffer1(adc->dmanr); // reenable + } + } else { + // both done bits set, we missed an interrupt + spin_unlock(&s->lock); + stop_adc(s); + spin_lock(&s->lock); + + if (adc->count + 2*adc->dma_fragsize > adc->dmasize) { + // Overrun. Log the error + adc->error++; + err("adc overrun"); + spin_unlock(&s->lock); + return IRQ_NONE; + } + + adc->nextIn += 2*adc->dma_fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + adc->count += 2*adc->dma_fragsize; + adc->total_bytes += 2*adc->dma_fragsize; + + spin_unlock(&s->lock); + start_adc(s); + spin_lock(&s->lock); + } + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up(&adc->wait); + + spin_unlock(&s->lock); + + return IRQ_HANDLED; +} + +/* --------------------------------------------------------------------- */ + +static loff_t au1000_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +static int au1000_open_mixdev(struct inode *inode, struct file *file) +{ + file->private_data = &au1000_state; + return nonseekable_open(inode, file); +} + +static int au1000_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, + unsigned long arg) +{ + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int au1000_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct ac97_codec *codec = &s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const */ struct file_operations au1000_mixer_fops = { + .owner = THIS_MODULE, + .llseek = au1000_llseek, + .ioctl = au1000_ioctl_mixdev, + .open = au1000_open_mixdev, + .release = au1000_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct au1000_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped) + return 0; + + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) + return -EBUSY; + tmo = 1000 * count / (s->no_vra ? + 48000 : s->dma_dac.sample_rate); + tmo /= s->dma_dac.dma_bytes_per_sample; + au1000_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static inline u8 S16_TO_U8(s16 ch) +{ + return (u8) (ch >> 8) + 0x80; +} +static inline s16 U8_TO_S16(u8 ch) +{ + return (s16) (ch - 0x80) << 8; +} + +/* + * Translates user samples to dma buffer suitable for AC'97 DAC data: + * If mono, copy left channel to right channel in dma buffer. + * If 8 bit samples, cvt to 16-bit before writing to dma buffer. + * If interpolating (no VRA), duplicate every audio frame src_factor times. + */ +static int translate_from_user(struct dmabuf *db, + char* dmabuf, + char* userbuf, + int dmacount) +{ + int sample, i; + int interp_bytes_per_sample; + int num_samples; + int mono = (db->num_channels == 1); + char usersample[12]; + s16 ch, dmasample[6]; + + if (db->sample_size == 16 && !mono && db->src_factor == 1) { + // no translation necessary, just copy + if (copy_from_user(dmabuf, userbuf, dmacount)) + return -EFAULT; + return dmacount; + } + + interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; + num_samples = dmacount / interp_bytes_per_sample; + + for (sample = 0; sample < num_samples; sample++) { + if (copy_from_user(usersample, userbuf, + db->user_bytes_per_sample)) { + dbg("%s: fault", __FUNCTION__); + return -EFAULT; + } + + for (i = 0; i < db->num_channels; i++) { + if (db->sample_size == 8) + ch = U8_TO_S16(usersample[i]); + else + ch = *((s16 *) (&usersample[i * 2])); + dmasample[i] = ch; + if (mono) + dmasample[i + 1] = ch; // right channel + } + + // duplicate every audio frame src_factor times + for (i = 0; i < db->src_factor; i++) + memcpy(dmabuf, dmasample, db->dma_bytes_per_sample); + + userbuf += db->user_bytes_per_sample; + dmabuf += interp_bytes_per_sample; + } + + return num_samples * interp_bytes_per_sample; +} + +/* + * Translates AC'97 ADC samples to user buffer: + * If mono, send only left channel to user buffer. + * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer. + * If decimating (no VRA), skip over src_factor audio frames. + */ +static int translate_to_user(struct dmabuf *db, + char* userbuf, + char* dmabuf, + int dmacount) +{ + int sample, i; + int interp_bytes_per_sample; + int num_samples; + int mono = (db->num_channels == 1); + char usersample[12]; + + if (db->sample_size == 16 && !mono && db->src_factor == 1) { + // no translation necessary, just copy + if (copy_to_user(userbuf, dmabuf, dmacount)) + return -EFAULT; + return dmacount; + } + + interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor; + num_samples = dmacount / interp_bytes_per_sample; + + for (sample = 0; sample < num_samples; sample++) { + for (i = 0; i < db->num_channels; i++) { + if (db->sample_size == 8) + usersample[i] = + S16_TO_U8(*((s16 *) (&dmabuf[i * 2]))); + else + *((s16 *) (&usersample[i * 2])) = + *((s16 *) (&dmabuf[i * 2])); + } + + if (copy_to_user(userbuf, usersample, + db->user_bytes_per_sample)) { + dbg("%s: fault", __FUNCTION__); + return -EFAULT; + } + + userbuf += db->user_bytes_per_sample; + dmabuf += interp_bytes_per_sample; + } + + return num_samples * interp_bytes_per_sample; +} + +/* + * Copy audio data to/from user buffer from/to dma buffer, taking care + * that we wrap when reading/writing the dma buffer. Returns actual byte + * count written to or read from the dma buffer. + */ +static int copy_dmabuf_user(struct dmabuf *db, char* userbuf, + int count, int to_user) +{ + char *bufptr = to_user ? db->nextOut : db->nextIn; + char *bufend = db->rawbuf + db->dmasize; + int cnt, ret; + + if (bufptr + count > bufend) { + int partial = (int) (bufend - bufptr); + if (to_user) { + if ((cnt = translate_to_user(db, userbuf, + bufptr, partial)) < 0) + return cnt; + ret = cnt; + if ((cnt = translate_to_user(db, userbuf + partial, + db->rawbuf, + count - partial)) < 0) + return cnt; + ret += cnt; + } else { + if ((cnt = translate_from_user(db, bufptr, userbuf, + partial)) < 0) + return cnt; + ret = cnt; + if ((cnt = translate_from_user(db, db->rawbuf, + userbuf + partial, + count - partial)) < 0) + return cnt; + ret += cnt; + } + } else { + if (to_user) + ret = translate_to_user(db, userbuf, bufptr, count); + else + ret = translate_from_user(db, bufptr, userbuf, count); + } + + return ret; +} + + +static ssize_t au1000_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + int cnt, usercnt, avail; + + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + count *= db->cnt_factor; + + mutex_lock(&s->sem); + add_wait_queue(&db->wait, &wait); + + while (count > 0) { + // wait for samples in ADC dma buffer + do { + if (db->stopped) + start_adc(s); + spin_lock_irqsave(&s->lock, flags); + avail = db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + mutex_unlock(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + mutex_lock(&s->sem); + } + } while (avail <= 0); + + // copy from nextOut to user + if ((cnt = copy_dmabuf_user(db, buffer, + count > avail ? + avail : count, 1)) < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + + count -= cnt; + usercnt = cnt / db->cnt_factor; + buffer += usercnt; + ret += usercnt; + } // while (count > 0) + +out: + mutex_unlock(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t au1000_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + int cnt, usercnt, avail; + +#ifdef AU1000_VERBOSE_DEBUG + dbg("write: count=%d", count); +#endif + + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + count *= db->cnt_factor; + + mutex_lock(&s->sem); + add_wait_queue(&db->wait, &wait); + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = (int) db->dmasize - db->count; + if (avail <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + mutex_unlock(&s->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out2; + } + mutex_lock(&s->sem); + } + } while (avail <= 0); + + // copy from user to nextIn + if ((cnt = copy_dmabuf_user(db, (char *) buffer, + count > avail ? + avail : count, 0)) < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + spin_unlock_irqrestore(&s->lock, flags); + if (db->stopped) + start_dac(s); + + count -= cnt; + usercnt = cnt / db->cnt_factor; + buffer += usercnt; + ret += usercnt; + } // while (count > 0) + +out: + mutex_unlock(&s->sem); +out2: + remove_wait_queue(&db->wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + + +/* No kernel lock - we have our own spinlock */ +static unsigned int au1000_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed)s->dma_dac.dma_fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed) s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.dma_fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int au1000_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + int ret = 0; + + dbg("%s", __FUNCTION__); + + lock_kernel(); + mutex_lock(&s->sem); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + vma->vm_flags &= ~VM_IO; + db->mapped = 1; +out: + mutex_unlock(&s->sem); + unlock_kernel(); + return ret; +} + + +#ifdef AU1000_VERBOSE_DEBUG +static struct ioctl_str_t { + unsigned int cmd; + const char *str; +} ioctl_str[] = { + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} +}; +#endif + +// Need to hold a spin-lock before calling this! +static int dma_count_done(struct dmabuf *db) +{ + if (db->stopped) + return 0; + + return db->dma_fragsize - get_dma_residue(db->dmanr); +} + + +static int au1000_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + +#ifdef AU1000_VERBOSE_DEBUG + for (count=0; countf_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = + s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = + s->dma_adc.rawbuf; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + } + if (s->open_mode & FMODE_READ) + if ((ret = prog_dmabuf_adc(s))) + return ret; + if (s->open_mode & FMODE_WRITE) + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return put_user((file->f_mode & FMODE_READ) ? + s->dma_adc.sample_rate : + s->dma_dac.sample_rate, + (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.num_channels = val ? 2 : 1; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.num_channels = val ? 2 : 1; + if (s->codec_ext_caps & AC97_EXT_DACS) { + // disable surround and center/lfe in AC'97 + u16 ext_stat = rdcodec(&s->codec, + AC97_EXTENDED_STATUS); + wrcodec(&s->codec, AC97_EXTENDED_STATUS, + ext_stat | (AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRJ | + AC97_EXTSTAT_PRK)); + } + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + if (val < 0 || val > 2) + return -EINVAL; + stop_adc(s); + s->dma_adc.num_channels = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + switch (val) { + case 1: + case 2: + break; + case 3: + case 5: + return -EINVAL; + case 4: + if (!(s->codec_ext_caps & + AC97_EXTID_SDAC)) + return -EINVAL; + break; + case 6: + if ((s->codec_ext_caps & + AC97_EXT_DACS) != AC97_EXT_DACS) + return -EINVAL; + break; + default: + return -EINVAL; + } + + stop_dac(s); + if (val <= 2 && + (s->codec_ext_caps & AC97_EXT_DACS)) { + // disable surround and center/lfe + // channels in AC'97 + u16 ext_stat = + rdcodec(&s->codec, + AC97_EXTENDED_STATUS); + wrcodec(&s->codec, + AC97_EXTENDED_STATUS, + ext_stat | (AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRJ | + AC97_EXTSTAT_PRK)); + } else if (val >= 4) { + // enable surround, center/lfe + // channels in AC'97 + u16 ext_stat = + rdcodec(&s->codec, + AC97_EXTENDED_STATUS); + ext_stat &= ~AC97_EXTSTAT_PRJ; + if (val == 6) + ext_stat &= + ~(AC97_EXTSTAT_PRI | + AC97_EXTSTAT_PRK); + wrcodec(&s->codec, + AC97_EXTENDED_STATUS, + ext_stat); + } + + s->dma_dac.num_channels = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */ + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->dma_adc.sample_size = 16; + else { + val = AFMT_U8; + s->dma_adc.sample_size = 8; + } + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->dma_dac.sample_size = 16; + else { + val = AFMT_U8; + s->dma_dac.sample_size = 8; + } + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->dma_adc.sample_size == 16) ? + AFMT_S16_LE : AFMT_U8; + else + val = (s->dma_dac.sample_size == 16) ? + AFMT_S16_LE : AFMT_U8; + } + return put_user(val, (int *) arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + count -= dma_count_done(&s->dma_dac); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = (s->dma_dac.dmasize - count) / + s->dma_dac.cnt_factor; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; +#ifdef AU1000_VERBOSE_DEBUG + dbg("bytes=%d, fragments=%d", abinfo.bytes, abinfo.fragments); +#endif + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + count += dma_count_done(&s->dma_adc); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count / s->dma_adc.cnt_factor; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *) arg, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + count -= dma_count_done(&s->dma_dac); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + count /= s->dma_dac.cnt_factor; + return put_user(count, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = dma_count_done(&s->dma_adc); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff - + s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) - + s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= (s->dma_adc.dma_fragsize-1); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = dma_count_done(&s->dma_dac); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff - + s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) - + s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= (s->dma_dac.dma_fragsize-1); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *) arg); + else + return put_user(s->dma_adc.fragsize, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *) arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->dma_adc.sample_rate : + s->dma_dac.sample_rate, + (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->dma_adc.num_channels, (int *)arg); + else + return put_user(s->dma_dac.num_channels, (int *)arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user(s->dma_adc.sample_size, (int *)arg); + else + return put_user(s->dma_dac.sample_size, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + + return mixdev_ioctl(&s->codec, cmd, arg); +} + + +static int au1000_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + struct au1000_state *s = &au1000_state; + int ret; + +#ifdef AU1000_VERBOSE_DEBUG + if (file->f_flags & O_NONBLOCK) + dbg("%s: non-blocking", __FUNCTION__); + else + dbg("%s: blocking", __FUNCTION__); +#endif + + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + + stop_dac(s); + stop_adc(s); + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->dma_adc.num_channels = 1; + s->dma_adc.sample_size = 8; + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->dma_adc.sample_size = 16; + } + + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->dma_dac.num_channels = 1; + s->dma_dac.sample_size = 8; + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->dma_dac.sample_size = 16; + } + + if (file->f_mode & FMODE_READ) { + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + mutex_unlock(&s->open_mutex); + mutex_init(&s->sem); + return nonseekable_open(inode, file); +} + +static int au1000_release(struct inode *inode, struct file *file) +{ + struct au1000_state *s = (struct au1000_state *)file->private_data; + + lock_kernel(); + + if (file->f_mode & FMODE_WRITE) { + unlock_kernel(); + drain_dac(s, file->f_flags & O_NONBLOCK); + lock_kernel(); + } + + mutex_lock(&s->open_mutex); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE)); + mutex_unlock(&s->open_mutex); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const */ struct file_operations au1000_audio_fops = { + .owner = THIS_MODULE, + .llseek = au1000_llseek, + .read = au1000_read, + .write = au1000_write, + .poll = au1000_poll, + .ioctl = au1000_ioctl, + .mmap = au1000_mmap, + .open = au1000_open, + .release = au1000_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef AU1000_DEBUG +static int proc_au1000_dump(char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct au1000_state *s = &au1000_state; + int cnt, len = 0; + + /* print out header */ + len += sprintf(buf + len, "\n\t\tAU1000 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf(buf + len, "AU1000 Audio Controller registers\n"); + len += sprintf(buf + len, "---------------------------------\n"); + len += sprintf (buf + len, "AC97C_CONFIG = %08x\n", + au_readl(AC97C_CONFIG)); + len += sprintf (buf + len, "AC97C_STATUS = %08x\n", + au_readl(AC97C_STATUS)); + len += sprintf (buf + len, "AC97C_CNTRL = %08x\n", + au_readl(AC97C_CNTRL)); + + /* print out CODEC state */ + len += sprintf(buf + len, "\nAC97 CODEC registers\n"); + len += sprintf(buf + len, "----------------------\n"); + for (cnt = 0; cnt <= 0x7e; cnt += 2) + len += sprintf(buf + len, "reg %02x = %04x\n", + cnt, rdcodec(&s->codec, cnt)); + + if (fpos >= len) { + *start = buf; + *eof = 1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof = 1; + return len; + +} +#endif /* AU1000_DEBUG */ + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); +MODULE_DESCRIPTION("Au1000 Audio Driver"); + +/* --------------------------------------------------------------------- */ + +static int __devinit au1000_probe(void) +{ + struct au1000_state *s = &au1000_state; + int val; +#ifdef AU1000_DEBUG + char proc_str[80]; +#endif + + memset(s, 0, sizeof(struct au1000_state)); + + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + mutex_init(&s->open_mutex); + spin_lock_init(&s->lock); + s->codec.private_data = s; + s->codec.id = 0; + s->codec.codec_read = rdcodec; + s->codec.codec_write = wrcodec; + s->codec.codec_wait = waitcodec; + + if (!request_mem_region(CPHYSADDR(AC97C_CONFIG), + 0x14, AU1000_MODULE_NAME)) { + err("AC'97 ports in use"); + return -1; + } + // Allocate the DMA Channels + if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX, + "audio DAC", + dac_dma_interrupt, + IRQF_DISABLED, s)) < 0) { + err("Can't get DAC DMA"); + goto err_dma1; + } + if ((s->dma_adc.dmanr = request_au1000_dma(DMA_ID_AC97C_RX, + "audio ADC", + adc_dma_interrupt, + IRQF_DISABLED, s)) < 0) { + err("Can't get ADC DMA"); + goto err_dma2; + } + + info("DAC: DMA%d/IRQ%d, ADC: DMA%d/IRQ%d", + s->dma_dac.dmanr, get_dma_done_irq(s->dma_dac.dmanr), + s->dma_adc.dmanr, get_dma_done_irq(s->dma_adc.dmanr)); + + // enable DMA coherency in read/write DMA channels + set_dma_mode(s->dma_dac.dmanr, + get_dma_mode(s->dma_dac.dmanr) & ~DMA_NC); + set_dma_mode(s->dma_adc.dmanr, + get_dma_mode(s->dma_adc.dmanr) & ~DMA_NC); + + /* register devices */ + + if ((s->dev_audio = register_sound_dsp(&au1000_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec.dev_mixer = + register_sound_mixer(&au1000_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef AU1000_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(AU1000_MODULE_NAME, 0, NULL, + proc_au1000_dump, NULL); +#endif /* AU1000_DEBUG */ + + // configure pins for AC'97 + au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); + + // Assert reset for 10msec to the AC'97 controller, and enable clock + au_writel(AC97C_RS | AC97C_CE, AC97C_CNTRL); + au1000_delay(10); + au_writel(AC97C_CE, AC97C_CNTRL); + au1000_delay(10); // wait for clock to stabilize + + /* cold reset the AC'97 */ + au_writel(AC97C_RESET, AC97C_CONFIG); + au1000_delay(10); + au_writel(0, AC97C_CONFIG); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + au1000_delay(500); + + /* warm reset the AC'97 to start the bitclk */ + au_writel(AC97C_SG | AC97C_SYNC, AC97C_CONFIG); + udelay(100); + au_writel(0, AC97C_CONFIG); + + /* codec init */ + if (!ac97_probe_codec(&s->codec)) + goto err_dev3; + + s->codec_base_caps = rdcodec(&s->codec, AC97_RESET); + s->codec_ext_caps = rdcodec(&s->codec, AC97_EXTENDED_ID); + info("AC'97 Base/Extended ID = %04x/%04x", + s->codec_base_caps, s->codec_ext_caps); + + /* + * On the Pb1000, audio playback is on the AUX_OUT + * channel (which defaults to LNLVL_OUT in AC'97 + * rev 2.2) so make sure this channel is listed + * as supported (soundcard.h calls this channel + * ALTPCM). ac97_codec.c does not handle detection + * of this channel correctly. + */ + s->codec.supported_mixers |= SOUND_MASK_ALTPCM; + /* + * Now set AUX_OUT's default volume. + */ + val = 0x4343; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_ALTPCM, + (unsigned long) &val); + + if (!(s->codec_ext_caps & AC97_EXTID_VRA)) { + // codec does not support VRA + s->no_vra = 1; + } else if (!vra) { + // Boot option says disable VRA + u16 ac97_extstat = rdcodec(&s->codec, AC97_EXTENDED_STATUS); + wrcodec(&s->codec, AC97_EXTENDED_STATUS, + ac97_extstat & ~AC97_EXTSTAT_VRA); + s->no_vra = 1; + } + if (s->no_vra) + info("no VRA, interpolating and decimating"); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, + (unsigned long) &val); + +#ifdef AU1000_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", AU1000_MODULE_NAME, + s->codec.id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, &s->codec); +#endif + +#ifdef CONFIG_MIPS_XXS1500 + /* deassert eapd */ + wrcodec(&s->codec, AC97_POWER_CONTROL, + rdcodec(&s->codec, AC97_POWER_CONTROL) & ~0x8000); + /* mute a number of signals which seem to be causing problems + * if not muted. + */ + wrcodec(&s->codec, AC97_PCBEEP_VOL, 0x8000); + wrcodec(&s->codec, AC97_PHONE_VOL, 0x8008); + wrcodec(&s->codec, AC97_MIC_VOL, 0x8008); + wrcodec(&s->codec, AC97_LINEIN_VOL, 0x8808); + wrcodec(&s->codec, AC97_CD_VOL, 0x8808); + wrcodec(&s->codec, AC97_VIDEO_VOL, 0x8808); + wrcodec(&s->codec, AC97_AUX_VOL, 0x8808); + wrcodec(&s->codec, AC97_PCMOUT_VOL, 0x0808); + wrcodec(&s->codec, AC97_GENERAL_PURPOSE, 0x2000); +#endif + + return 0; + + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_au1000_dma(s->dma_adc.dmanr); + err_dma2: + free_au1000_dma(s->dma_dac.dmanr); + err_dma1: + release_mem_region(CPHYSADDR(AC97C_CONFIG), 0x14); + return -1; +} + +static void au1000_remove(void) +{ + struct au1000_state *s = &au1000_state; + + if (!s) + return; +#ifdef AU1000_DEBUG + if (s->ps) + remove_proc_entry(AU1000_MODULE_NAME, NULL); +#endif /* AU1000_DEBUG */ + synchronize_irq(); + free_au1000_dma(s->dma_adc.dmanr); + free_au1000_dma(s->dma_dac.dmanr); + release_mem_region(CPHYSADDR(AC97C_CONFIG), 0x14); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec.dev_mixer); +} + +static int __init init_au1000(void) +{ + info("stevel@mvista.com, built " __TIME__ " on " __DATE__); + return au1000_probe(); +} + +static void __exit cleanup_au1000(void) +{ + info("unloading"); + au1000_remove(); +} + +module_init(init_au1000); +module_exit(cleanup_au1000); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +static int __init au1000_setup(char *options) +{ + char *this_opt; + + if (!options || !*options) + return 0; + + while ((this_opt = strsep(&options, ","))) { + if (!*this_opt) + continue; + if (!strncmp(this_opt, "vra", 3)) { + vra = 1; + } + } + + return 1; +} + +__setup("au1000_audio=", au1000_setup); + +#endif /* MODULE */ diff --git a/trunk/sound/oss/audio_syms.c b/trunk/sound/oss/audio_syms.c new file mode 100644 index 000000000000..5da217fcbedd --- /dev/null +++ b/trunk/sound/oss/audio_syms.c @@ -0,0 +1,16 @@ +/* + * Exported symbols for audio driver. + */ + +#include + +char audio_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(DMAbuf_start_dma); +EXPORT_SYMBOL(DMAbuf_open_dma); +EXPORT_SYMBOL(DMAbuf_close_dma); +EXPORT_SYMBOL(DMAbuf_inputintr); +EXPORT_SYMBOL(DMAbuf_outputintr); diff --git a/trunk/sound/oss/awe_hw.h b/trunk/sound/oss/awe_hw.h new file mode 100644 index 000000000000..ab00c3c67e4e --- /dev/null +++ b/trunk/sound/oss/awe_hw.h @@ -0,0 +1,99 @@ +/* + * sound/oss/awe_hw.h + * + * Access routines and definitions for the low level driver for the + * Creative AWE32/SB32/AWE64 wave table synth. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AWE_HW_H_DEF +#define AWE_HW_H_DEF + +/* + * Emu-8000 control registers + * name(channel) reg, port + */ + +#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch)) + +#define Data0 0 /* 0x620: doubleword r/w */ +#define Data1 1 /* 0xA20: doubleword r/w */ +#define Data2 2 /* 0xA22: word r/w */ +#define Data3 3 /* 0xE20: word r/w */ +#define Pointer 4 /* 0xE22 register pointer r/w */ + +#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */ +#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */ +#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */ +#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */ +#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */ +#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */ +#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */ +#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */ +#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */ +#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */ +#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */ +#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */ +#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */ +#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */ +#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */ +#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */ +#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */ +#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */ +#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */ +#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */ +#define AWE_WC_Cmd awe_cmd_idx(1,27) +#define AWE_WC_Port Data2 +#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */ +#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */ +#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */ +#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */ +#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */ +#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */ +#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */ +#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */ +#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */ +#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */ +#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */ +#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */ +#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */ +#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */ +#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */ +#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */ +#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */ +#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */ +#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */ +#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */ +#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */ + +/* used during detection (returns ROM version?; not documented in ADIP) */ +#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */ +#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */ + + +#define AWE_MAX_VOICES 32 +#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/ + +#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */ +#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */ + +#define AWE_DRAM_OFFSET 0x200000 +#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */ + +#endif diff --git a/trunk/sound/oss/awe_wave.c b/trunk/sound/oss/awe_wave.c new file mode 100644 index 000000000000..1b968f7ecb3f --- /dev/null +++ b/trunk/sound/oss/awe_wave.c @@ -0,0 +1,6148 @@ +/* + * sound/oss/awe_wave.c + * + * The low level driver for the AWE32/SB32/AWE64 wave table synth. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-2000 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Changelog: + * Aug 18, 2003, Adam Belay + * - detection code rewrite + */ + +#include +#include +#include +#include +#include + +#include "sound_config.h" + +#include "awe_wave.h" +#include "awe_hw.h" + +#ifdef AWE_HAS_GUS_COMPATIBILITY +#include "tuning.h" +#include +#endif + +/* + * debug message + */ + +#ifdef AWE_DEBUG_ON +#define DEBUG(LVL,XXX) {if (ctrls[AWE_MD_DEBUG_MODE] > LVL) { XXX; }} +#define ERRMSG(XXX) {if (ctrls[AWE_MD_DEBUG_MODE]) { XXX; }} +#define FATALERR(XXX) XXX +#else +#define DEBUG(LVL,XXX) /**/ +#define ERRMSG(XXX) XXX +#define FATALERR(XXX) XXX +#endif + +/* + * bank and voice record + */ + +typedef struct _sf_list sf_list; +typedef struct _awe_voice_list awe_voice_list; +typedef struct _awe_sample_list awe_sample_list; + +/* soundfont record */ +struct _sf_list { + unsigned short sf_id; /* id number */ + unsigned short type; /* lock & shared flags */ + int num_info; /* current info table index */ + int num_sample; /* current sample table index */ + int mem_ptr; /* current word byte pointer */ + awe_voice_list *infos, *last_infos; /* instruments */ + awe_sample_list *samples, *last_samples; /* samples */ +#ifdef AWE_ALLOW_SAMPLE_SHARING + sf_list *shared; /* shared list */ + unsigned char name[AWE_PATCH_NAME_LEN]; /* sharing id */ +#endif + sf_list *next, *prev; +}; + +/* instrument list */ +struct _awe_voice_list { + awe_voice_info v; /* instrument information */ + sf_list *holder; /* parent sf_list of this record */ + unsigned char bank, instr; /* preset number information */ + char type, disabled; /* type=normal/mapped, disabled=boolean */ + awe_voice_list *next; /* linked list with same sf_id */ + awe_voice_list *next_instr; /* instrument list */ + awe_voice_list *next_bank; /* hash table list */ +}; + +/* voice list type */ +#define V_ST_NORMAL 0 +#define V_ST_MAPPED 1 + +/* sample list */ +struct _awe_sample_list { + awe_sample_info v; /* sample information */ + sf_list *holder; /* parent sf_list of this record */ + awe_sample_list *next; /* linked list with same sf_id */ +}; + +/* sample and information table */ +static int current_sf_id; /* current number of fonts */ +static int locked_sf_id; /* locked position */ +static sf_list *sfhead, *sftail; /* linked-lists */ + +#define awe_free_mem_ptr() (sftail ? sftail->mem_ptr : 0) +#define awe_free_info() (sftail ? sftail->num_info : 0) +#define awe_free_sample() (sftail ? sftail->num_sample : 0) + +#define AWE_MAX_PRESETS 256 +#define AWE_DEFAULT_PRESET 0 +#define AWE_DEFAULT_BANK 0 +#define AWE_DEFAULT_DRUM 0 +#define AWE_DRUM_BANK 128 + +#define MAX_LAYERS AWE_MAX_VOICES + +/* preset table index */ +static awe_voice_list *preset_table[AWE_MAX_PRESETS]; + +/* + * voice table + */ + +/* effects table */ +typedef struct FX_Rec { /* channel effects */ + unsigned char flags[AWE_FX_END]; + short val[AWE_FX_END]; +} FX_Rec; + + +/* channel parameters */ +typedef struct _awe_chan_info { + int channel; /* channel number */ + int bank; /* current tone bank */ + int instr; /* current program */ + int bender; /* midi pitchbend (-8192 - 8192) */ + int bender_range; /* midi bender range (x100) */ + int panning; /* panning (0-127) */ + int main_vol; /* channel volume (0-127) */ + int expression_vol; /* midi expression (0-127) */ + int chan_press; /* channel pressure */ + int sustained; /* sustain status in MIDI */ + FX_Rec fx; /* effects */ + FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ +} awe_chan_info; + +/* voice parameters */ +typedef struct _voice_info { + int state; +#define AWE_ST_OFF (1<<0) /* no sound */ +#define AWE_ST_ON (1<<1) /* playing */ +#define AWE_ST_STANDBY (1<<2) /* stand by for playing */ +#define AWE_ST_SUSTAINED (1<<3) /* sustained */ +#define AWE_ST_MARK (1<<4) /* marked for allocation */ +#define AWE_ST_DRAM (1<<5) /* DRAM read/write */ +#define AWE_ST_FM (1<<6) /* reserved for FM */ +#define AWE_ST_RELEASED (1<<7) /* released */ + + int ch; /* midi channel */ + int key; /* internal key for search */ + int layer; /* layer number (for channel mode only) */ + int time; /* allocated time */ + awe_chan_info *cinfo; /* channel info */ + + int note; /* midi key (0-127) */ + int velocity; /* midi velocity (0-127) */ + int sostenuto; /* sostenuto on/off */ + awe_voice_info *sample; /* assigned voice */ + + /* EMU8000 parameters */ + int apitch; /* pitch parameter */ + int avol; /* volume parameter */ + int apan; /* panning parameter */ + int acutoff; /* cutoff parameter */ + short aaux; /* aux word */ +} voice_info; + +/* voice information */ +static voice_info voices[AWE_MAX_VOICES]; + +#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED)) +#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON) +#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED)) +#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM)) + + +/* MIDI channel effects information (for hw control) */ +static awe_chan_info channels[AWE_MAX_CHANNELS]; + + +/* + * global variables + */ + +#ifndef AWE_DEFAULT_BASE_ADDR +#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ +#endif + +#ifndef AWE_DEFAULT_MEM_SIZE +#define AWE_DEFAULT_MEM_SIZE -1 /* autodetect */ +#endif + +static int io = AWE_DEFAULT_BASE_ADDR; /* Emu8000 base address */ +static int memsize = AWE_DEFAULT_MEM_SIZE; /* memory size in Kbytes */ +#ifdef CONFIG_PNP +static int isapnp = -1; +#else +static int isapnp; +#endif + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("SB AWE32/64 WaveTable driver"); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "base i/o port of Emu8000"); +module_param(memsize, int, 0); +MODULE_PARM_DESC(memsize, "onboard DRAM size in Kbytes"); +module_param(isapnp, bool, 0); +MODULE_PARM_DESC(isapnp, "use ISAPnP detection"); + +/* DRAM start offset */ +static int awe_mem_start = AWE_DRAM_OFFSET; + +/* maximum channels for playing */ +static int awe_max_voices = AWE_MAX_VOICES; + +static int patch_opened; /* sample already loaded? */ + +static char atten_relative = FALSE; +static short atten_offset; + +static int awe_present = FALSE; /* awe device present? */ +static int awe_busy = FALSE; /* awe device opened? */ + +static int my_dev = -1; + +#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25)) +#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c))) +#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c))) +#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c))) +static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ + +static int playing_mode = AWE_PLAY_INDIRECT; +#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT) +#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2) + +static int current_alloc_time; /* voice allocation index for channel mode */ + +static struct synth_info awe_info = { + "AWE32 Synth", /* name */ + 0, /* device */ + SYNTH_TYPE_SAMPLE, /* synth_type */ + SAMPLE_TYPE_AWE32, /* synth_subtype */ + 0, /* perc_mode (obsolete) */ + AWE_MAX_VOICES, /* nr_voices */ + 0, /* nr_drums (obsolete) */ + 400 /* instr_bank_size */ +}; + + +static struct voice_alloc_info *voice_alloc; /* set at initialization */ + + +/* + * function prototypes + */ + +static int awe_request_region(void); +static void awe_release_region(void); + +static void awe_reset_samples(void); +/* emu8000 chip i/o access */ +static void setup_ports(int p1, int p2, int p3); +static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data); +static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data); +static unsigned short awe_peek(unsigned short cmd, unsigned short port); +static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port); +static void awe_wait(unsigned short delay); + +/* initialize emu8000 chip */ +static void awe_initialize(void); + +/* set voice parameters */ +static void awe_init_ctrl_parms(int init_all); +static void awe_init_voice_info(awe_voice_info *vp); +static void awe_init_voice_parm(awe_voice_parm *pp); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int freq_to_note(int freq); +static int calc_rate_offset(int Hz); +/*static int calc_parm_delay(int msec);*/ +static int calc_parm_hold(int msec); +static int calc_parm_attack(int msec); +static int calc_parm_decay(int msec); +static int calc_parm_search(int msec, short *table); +#endif /* gus compat */ + +/* turn on/off note */ +static void awe_note_on(int voice); +static void awe_note_off(int voice); +static void awe_terminate(int voice); +static void awe_exclusive_off(int voice); +static void awe_note_off_all(int do_sustain); + +/* calculate voice parameters */ +typedef void (*fx_affect_func)(int voice, int forced); +static void awe_set_pitch(int voice, int forced); +static void awe_set_voice_pitch(int voice, int forced); +static void awe_set_volume(int voice, int forced); +static void awe_set_voice_vol(int voice, int forced); +static void awe_set_pan(int voice, int forced); +static void awe_fx_fmmod(int voice, int forced); +static void awe_fx_tremfrq(int voice, int forced); +static void awe_fx_fm2frq2(int voice, int forced); +static void awe_fx_filterQ(int voice, int forced); +static void awe_calc_pitch(int voice); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_calc_pitch_from_freq(int voice, int freq); +#endif +static void awe_calc_volume(int voice); +static void awe_update_volume(void); +static void awe_change_master_volume(short val); +static void awe_voice_init(int voice, int init_all); +static void awe_channel_init(int ch, int init_all); +static void awe_fx_init(int ch); +static void awe_send_effect(int voice, int layer, int type, int val); +static void awe_modwheel_change(int voice, int value); + +/* sequencer interface */ +static int awe_open(int dev, int mode); +static void awe_close(int dev); +static int awe_ioctl(int dev, unsigned int cmd, void __user * arg); +static int awe_kill_note(int dev, int voice, int note, int velocity); +static int awe_start_note(int dev, int v, int note_num, int volume); +static int awe_set_instr(int dev, int voice, int instr_no); +static int awe_set_instr_2(int dev, int voice, int instr_no); +static void awe_reset(int dev); +static void awe_hw_control(int dev, unsigned char *event); +static int awe_load_patch(int dev, int format, const char __user *addr, + int offs, int count, int pmgr_flag); +static void awe_aftertouch(int dev, int voice, int pressure); +static void awe_controller(int dev, int voice, int ctrl_num, int value); +static void awe_panning(int dev, int voice, int value); +static void awe_volume_method(int dev, int mode); +static void awe_bender(int dev, int voice, int value); +static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc); +static void awe_setup_voice(int dev, int voice, int chn); + +#define awe_key_pressure(dev,voice,key,press) awe_start_note(dev,voice,(key)+128,press) + +/* hardware controls */ +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_hw_gus_control(int dev, int cmd, unsigned char *event); +#endif +static void awe_hw_awe_control(int dev, int cmd, unsigned char *event); +static void awe_voice_change(int voice, fx_affect_func func); +static void awe_sostenuto_on(int voice, int forced); +static void awe_sustain_off(int voice, int forced); +static void awe_terminate_and_init(int voice, int forced); + +/* voice search */ +static int awe_search_key(int bank, int preset, int note); +static awe_voice_list *awe_search_instr(int bank, int preset, int note); +static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist); +static void awe_alloc_multi_voices(int ch, int note, int velocity, int key); +static void awe_alloc_one_voice(int voice, int note, int velocity); +static int awe_clear_voice(void); + +/* load / remove patches */ +static int awe_open_patch(awe_patch_info *patch, const char __user *addr, int count); +static int awe_close_patch(awe_patch_info *patch, const char __user *addr, int count); +static int awe_unload_patch(awe_patch_info *patch, const char __user *addr, int count); +static int awe_load_info(awe_patch_info *patch, const char __user *addr, int count); +static int awe_remove_info(awe_patch_info *patch, const char __user *addr, int count); +static int awe_load_data(awe_patch_info *patch, const char __user *addr, int count); +static int awe_replace_data(awe_patch_info *patch, const char __user *addr, int count); +static int awe_load_map(awe_patch_info *patch, const char __user *addr, int count); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int awe_load_guspatch(const char __user *addr, int offs, int size, int pmgr_flag); +#endif +/*static int awe_probe_info(awe_patch_info *patch, const char __user *addr, int count);*/ +static int awe_probe_data(awe_patch_info *patch, const char __user *addr, int count); +static sf_list *check_patch_opened(int type, char *name); +static int awe_write_wave_data(const char __user *addr, int offset, awe_sample_list *sp, int channels); +static int awe_create_sf(int type, char *name); +static void awe_free_sf(sf_list *sf); +static void add_sf_info(sf_list *sf, awe_voice_list *rec); +static void add_sf_sample(sf_list *sf, awe_sample_list *smp); +static void purge_old_list(awe_voice_list *rec, awe_voice_list *next); +static void add_info_list(awe_voice_list *rec); +static void awe_remove_samples(int sf_id); +static void rebuild_preset_list(void); +static short awe_set_sample(awe_voice_list *rec); +static awe_sample_list *search_sample_index(sf_list *sf, int sample); + +static int is_identical_holder(sf_list *sf1, sf_list *sf2); +#ifdef AWE_ALLOW_SAMPLE_SHARING +static int is_identical_name(unsigned char *name, sf_list *p); +static int is_shared_sf(unsigned char *name); +static int info_duplicated(sf_list *sf, awe_voice_list *rec); +#endif /* allow sharing */ + +/* lowlevel functions */ +static void awe_init_audio(void); +static void awe_init_dma(void); +static void awe_init_array(void); +static void awe_send_array(unsigned short *data); +static void awe_tweak_voice(int voice); +static void awe_tweak(void); +static void awe_init_fm(void); +static int awe_open_dram_for_write(int offset, int channels); +static void awe_open_dram_for_check(void); +static void awe_close_dram(void); +/*static void awe_write_dram(unsigned short c);*/ +static int awe_detect_base(int addr); +static int awe_detect(void); +static void awe_check_dram(void); +static int awe_load_chorus_fx(awe_patch_info *patch, const char __user *addr, int count); +static void awe_set_chorus_mode(int mode); +static void awe_update_chorus_mode(void); +static int awe_load_reverb_fx(awe_patch_info *patch, const char __user *addr, int count); +static void awe_set_reverb_mode(int mode); +static void awe_update_reverb_mode(void); +static void awe_equalizer(int bass, int treble); +static void awe_update_equalizer(void); + +#ifdef CONFIG_AWE32_MIXER +static void attach_mixer(void); +static void unload_mixer(void); +#endif + +#ifdef CONFIG_AWE32_MIDIEMU +static void attach_midiemu(void); +static void unload_midiemu(void); +#endif + +#define limitvalue(x, a, b) if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b) + +/* + * control parameters + */ + + +#ifdef AWE_USE_NEW_VOLUME_CALC +#define DEF_VOLUME_CALC TRUE +#else +#define DEF_VOLUME_CALC FALSE +#endif /* new volume */ + +#define DEF_ZERO_ATTEN 32 /* 12dB below */ +#define DEF_MOD_SENSE 18 +#define DEF_CHORUS_MODE 2 +#define DEF_REVERB_MODE 4 +#define DEF_BASS_LEVEL 5 +#define DEF_TREBLE_LEVEL 9 + +static struct CtrlParmsDef { + int value; + int init_each_time; + void (*update)(void); +} ctrl_parms[AWE_MD_END] = { + {0,0, NULL}, {0,0, NULL}, /* <-- not used */ + {AWE_VERSION_NUMBER, FALSE, NULL}, + {TRUE, FALSE, NULL}, /* exclusive */ + {TRUE, FALSE, NULL}, /* realpan */ + {AWE_DEFAULT_BANK, FALSE, NULL}, /* gusbank */ + {FALSE, TRUE, NULL}, /* keep effect */ + {DEF_ZERO_ATTEN, FALSE, awe_update_volume}, /* zero_atten */ + {FALSE, FALSE, NULL}, /* chn_prior */ + {DEF_MOD_SENSE, FALSE, NULL}, /* modwheel sense */ + {AWE_DEFAULT_PRESET, FALSE, NULL}, /* def_preset */ + {AWE_DEFAULT_BANK, FALSE, NULL}, /* def_bank */ + {AWE_DEFAULT_DRUM, FALSE, NULL}, /* def_drum */ + {FALSE, FALSE, NULL}, /* toggle_drum_bank */ + {DEF_VOLUME_CALC, FALSE, awe_update_volume}, /* new_volume_calc */ + {DEF_CHORUS_MODE, FALSE, awe_update_chorus_mode}, /* chorus mode */ + {DEF_REVERB_MODE, FALSE, awe_update_reverb_mode}, /* reverb mode */ + {DEF_BASS_LEVEL, FALSE, awe_update_equalizer}, /* bass level */ + {DEF_TREBLE_LEVEL, FALSE, awe_update_equalizer}, /* treble level */ + {0, FALSE, NULL}, /* debug mode */ + {FALSE, FALSE, NULL}, /* pan exchange */ +}; + +static int ctrls[AWE_MD_END]; + + +/* + * synth operation table + */ + +static struct synth_operations awe_operations = +{ + .owner = THIS_MODULE, + .id = "EMU8K", + .info = &awe_info, + .midi_dev = 0, + .synth_type = SYNTH_TYPE_SAMPLE, + .synth_subtype = SAMPLE_TYPE_AWE32, + .open = awe_open, + .close = awe_close, + .ioctl = awe_ioctl, + .kill_note = awe_kill_note, + .start_note = awe_start_note, + .set_instr = awe_set_instr_2, + .reset = awe_reset, + .hw_control = awe_hw_control, + .load_patch = awe_load_patch, + .aftertouch = awe_aftertouch, + .controller = awe_controller, + .panning = awe_panning, + .volume_method = awe_volume_method, + .bender = awe_bender, + .alloc_voice = awe_alloc, + .setup_voice = awe_setup_voice +}; + +static void free_tables(void) +{ + if (sftail) { + sf_list *p, *prev; + for (p = sftail; p; p = prev) { + prev = p->prev; + awe_free_sf(p); + } + } + sfhead = sftail = NULL; +} + +/* + * clear sample tables + */ + +static void +awe_reset_samples(void) +{ + /* free all bank tables */ + memset(preset_table, 0, sizeof(preset_table)); + free_tables(); + + current_sf_id = 0; + locked_sf_id = 0; + patch_opened = 0; +} + + +/* + * EMU register access + */ + +/* select a given AWE32 pointer */ +static int awe_ports[5]; +static int port_setuped = FALSE; +static int awe_cur_cmd = -1; +#define awe_set_cmd(cmd) \ +if (awe_cur_cmd != cmd) { outw(cmd, awe_ports[Pointer]); awe_cur_cmd = cmd; } + +/* write 16bit data */ +static void +awe_poke(unsigned short cmd, unsigned short port, unsigned short data) +{ + awe_set_cmd(cmd); + outw(data, awe_ports[port]); +} + +/* write 32bit data */ +static void +awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) +{ + unsigned short addr = awe_ports[port]; + awe_set_cmd(cmd); + outw(data, addr); /* write lower 16 bits */ + outw(data >> 16, addr + 2); /* write higher 16 bits */ +} + +/* read 16bit data */ +static unsigned short +awe_peek(unsigned short cmd, unsigned short port) +{ + unsigned short k; + awe_set_cmd(cmd); + k = inw(awe_ports[port]); + return k; +} + +/* read 32bit data */ +static unsigned int +awe_peek_dw(unsigned short cmd, unsigned short port) +{ + unsigned int k1, k2; + unsigned short addr = awe_ports[port]; + awe_set_cmd(cmd); + k1 = inw(addr); + k2 = inw(addr + 2); + k1 |= k2 << 16; + return k1; +} + +/* wait delay number of AWE32 44100Hz clocks */ +#ifdef WAIT_BY_LOOP /* wait by loop -- that's not good.. */ +static void +awe_wait(unsigned short delay) +{ + unsigned short clock, target; + unsigned short port = awe_ports[AWE_WC_Port]; + int counter; + + /* sample counter */ + awe_set_cmd(AWE_WC_Cmd); + clock = (unsigned short)inw(port); + target = clock + delay; + counter = 0; + if (target < clock) { + for (; (unsigned short)inw(port) > target; counter++) + if (counter > 65536) + break; + } + for (; (unsigned short)inw(port) < target; counter++) + if (counter > 65536) + break; +} +#else + +static void awe_wait(unsigned short delay) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((HZ*(unsigned long)delay + 44099)/44100); +} +/* +static void awe_wait(unsigned short delay) +{ + udelay(((unsigned long)delay * 1000000L + 44099) / 44100); +} +*/ +#endif /* wait by loop */ + +/* write a word data */ +#define awe_write_dram(c) awe_poke(AWE_SMLD, c) + +/* + * AWE32 voice parameters + */ + +/* initialize voice_info record */ +static void +awe_init_voice_info(awe_voice_info *vp) +{ + vp->sample = 0; + vp->rate_offset = 0; + + vp->start = 0; + vp->end = 0; + vp->loopstart = 0; + vp->loopend = 0; + vp->mode = 0; + vp->root = 60; + vp->tune = 0; + vp->low = 0; + vp->high = 127; + vp->vellow = 0; + vp->velhigh = 127; + + vp->fixkey = -1; + vp->fixvel = -1; + vp->fixpan = -1; + vp->pan = -1; + + vp->exclusiveClass = 0; + vp->amplitude = 127; + vp->attenuation = 0; + vp->scaleTuning = 100; + + awe_init_voice_parm(&vp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +awe_init_voice_parm(awe_voice_parm *pp) +{ + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + pp->modkeyhold = 0; + pp->modkeydecay = 0; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + pp->volkeyhold = 0; + pp->volkeydecay = 0; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + pp->pefe = 0; + + pp->fmmod = 0; + pp->tremfrq = 0; + pp->fm2frq2 = 0; + + pp->cutoff = 0xff; + pp->filterQ = 0; + + pp->chorus = 0; + pp->reverb = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* convert frequency mHz to abstract cents (= midi key * 100) */ +static int +freq_to_note(int mHz) +{ + /* abscents = log(mHz/8176) / log(2) * 1200 */ + unsigned int max_val = (unsigned int)0xffffffff / 10000; + int i, times; + unsigned int base; + unsigned int freq; + int note, tune; + + if (mHz == 0) + return 0; + if (mHz < 0) + return 12799; /* maximum */ + + freq = mHz; + note = 0; + for (base = 8176 * 2; freq >= base; base *= 2) { + note += 12; + if (note >= 128) /* over maximum */ + return 12799; + } + base /= 2; + + /* to avoid overflow... */ + times = 10000; + while (freq > max_val) { + max_val *= 10; + times /= 10; + base /= 10; + } + + freq = freq * times / base; + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + note++; + } + + tune = 0; + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + tune++; + } + + return note * 100 + tune; +} + + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + */ +static int +calc_rate_offset(int Hz) +{ + /* offset = log(Hz / 44100) / log(2) * 4096 */ + int freq, base, i; + + /* maybe smaller than max (44100Hz) */ + if (Hz <= 0 || Hz >= 44100) return 0; + + base = 0; + for (freq = Hz * 2; freq < 44100; freq *= 2) + base++; + base *= 1200; + + freq = 44100 * 10000 / (freq/2); + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + base += 100; + } + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + base++; + } + return -base * 4096 / 1200; +} + + +/* + * convert envelope time parameter to AWE32 raw parameter + */ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, +707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, +361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, +180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, +90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, +45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, +22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, +11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, +2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, +1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, +691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, +345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, +172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, +86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, +43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, +}; + +#define calc_parm_delay(msec) (0x8000 - (msec) * 1000 / 725); + +/* delay time = 0x8000 - msec/92 */ +static int +calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val > 127) val = 127; + return val; +} + +/* attack time: search from time table */ +static int +calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +static int +calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/* + * effects table + */ + +/* set an effect value */ +#define FX_FLAG_OFF 0 +#define FX_FLAG_SET 1 +#define FX_FLAG_ADD 2 + +#define FX_SET(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value)) +#define FX_ADD(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value)) +#define FX_UNSET(rec,type) \ + ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0) + +/* check the effect value is set */ +#define FX_ON(rec,type) ((rec)->flags[type]) + +#define PARM_BYTE 0 +#define PARM_WORD 1 +#define PARM_SIGN 2 + +static struct PARM_DEFS { + int type; /* byte or word */ + int low, high; /* value range */ + fx_affect_func realtime; /* realtime paramater change */ +} parm_defs[] = { + {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */ + {PARM_SIGN, -128, 127, awe_fx_tremfrq}, /* lfo1 volume */ + {PARM_SIGN, -128, 127, awe_fx_fmmod}, /* lfo1 pitch */ + {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */ + {PARM_SIGN, -128, 127, awe_fx_fm2frq2}, /* lfo2 pitch */ + + {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* chorus */ + {PARM_BYTE, 0, 0xff, NULL}, /* reverb */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */ + {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */ + + {PARM_WORD, 0, 0xffff, NULL}, /* sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop end */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */ +}; + + +static unsigned char +FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) { + if (parm_defs[type].type == PARM_SIGN) { + if (value > 0x7f) + effect += (int)value - 0x100; + else + effect += (int)value; + } else { + effect += (int)value; + } + } + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned char)effect; + } + return value; +} + +/* get word effect value */ +static unsigned short +FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) + effect += (int)value; + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned short)effect; + } + return value; +} + +/* get word (upper=type1/lower=type2) effect value */ +static unsigned short +FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value) +{ + unsigned short tmp; + tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8)); + tmp <<= 8; + tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff)); + return tmp; +} + +/* address offset */ +static int +FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode) +{ + int addr = 0; + if (lay && FX_ON(lay, hi)) + addr = (short)lay->val[hi]; + else if (FX_ON(rec, hi)) + addr = (short)rec->val[hi]; + addr = addr << 15; + if (lay && FX_ON(lay, lo)) + addr += (short)lay->val[lo]; + else if (FX_ON(rec, lo)) + addr += (short)rec->val[lo]; + if (!(mode & AWE_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + + +/* + * turn on/off sample + */ + +/* table for volume target calculation */ +static unsigned short voltarget[16] = { + 0xEAC0, 0XE0C8, 0XD740, 0XCE20, 0XC560, 0XBD08, 0XB500, 0XAD58, + 0XA5F8, 0X9EF0, 0X9830, 0X91C0, 0X8B90, 0X85A8, 0X8000, 0X7A90 +}; + +static void +awe_note_on(int voice) +{ + unsigned int temp; + int addr; + int vtarget, ftarget, ptarget, pitch; + awe_voice_info *vp; + awe_voice_parm_block *parm; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* A voice sample must assigned before calling */ + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + parm = (awe_voice_parm_block*)&vp->parm; + + /* channel to be silent and idle */ + awe_poke(AWE_DCYSUSV(voice), 0x0080); + awe_poke(AWE_VTFT(voice), 0x0000FFFF); + awe_poke(AWE_CVCF(voice), 0x0000FFFF); + awe_poke(AWE_PTRX(voice), 0); + awe_poke(AWE_CPF(voice), 0); + + /* set pitch offset */ + awe_set_pitch(voice, TRUE); + + /* modulation & volume envelope */ + if (parm->modatk >= 0x80 && parm->moddelay >= 0x8000) { + awe_poke(AWE_ENVVAL(voice), 0xBFFF); + pitch = (parm->env1pit<<4) + voices[voice].apitch; + if (pitch > 0xffff) pitch = 0xffff; + /* calculate filter target */ + ftarget = parm->cutoff + parm->env1fc; + limitvalue(ftarget, 0, 255); + ftarget <<= 8; + } else { + awe_poke(AWE_ENVVAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, parm->moddelay)); + ftarget = parm->cutoff; + ftarget <<= 8; + pitch = voices[voice].apitch; + } + + /* calcualte pitch target */ + if (pitch != 0xffff) { + ptarget = 1 << (pitch >> 12); + if (pitch & 0x800) ptarget += (ptarget*0x102e)/0x2710; + if (pitch & 0x400) ptarget += (ptarget*0x764)/0x2710; + if (pitch & 0x200) ptarget += (ptarget*0x389)/0x2710; + ptarget += (ptarget>>1); + if (ptarget > 0xffff) ptarget = 0xffff; + + } else ptarget = 0xffff; + if (parm->modatk >= 0x80) + awe_poke(AWE_ATKHLD(voice), + FX_BYTE(fx, fx_lay, AWE_FX_ENV1_HOLD, parm->modhld) << 8 | 0x7f); + else + awe_poke(AWE_ATKHLD(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, + vp->parm.modatkhld)); + awe_poke(AWE_DCYSUS(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, + vp->parm.moddcysus)); + + if (parm->volatk >= 0x80 && parm->voldelay >= 0x8000) { + awe_poke(AWE_ENVVOL(voice), 0xBFFF); + vtarget = voltarget[voices[voice].avol%0x10]>>(voices[voice].avol>>4); + } else { + awe_poke(AWE_ENVVOL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); + vtarget = 0; + } + if (parm->volatk >= 0x80) + awe_poke(AWE_ATKHLDV(voice), + FX_BYTE(fx, fx_lay, AWE_FX_ENV2_HOLD, parm->volhld) << 8 | 0x7f); + else + awe_poke(AWE_ATKHLDV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, + vp->parm.volatkhld)); + /* decay/sustain parameter for volume envelope must be set at last */ + + /* cutoff and volume */ + awe_set_volume(voice, TRUE); + + /* modulation envelope heights */ + awe_poke(AWE_PEFE(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, + vp->parm.pefe)); + + /* lfo1/2 delay */ + awe_poke(AWE_LFO1VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); + awe_poke(AWE_LFO2VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); + + /* lfo1 pitch & cutoff shift */ + awe_fx_fmmod(voice, TRUE); + /* lfo1 volume & freq */ + awe_fx_tremfrq(voice, TRUE); + /* lfo2 pitch & freq */ + awe_fx_fm2frq2(voice, TRUE); + /* pan & loop start */ + awe_set_pan(voice, TRUE); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->loopend - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END, + AWE_FX_COARSE_LOOP_END, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus); + temp = (temp <<24) | (unsigned int)addr; + awe_poke_dw(AWE_CSL(voice), temp); + DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr)); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->start - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START, + AWE_FX_COARSE_SAMPLE_START, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ); + temp = (temp<<28) | (unsigned int)addr; + awe_poke_dw(AWE_CCCA(voice), temp); + DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr)); + + /* clear unknown registers */ + awe_poke_dw(AWE_00A0(voice), 0); + awe_poke_dw(AWE_0080(voice), 0); + + /* reset volume */ + awe_poke_dw(AWE_VTFT(voice), (vtarget<<16)|ftarget); + awe_poke_dw(AWE_CVCF(voice), (vtarget<<16)|ftarget); + + /* set reverb */ + temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb); + temp = (temp << 8) | (ptarget << 16) | voices[voice].aaux; + awe_poke_dw(AWE_PTRX(voice), temp); + awe_poke_dw(AWE_CPF(voice), ptarget << 16); + /* turn on envelope */ + awe_poke(AWE_DCYSUSV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, + vp->parm.voldcysus)); + + voices[voice].state = AWE_ST_ON; + + /* clear voice position for the next note on this channel */ + if (SINGLE_LAYER_MODE()) { + FX_UNSET(fx, AWE_FX_SAMPLE_START); + FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START); + } +} + + +/* turn off the voice */ +static void +awe_note_off(int voice) +{ + awe_voice_info *vp; + unsigned short tmp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if ((vp = voices[voice].sample) == NULL) { + voices[voice].state = AWE_ST_OFF; + return; + } + + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE, + (unsigned char)vp->parm.modrelease); + awe_poke(AWE_DCYSUS(voice), tmp); + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE, + (unsigned char)vp->parm.volrelease); + awe_poke(AWE_DCYSUSV(voice), tmp); + voices[voice].state = AWE_ST_RELEASED; +} + +/* force to terminate the voice (no releasing echo) */ +static void +awe_terminate(int voice) +{ + awe_poke(AWE_DCYSUSV(voice), 0x807F); + awe_tweak_voice(voice); + voices[voice].state = AWE_ST_OFF; +} + +/* turn off other voices with the same exclusive class (for drums) */ +static void +awe_exclusive_off(int voice) +{ + int i, exclass; + + if (voices[voice].sample == NULL) + return; + if ((exclass = voices[voice].sample->exclusiveClass) == 0) + return; /* not exclusive */ + + /* turn off voices with the same class */ + for (i = 0; i < awe_max_voices; i++) { + if (i != voice && IS_PLAYING(i) && + voices[i].sample && voices[i].ch == voices[voice].ch && + voices[i].sample->exclusiveClass == exclass) { + DEBUG(4,printk("AWE32: [exoff(%d)]\n", i)); + awe_terminate(i); + awe_voice_init(i, TRUE); + } + } +} + + +/* + * change the parameters of an audible voice + */ + +/* change pitch */ +static void +awe_set_pitch(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + awe_poke(AWE_IP(voice), voices[voice].apitch); + DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch)); +} + +/* calculate & change pitch */ +static void +awe_set_voice_pitch(int voice, int forced) +{ + awe_calc_pitch(voice); + awe_set_pitch(voice, forced); +} + +/* change volume & cutoff */ +static void +awe_set_volume(int voice, int forced) +{ + awe_voice_info *vp; + unsigned short tmp2; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (!IS_PLAYING(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, + (unsigned char)voices[voice].acutoff); + tmp2 = (tmp2 << 8); + tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN, + (unsigned char)voices[voice].avol); + awe_poke(AWE_IFATN(voice), tmp2); +} + +/* calculate & change volume */ +static void +awe_set_voice_vol(int voice, int forced) +{ + if (IS_EMPTY(voice)) + return; + awe_calc_volume(voice); + awe_set_volume(voice, forced); +} + + +/* change pan; this could make a click noise.. */ +static void +awe_set_pan(int voice, int forced) +{ + unsigned int temp; + int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->fixpan > 0) /* 0-127 */ + temp = 255 - (int)vp->fixpan * 2; + else { + int pos = 0; + if (vp->pan >= 0) /* 0-127 */ + pos = (int)vp->pan * 2 - 128; + pos += voices[voice].cinfo->panning; /* -128 - 127 */ + temp = 127 - pos; + } + limitvalue(temp, 0, 255); + if (ctrls[AWE_MD_PAN_EXCHANGE]) { + temp = 255 - temp; + } + if (forced || temp != voices[voice].apan) { + voices[voice].apan = temp; + if (temp == 0) + voices[voice].aaux = 0xff; + else + voices[voice].aaux = (-temp) & 0xff; + addr = vp->loopstart - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START, + AWE_FX_COARSE_LOOP_START, vp->mode); + temp = (temp<<24) | (unsigned int)addr; + awe_poke_dw(AWE_PSST(voice), temp); + DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr)); + } +} + +/* effects change during playing */ +static void +awe_fx_fmmod(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_FMMOD(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, + vp->parm.fmmod)); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +awe_fx_tremfrq(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_TREMFRQ(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, + vp->parm.tremfrq)); +} + +/* set lfo2 pitch & frequency */ +static void +awe_fx_fm2frq2(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + awe_poke(AWE_FM2FRQ2(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, + vp->parm.fm2frq2)); +} + + +/* Q & current address (Q 4bit value, MSB) */ +static void +awe_fx_filterQ(int voice, int forced) +{ + unsigned int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index == 0) + return; + + addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff; + addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28); + awe_poke_dw(AWE_CCCA(voice), addr); +} + +/* + * calculate pitch offset + * + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + */ + +static void +awe_calc_pitch(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int offset; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + + /* calculate offset */ + if (ap->fixkey >= 0) { + DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune)); + offset = (ap->fixkey - ap->root) * 4096 / 12; + } else { + DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune)); + offset = (vp->note - ap->root) * 4096 / 12; + DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset)); + } + offset = (offset * ap->scaleTuning) / 100; + DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset)); + offset += ap->tune * 4096 / 1200; + DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset)); + if (cp->bender != 0) { + DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender)); + /* (819200: 1 semitone) ==> (4096: 12 semitones) */ + offset += cp->bender * cp->bender_range / 2400; + } + + /* add initial pitch correction */ + if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH)) + offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH]; + else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) + offset += cp->fx.val[AWE_FX_INIT_PITCH]; + + /* 0xe000: root pitch */ + vp->apitch = 0xe000 + ap->rate_offset + offset; + DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset)); + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY +/* calculate MIDI key and semitone from the specified frequency */ +static void +awe_calc_pitch_from_freq(int voice, int freq) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + int offset; + int note; + + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + note = freq_to_note(freq); + offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200; + offset = (offset * ap->scaleTuning) / 100; + if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH)) + offset += fx_lay->val[AWE_FX_INIT_PITCH]; + else if (FX_ON(fx, AWE_FX_INIT_PITCH)) + offset += fx->val[AWE_FX_INIT_PITCH]; + vp->apitch = 0xe000 + ap->rate_offset + offset; + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/* + * calculate volume attenuation + * + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + */ + +static int vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + +/* tables for volume->attenuation calculation */ +static unsigned char voltab1[128] = { + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, + 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, + 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, + 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char voltab2[128] = { + 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, + 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, + 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, + 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, + 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, + 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, + 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char expressiontab[128] = { + 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, + 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, + 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, + 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, + 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, + 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, + 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, + 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static void +awe_calc_volume(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int vol; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + + ap = vp->sample; + if (ap->index == 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample((awe_voice_list*)ap) == 0) + return; + } + + if (ctrls[AWE_MD_NEW_VOLUME_CALC]) { + int main_vol = cp->main_vol * ap->amplitude / 127; + limitvalue(vp->velocity, 0, 127); + limitvalue(main_vol, 0, 127); + limitvalue(cp->expression_vol, 0, 127); + + vol = voltab1[main_vol] + voltab2[vp->velocity]; + vol = (vol * 8) / 3; + vol += ap->attenuation; + if (cp->expression_vol < 127) + vol += ((0x100 - vol) * expressiontab[cp->expression_vol])/128; + vol += atten_offset; + if (atten_relative) + vol += ctrls[AWE_MD_ZERO_ATTEN]; + limitvalue(vol, 0, 255); + vp->avol = vol; + + } else { + /* 0 - 127 */ + vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127); + vol = vol * ap->amplitude / 127; + + if (vol < 0) vol = 0; + if (vol > 127) vol = 127; + + /* calc to attenuation */ + vol = vol_table[vol]; + vol += (int)ap->attenuation; + vol += atten_offset; + if (atten_relative) + vol += ctrls[AWE_MD_ZERO_ATTEN]; + if (vol > 255) vol = 255; + + vp->avol = vol; + } + if (cp->bank != AWE_DRUM_BANK && ((awe_voice_parm_block*)(&ap->parm))->volatk < 0x7d) { + int atten; + if (vp->velocity < 70) atten = 70; + else atten = vp->velocity; + vp->acutoff = (atten * ap->parm.cutoff + 0xa0) >> 7; + } else { + vp->acutoff = ap->parm.cutoff; + } + DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol)); +} + +/* change master volume */ +static void +awe_change_master_volume(short val) +{ + limitvalue(val, 0, 127); + atten_offset = vol_table[val]; + atten_relative = TRUE; + awe_update_volume(); +} + +/* update volumes of all available channels */ +static void awe_update_volume(void) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + awe_set_voice_vol(i, TRUE); +} + +/* set sostenuto on */ +static void awe_sostenuto_on(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + voices[voice].sostenuto = 127; +} + + +/* drop sustain */ +static void awe_sustain_off(int voice, int forced) +{ + if (voices[voice].state == AWE_ST_SUSTAINED) { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + + +/* terminate and initialize voice */ +static void awe_terminate_and_init(int voice, int forced) +{ + awe_terminate(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, TRUE); +} + + +/* + * synth operation routines + */ + +#define AWE_VOICE_KEY(v) (0x8000 | (v)) +#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1)) +#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c)) + +/* initialize the voice */ +static void +awe_voice_init(int voice, int init_all) +{ + voice_info *vp = &voices[voice]; + + /* reset voice search key */ + if (playing_mode == AWE_PLAY_DIRECT) + vp->key = AWE_VOICE_KEY(voice); + else + vp->key = 0; + + /* clear voice mapping */ + voice_alloc->map[voice] = 0; + + /* touch the timing flag */ + vp->time = current_alloc_time; + + /* initialize other parameters if necessary */ + if (init_all) { + vp->note = -1; + vp->velocity = 0; + vp->sostenuto = 0; + + vp->sample = NULL; + vp->cinfo = &channels[voice]; + vp->ch = voice; + vp->state = AWE_ST_OFF; + + /* emu8000 parameters */ + vp->apitch = 0; + vp->avol = 255; + vp->apan = -1; + } +} + +/* clear effects */ +static void awe_fx_init(int ch) +{ + if (SINGLE_LAYER_MODE() && !ctrls[AWE_MD_KEEP_EFFECT]) { + memset(&channels[ch].fx, 0, sizeof(channels[ch].fx)); + memset(&channels[ch].fx_layer, 0, sizeof(&channels[ch].fx_layer)); + } +} + +/* initialize channel info */ +static void awe_channel_init(int ch, int init_all) +{ + awe_chan_info *cp = &channels[ch]; + cp->channel = ch; + if (init_all) { + cp->panning = 0; /* zero center */ + cp->bender_range = 200; /* sense * 100 */ + cp->main_vol = 127; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) { + cp->instr = ctrls[AWE_MD_DEF_DRUM]; + cp->bank = AWE_DRUM_BANK; + } else { + cp->instr = ctrls[AWE_MD_DEF_PRESET]; + cp->bank = ctrls[AWE_MD_DEF_BANK]; + } + } + + cp->bender = 0; /* zero tune skew */ + cp->expression_vol = 127; + cp->chan_press = 0; + cp->sustained = 0; + + if (! ctrls[AWE_MD_KEEP_EFFECT]) { + memset(&cp->fx, 0, sizeof(cp->fx)); + memset(&cp->fx_layer, 0, sizeof(cp->fx_layer)); + } +} + + +/* change the voice parameters; voice = channel */ +static void awe_voice_change(int voice, fx_affect_func func) +{ + int i; + switch (playing_mode) { + case AWE_PLAY_DIRECT: + func(voice, FALSE); + break; + case AWE_PLAY_INDIRECT: + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == AWE_VOICE_KEY(voice)) + func(i, FALSE); + break; + default: + for (i = 0; i < awe_max_voices; i++) + if (KEY_CHAN_MATCH(voices[i].key, voice)) + func(i, FALSE); + break; + } +} + + +/* + * device open / close + */ + +/* open device: + * reset status of all voices, and clear sample position flag + */ +static int +awe_open(int dev, int mode) +{ + if (awe_busy) + return -EBUSY; + + awe_busy = TRUE; + + /* set default mode */ + awe_init_ctrl_parms(FALSE); + atten_relative = TRUE; + atten_offset = 0; + drum_flags = DEFAULT_DRUM_FLAGS; + playing_mode = AWE_PLAY_INDIRECT; + + /* reset voices & channels */ + awe_reset(dev); + + patch_opened = 0; + + return 0; +} + + +/* close device: + * reset all voices again (terminate sounds) + */ +static void +awe_close(int dev) +{ + awe_reset(dev); + awe_busy = FALSE; +} + + +/* set miscellaneous mode parameters + */ +static void +awe_init_ctrl_parms(int init_all) +{ + int i; + for (i = 0; i < AWE_MD_END; i++) { + if (init_all || ctrl_parms[i].init_each_time) + ctrls[i] = ctrl_parms[i].value; + } +} + + +/* sequencer I/O control: + */ +static int +awe_ioctl(int dev, unsigned int cmd, void __user *arg) +{ + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + if (copy_to_user(arg, &awe_info, sizeof(awe_info))) + return -EFAULT; + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + awe_reset(dev); + awe_reset_samples(); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + /* what's this? */ + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return memsize - awe_free_mem_ptr() * 2; + break; + + default: + printk(KERN_WARNING "AWE32: unsupported ioctl %d\n", cmd); + return -EINVAL; + break; + } +} + + +static int voice_in_range(int voice) +{ + if (playing_mode == AWE_PLAY_DIRECT) { + if (voice < 0 || voice >= awe_max_voices) + return FALSE; + } else { + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return FALSE; + } + return TRUE; +} + +static void release_voice(int voice, int do_sustain) +{ + if (IS_NO_SOUND(voice)) + return; + if (do_sustain && (voices[voice].cinfo->sustained == 127 || + voices[voice].sostenuto == 127)) + voices[voice].state = AWE_ST_SUSTAINED; + else { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + +/* release all notes */ +static void awe_note_off_all(int do_sustain) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + release_voice(i, do_sustain); +} + +/* kill a voice: + * not terminate, just release the voice. + */ +static int +awe_kill_note(int dev, int voice, int note, int velocity) +{ + int i, v2, key; + + DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return -EINVAL; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + break; + + case AWE_PLAY_MULTI2: + v2 = voice_alloc->map[voice] >> 8; + voice_alloc->map[voice] = 0; + voice = v2; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + /* continue to below */ + default: + key = AWE_CHAN_KEY(voice, note); + break; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + release_voice(i, TRUE); + } + return 0; +} + + +static void start_or_volume_change(int voice, int velocity) +{ + voices[voice].velocity = velocity; + awe_calc_volume(voice); + if (voices[voice].state == AWE_ST_STANDBY) + awe_note_on(voice); + else if (voices[voice].state == AWE_ST_ON) + awe_set_volume(voice, FALSE); +} + +static void set_and_start_voice(int voice, int state) +{ + /* calculate pitch & volume parameters */ + voices[voice].state = state; + awe_calc_pitch(voice); + awe_calc_volume(voice); + if (state == AWE_ST_ON) + awe_note_on(voice); +} + +/* start a voice: + * if note is 255, identical with aftertouch function. + * Otherwise, start a voice with specified not and volume. + */ +static int +awe_start_note(int dev, int voice, int note, int velocity) +{ + int i, key, state, volonly; + + DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return -EINVAL; + + if (velocity == 0) + state = AWE_ST_STANDBY; /* stand by for playing */ + else + state = AWE_ST_ON; /* really play */ + volonly = FALSE; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + if (note == 255) + volonly = TRUE; + break; + + case AWE_PLAY_MULTI2: + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + /* continue to below */ + default: + if (note >= 128) { /* key volume mode */ + note -= 128; + volonly = TRUE; + } + key = AWE_CHAN_KEY(voice, note); + break; + } + + /* dynamic volume change */ + if (volonly) { + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + start_or_volume_change(i, velocity); + } + return 0; + } + + /* if the same note still playing, stop it */ + if (playing_mode != AWE_PLAY_DIRECT || ctrls[AWE_MD_EXCLUSIVE_SOUND]) { + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) { + if (voices[i].state == AWE_ST_ON) { + awe_note_off(i); + awe_voice_init(i, FALSE); + } else if (voices[i].state == AWE_ST_STANDBY) + awe_voice_init(i, TRUE); + } + } + + /* allocate voices */ + if (playing_mode == AWE_PLAY_DIRECT) + awe_alloc_one_voice(voice, note, velocity); + else + awe_alloc_multi_voices(voice, note, velocity, key); + + /* turn off other voices exlusively (for drums) */ + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) + awe_exclusive_off(i); + + /* set up pitch and volume parameters */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key && voices[i].state == AWE_ST_OFF) + set_and_start_voice(i, state); + } + + return 0; +} + + +/* calculate hash key */ +static int +awe_search_key(int bank, int preset, int note) +{ + unsigned int key; + +#if 1 /* new hash table */ + if (bank == AWE_DRUM_BANK) + key = preset + note + 128; + else + key = bank + preset; +#else + key = preset; +#endif + key %= AWE_MAX_PRESETS; + + return (int)key; +} + + +/* search instrument from hash table */ +static awe_voice_list * +awe_search_instr(int bank, int preset, int note) +{ + awe_voice_list *p; + int key, key2; + + key = awe_search_key(bank, preset, note); + for (p = preset_table[key]; p; p = p->next_bank) { + if (p->instr == preset && p->bank == bank) + return p; + } + key2 = awe_search_key(bank, preset, 0); /* search default */ + if (key == key2) + return NULL; + for (p = preset_table[key2]; p; p = p->next_bank) { + if (p->instr == preset && p->bank == bank) + return p; + } + return NULL; +} + + +/* assign the instrument to a voice */ +static int +awe_set_instr_2(int dev, int voice, int instr_no) +{ + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return -EINVAL; + } + return awe_set_instr(dev, voice, instr_no); +} + +/* assign the instrument to a channel; voice is the channel number */ +static int +awe_set_instr(int dev, int voice, int instr_no) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return -EINVAL; + + if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) + return -EINVAL; + + cinfo = &channels[voice]; + cinfo->instr = instr_no; + DEBUG(2,printk("AWE32: [program(%d) %d]\n", voice, instr_no)); + + return 0; +} + + +/* reset all voices; terminate sounds and initialize parameters */ +static void +awe_reset(int dev) +{ + int i; + current_alloc_time = 0; + /* don't turn off voice 31 and 32. they are used also for FM voices */ + for (i = 0; i < awe_max_voices; i++) { + awe_terminate(i); + awe_voice_init(i, TRUE); + } + for (i = 0; i < AWE_MAX_CHANNELS; i++) + awe_channel_init(i, TRUE); + for (i = 0; i < 16; i++) { + awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127; + awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127; + } + awe_init_fm(); + awe_tweak(); +} + + +/* hardware specific control: + * GUS specific and AWE32 specific controls are available. + */ +static void +awe_hw_control(int dev, unsigned char *event) +{ + int cmd = event[2]; + if (cmd & _AWE_MODE_FLAG) + awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#ifdef AWE_HAS_GUS_COMPATIBILITY + else + awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#endif +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* GUS compatible controls */ +static void +awe_hw_gus_control(int dev, int cmd, unsigned char *event) +{ + int voice, i, key; + unsigned short p1; + short p2; + int plong; + + if (MULTI_LAYER_MODE()) + return; + if (cmd == _GUS_NUMVOICES) + return; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + awe_set_instr(dev, voice, p1); + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> -128 to 127 */ + awe_panning(dev, voice, ((int)p1 << 4) - 128); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: + FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START, + (short)(plong & 0x7fff)); + FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff); + return; + } + + key = AWE_VOICE_KEY(voice); + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) { + switch (cmd) { + case _GUS_VOICEON: + awe_note_on(i); + break; + + case _GUS_VOICEOFF: + awe_terminate(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, TRUE); + break; + + case _GUS_VOICEFADE: + awe_note_off(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, FALSE); + break; + + case _GUS_VOICEFREQ: + awe_calc_pitch_from_freq(i, plong); + break; + } + } + } +} + +#endif /* gus_compat */ + + +/* AWE32 specific controls */ +static void +awe_hw_awe_control(int dev, int cmd, unsigned char *event) +{ + int voice; + unsigned short p1; + short p2; + int i; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + + switch (cmd) { + case _AWE_DEBUG_MODE: + ctrls[AWE_MD_DEBUG_MODE] = p1; + printk(KERN_DEBUG "AWE32: debug mode = %d\n", ctrls[AWE_MD_DEBUG_MODE]); + break; + case _AWE_REVERB_MODE: + ctrls[AWE_MD_REVERB_MODE] = p1; + awe_update_reverb_mode(); + break; + + case _AWE_CHORUS_MODE: + ctrls[AWE_MD_CHORUS_MODE] = p1; + awe_update_chorus_mode(); + break; + + case _AWE_REMOVE_LAST_SAMPLES: + DEBUG(0,printk("AWE32: remove last samples\n")); + awe_reset(0); + if (locked_sf_id > 0) + awe_remove_samples(locked_sf_id); + break; + + case _AWE_INITIALIZE_CHIP: + awe_initialize(); + break; + + case _AWE_SEND_EFFECT: + i = -1; + if (p1 >= 0x100) { + i = (p1 >> 8); + if (i < 0 || i >= MAX_LAYERS) + break; + } + awe_send_effect(voice, i, p1, p2); + break; + + case _AWE_RESET_CHANNEL: + awe_channel_init(voice, !p1); + break; + + case _AWE_TERMINATE_ALL: + awe_reset(0); + break; + + case _AWE_TERMINATE_CHANNEL: + awe_voice_change(voice, awe_terminate_and_init); + break; + + case _AWE_RELEASE_ALL: + awe_note_off_all(FALSE); + break; + case _AWE_NOTEOFF_ALL: + awe_note_off_all(TRUE); + break; + + case _AWE_INITIAL_VOLUME: + DEBUG(0,printk("AWE32: init attenuation %d\n", p1)); + atten_relative = (char)p2; + atten_offset = (short)p1; + awe_update_volume(); + break; + + case _AWE_CHN_PRESSURE: + channels[voice].chan_press = p1; + awe_modwheel_change(voice, p1); + break; + + case _AWE_CHANNEL_MODE: + DEBUG(0,printk("AWE32: channel mode = %d\n", p1)); + playing_mode = p1; + awe_reset(0); + break; + + case _AWE_DRUM_CHANNELS: + DEBUG(0,printk("AWE32: drum flags = %x\n", p1)); + drum_flags = *(unsigned int*)&event[4]; + break; + + case _AWE_MISC_MODE: + DEBUG(0,printk("AWE32: ctrl parms = %d %d\n", p1, p2)); + if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) { + ctrls[p1] = p2; + if (ctrl_parms[p1].update) + ctrl_parms[p1].update(); + } + break; + + case _AWE_EQUALIZER: + ctrls[AWE_MD_BASS_LEVEL] = p1; + ctrls[AWE_MD_TREBLE_LEVEL] = p2; + awe_update_equalizer(); + break; + + default: + DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice)); + break; + } +} + + +/* change effects */ +static void +awe_send_effect(int voice, int layer, int type, int val) +{ + awe_chan_info *cinfo; + FX_Rec *fx; + int mode; + + cinfo = &channels[voice]; + if (layer >= 0 && layer < MAX_LAYERS) + fx = &cinfo->fx_layer[layer]; + else + fx = &cinfo->fx; + + if (type & 0x40) + mode = FX_FLAG_OFF; + else if (type & 0x80) + mode = FX_FLAG_ADD; + else + mode = FX_FLAG_SET; + type &= 0x3f; + + if (type >= 0 && type < AWE_FX_END) { + DEBUG(2,printk("AWE32: effects (%d) %d %d\n", voice, type, val)); + if (mode == FX_FLAG_SET) + FX_SET(fx, type, val); + else if (mode == FX_FLAG_ADD) + FX_ADD(fx, type, val); + else + FX_UNSET(fx, type); + if (mode != FX_FLAG_OFF && parm_defs[type].realtime) { + DEBUG(2,printk("AWE32: fx_realtime (%d)\n", voice)); + awe_voice_change(voice, parm_defs[type].realtime); + } + } +} + + +/* change modulation wheel; voice is already mapped on multi2 mode */ +static void +awe_modwheel_change(int voice, int value) +{ + int i; + awe_chan_info *cinfo; + + cinfo = &channels[voice]; + i = value * ctrls[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i); + awe_voice_change(voice, awe_fx_fm2frq2); +} + + +/* voice pressure change */ +static void +awe_aftertouch(int dev, int voice, int pressure) +{ + int note; + + DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); + if (! voice_in_range(voice)) + return; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + awe_start_note(dev, voice, 255, pressure); + break; + case AWE_PLAY_MULTI2: + note = (voice_alloc->map[voice] & 0xff) - 1; + awe_key_pressure(dev, voice, note + 0x80, pressure); + break; + } +} + + +/* voice control change */ +static void +awe_controller(int dev, int voice, int ctrl_num, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + + switch (ctrl_num) { + case CTL_BANK_SELECT: /* MIDI control #0 */ + DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) && + !ctrls[AWE_MD_TOGGLE_DRUM_BANK]) + break; + if (value < 0 || value > 255) + break; + cinfo->bank = value; + if (cinfo->bank == AWE_DRUM_BANK) + DRUM_CHANNEL_ON(cinfo->channel); + else + DRUM_CHANNEL_OFF(cinfo->channel); + awe_set_instr(dev, voice, cinfo->instr); + break; + + case CTL_MODWHEEL: /* MIDI control #1 */ + DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value)); + awe_modwheel_change(voice, value); + break; + + case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); + /* zero centered */ + cinfo->bender = value; + awe_voice_change(voice, awe_set_voice_pitch); + break; + + case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); + /* value = sense x 100 */ + cinfo->bender_range = value; + /* no audible pitch change yet.. */ + break; + + case CTL_EXPRESSION: /* MIDI control #11 */ + if (SINGLE_LAYER_MODE()) + value /= 128; + case CTRL_EXPRESSION: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->expression_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_PAN: /* MIDI control #10 */ + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); + /* (0-127) -> signed 8bit */ + cinfo->panning = value * 2 - 128; + if (ctrls[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); + break; + + case CTL_MAIN_VOLUME: /* MIDI control #7 */ + if (SINGLE_LAYER_MODE()) + value = (value * 100) / 16383; + case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->main_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ + DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); + break; + + case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ + DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); + break; + + case 120: /* all sounds off */ + awe_note_off_all(FALSE); + break; + case 123: /* all notes off */ + awe_note_off_all(TRUE); + break; + + case CTL_SUSTAIN: /* MIDI control #64 */ + cinfo->sustained = value; + if (value != 127) + awe_voice_change(voice, awe_sustain_off); + break; + + case CTL_SOSTENUTO: /* MIDI control #66 */ + if (value == 127) + awe_voice_change(voice, awe_sostenuto_on); + else + awe_voice_change(voice, awe_sustain_off); + break; + + default: + DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", + voice, ctrl_num, value)); + break; + } +} + + +/* voice pan change (value = -128 - 127) */ +static void +awe_panning(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + cinfo->panning = value; + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); + if (ctrls[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); +} + + +/* volume mode change */ +static void +awe_volume_method(int dev, int mode) +{ + /* not impremented */ + DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); +} + + +/* pitch wheel change: 0-16384 */ +static void +awe_bender(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + /* convert to zero centered value */ + cinfo = &channels[voice]; + cinfo->bender = value - 8192; + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); + awe_voice_change(voice, awe_set_voice_pitch); +} + + +/* + * load a sound patch: + * three types of patches are accepted: AWE, GUS, and SYSEX. + */ + +static int +awe_load_patch(int dev, int format, const char __user *addr, + int offs, int count, int pmgr_flag) +{ + awe_patch_info patch; + int rc = 0; + +#ifdef AWE_HAS_GUS_COMPATIBILITY + if (format == GUS_PATCH) { + return awe_load_guspatch(addr, offs, count, pmgr_flag); + } else +#endif + if (format == SYSEX_PATCH) { + /* no system exclusive message supported yet */ + return 0; + } else if (format != AWE_PATCH) { + printk(KERN_WARNING "AWE32 Error: Invalid patch format (key) 0x%x\n", format); + return -EINVAL; + } + + if (count < AWE_PATCH_INFO_SIZE) { + printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); + return -EINVAL; + } + if (copy_from_user(((char*)&patch) + offs, addr + offs, + AWE_PATCH_INFO_SIZE - offs)) + return -EFAULT; + + count -= AWE_PATCH_INFO_SIZE; + if (count < patch.len) { + printk(KERN_WARNING "AWE32: sample: Patch record too short (%d<%d)\n", + count, patch.len); + return -EINVAL; + } + + switch (patch.type) { + case AWE_LOAD_INFO: + rc = awe_load_info(&patch, addr, count); + break; + case AWE_LOAD_DATA: + rc = awe_load_data(&patch, addr, count); + break; + case AWE_OPEN_PATCH: + rc = awe_open_patch(&patch, addr, count); + break; + case AWE_CLOSE_PATCH: + rc = awe_close_patch(&patch, addr, count); + break; + case AWE_UNLOAD_PATCH: + rc = awe_unload_patch(&patch, addr, count); + break; + case AWE_REPLACE_DATA: + rc = awe_replace_data(&patch, addr, count); + break; + case AWE_MAP_PRESET: + rc = awe_load_map(&patch, addr, count); + break; + /* case AWE_PROBE_INFO: + rc = awe_probe_info(&patch, addr, count); + break;*/ + case AWE_PROBE_DATA: + rc = awe_probe_data(&patch, addr, count); + break; + case AWE_REMOVE_INFO: + rc = awe_remove_info(&patch, addr, count); + break; + case AWE_LOAD_CHORUS_FX: + rc = awe_load_chorus_fx(&patch, addr, count); + break; + case AWE_LOAD_REVERB_FX: + rc = awe_load_reverb_fx(&patch, addr, count); + break; + + default: + printk(KERN_WARNING "AWE32 Error: unknown patch format type %d\n", + patch.type); + rc = -EINVAL; + } + + return rc; +} + + +/* create an sf list record */ +static int +awe_create_sf(int type, char *name) +{ + sf_list *rec; + + /* terminate sounds */ + awe_reset(0); + rec = (sf_list *)kmalloc(sizeof(*rec), GFP_KERNEL); + if (rec == NULL) + return 1; /* no memory */ + rec->sf_id = current_sf_id + 1; + rec->type = type; + if (/*current_sf_id == 0 ||*/ (type & AWE_PAT_LOCKED) != 0) + locked_sf_id = current_sf_id + 1; + rec->num_info = awe_free_info(); + rec->num_sample = awe_free_sample(); + rec->mem_ptr = awe_free_mem_ptr(); + rec->infos = rec->last_infos = NULL; + rec->samples = rec->last_samples = NULL; + + /* add to linked-list */ + rec->next = NULL; + rec->prev = sftail; + if (sftail) + sftail->next = rec; + else + sfhead = rec; + sftail = rec; + current_sf_id++; + +#ifdef AWE_ALLOW_SAMPLE_SHARING + rec->shared = NULL; + if (name) + memcpy(rec->name, name, AWE_PATCH_NAME_LEN); + else + strcpy(rec->name, "*TEMPORARY*"); + if (current_sf_id > 1 && name && (type & AWE_PAT_SHARED) != 0) { + /* is the current font really a shared font? */ + if (is_shared_sf(rec->name)) { + /* check if the shared font is already installed */ + sf_list *p; + for (p = rec->prev; p; p = p->prev) { + if (is_identical_name(rec->name, p)) { + rec->shared = p; + break; + } + } + } + } +#endif /* allow sharing */ + + return 0; +} + + +#ifdef AWE_ALLOW_SAMPLE_SHARING + +/* check if the given name is a valid shared name */ +#define ASC_TO_KEY(c) ((c) - 'A' + 1) +static int is_shared_sf(unsigned char *name) +{ + static unsigned char id_head[4] = { + ASC_TO_KEY('A'), ASC_TO_KEY('W'), ASC_TO_KEY('E'), + AWE_MAJOR_VERSION, + }; + if (memcmp(name, id_head, 4) == 0) + return TRUE; + return FALSE; +} + +/* check if the given name matches to the existing list */ +static int is_identical_name(unsigned char *name, sf_list *p) +{ + char *id = p->name; + if (is_shared_sf(id) && memcmp(id, name, AWE_PATCH_NAME_LEN) == 0) + return TRUE; + return FALSE; +} + +/* check if the given voice info exists */ +static int info_duplicated(sf_list *sf, awe_voice_list *rec) +{ + /* search for all sharing lists */ + for (; sf; sf = sf->shared) { + awe_voice_list *p; + for (p = sf->infos; p; p = p->next) { + if (p->type == V_ST_NORMAL && + p->bank == rec->bank && + p->instr == rec->instr && + p->v.low == rec->v.low && + p->v.high == rec->v.high && + p->v.sample == rec->v.sample) + return TRUE; + } + } + return FALSE; +} + +#endif /* AWE_ALLOW_SAMPLE_SHARING */ + + +/* free sf_list record */ +/* linked-list in this function is not cared */ +static void +awe_free_sf(sf_list *sf) +{ + if (sf->infos) { + awe_voice_list *p, *next; + for (p = sf->infos; p; p = next) { + next = p->next; + kfree(p); + } + } + if (sf->samples) { + awe_sample_list *p, *next; + for (p = sf->samples; p; p = next) { + next = p->next; + kfree(p); + } + } + kfree(sf); +} + + +/* open patch; create sf list and set opened flag */ +static int +awe_open_patch(awe_patch_info *patch, const char __user *addr, int count) +{ + awe_open_parm parm; + int shared; + + if (copy_from_user(&parm, addr + AWE_PATCH_INFO_SIZE, sizeof(parm))) + return -EFAULT; + shared = FALSE; + +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (sftail && (parm.type & AWE_PAT_SHARED) != 0) { + /* is the previous font the same font? */ + if (is_identical_name(parm.name, sftail)) { + /* then append to the previous */ + shared = TRUE; + awe_reset(0); + if (parm.type & AWE_PAT_LOCKED) + locked_sf_id = current_sf_id; + } + } +#endif /* allow sharing */ + if (! shared) { + if (awe_create_sf(parm.type, parm.name)) { + printk(KERN_ERR "AWE32: can't open: failed to alloc new list\n"); + return -ENOMEM; + } + } + patch_opened = TRUE; + return current_sf_id; +} + +/* check if the patch is already opened */ +static sf_list * +check_patch_opened(int type, char *name) +{ + if (! patch_opened) { + if (awe_create_sf(type, name)) { + printk(KERN_ERR "AWE32: failed to alloc new list\n"); + return NULL; + } + patch_opened = TRUE; + return sftail; + } + return sftail; +} + +/* close the patch; if no voice is loaded, remove the patch */ +static int +awe_close_patch(awe_patch_info *patch, const char __user *addr, int count) +{ + if (patch_opened && sftail) { + /* if no voice is loaded, release the current patch */ + if (sftail->infos == NULL) { + awe_reset(0); + awe_remove_samples(current_sf_id - 1); + } + } + patch_opened = 0; + return 0; +} + + +/* remove the latest patch */ +static int +awe_unload_patch(awe_patch_info *patch, const char __user *addr, int count) +{ + if (current_sf_id > 0 && current_sf_id > locked_sf_id) { + awe_reset(0); + awe_remove_samples(current_sf_id - 1); + } + return 0; +} + +/* allocate voice info list records */ +static awe_voice_list * +alloc_new_info(void) +{ + awe_voice_list *newlist; + + newlist = kmalloc(sizeof(*newlist), GFP_KERNEL); + if (newlist == NULL) { + printk(KERN_ERR "AWE32: can't alloc info table\n"); + return NULL; + } + return newlist; +} + +/* allocate sample info list records */ +static awe_sample_list * +alloc_new_sample(void) +{ + awe_sample_list *newlist; + + newlist = (awe_sample_list *)kmalloc(sizeof(*newlist), GFP_KERNEL); + if (newlist == NULL) { + printk(KERN_ERR "AWE32: can't alloc sample table\n"); + return NULL; + } + return newlist; +} + +/* load voice map */ +static int +awe_load_map(awe_patch_info *patch, const char __user *addr, int count) +{ + awe_voice_map map; + awe_voice_list *rec, *p; + sf_list *sf; + + /* get the link info */ + if (count < sizeof(map)) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) + return -EFAULT; + + /* check if the identical mapping already exists */ + p = awe_search_instr(map.map_bank, map.map_instr, map.map_key); + for (; p; p = p->next_instr) { + if (p->type == V_ST_MAPPED && + p->v.start == map.src_instr && + p->v.end == map.src_bank && + p->v.fixkey == map.src_key) + return 0; /* already present! */ + } + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MAP, NULL)) == NULL) + return -ENOMEM; + + if ((rec = alloc_new_info()) == NULL) + return -ENOMEM; + + rec->bank = map.map_bank; + rec->instr = map.map_instr; + rec->type = V_ST_MAPPED; + rec->disabled = FALSE; + awe_init_voice_info(&rec->v); + if (map.map_key >= 0) { + rec->v.low = map.map_key; + rec->v.high = map.map_key; + } + rec->v.start = map.src_instr; + rec->v.end = map.src_bank; + rec->v.fixkey = map.src_key; + add_sf_info(sf, rec); + add_info_list(rec); + + return 0; +} + +#if 0 +/* probe preset in the current list -- nothing to be loaded */ +static int +awe_probe_info(awe_patch_info *patch, const char __user *addr, int count) +{ +#ifdef AWE_ALLOW_SAMPLE_SHARING + awe_voice_map map; + awe_voice_list *p; + + if (! patch_opened) + return -EINVAL; + + /* get the link info */ + if (count < sizeof(map)) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + if (copy_from_user(&map, addr + AWE_PATCH_INFO_SIZE, sizeof(map))) + return -EFAULT; + + /* check if the identical mapping already exists */ + if (sftail == NULL) + return -EINVAL; + p = awe_search_instr(map.src_bank, map.src_instr, map.src_key); + for (; p; p = p->next_instr) { + if (p->type == V_ST_NORMAL && + is_identical_holder(p->holder, sftail) && + p->v.low <= map.src_key && + p->v.high >= map.src_key) + return 0; /* already present! */ + } +#endif /* allow sharing */ + return -EINVAL; +} +#endif + +/* probe sample in the current list -- nothing to be loaded */ +static int +awe_probe_data(awe_patch_info *patch, const char __user *addr, int count) +{ +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (! patch_opened) + return -EINVAL; + + /* search the specified sample by optarg */ + if (search_sample_index(sftail, patch->optarg) != NULL) + return 0; +#endif /* allow sharing */ + return -EINVAL; +} + + +/* remove the present instrument layers */ +static int +remove_info(sf_list *sf, int bank, int instr) +{ + awe_voice_list *prev, *next, *p; + int removed = 0; + + prev = NULL; + for (p = sf->infos; p; p = next) { + next = p->next; + if (p->type == V_ST_NORMAL && + p->bank == bank && p->instr == instr) { + /* remove this layer */ + if (prev) + prev->next = next; + else + sf->infos = next; + if (p == sf->last_infos) + sf->last_infos = prev; + sf->num_info--; + removed++; + kfree(p); + } else + prev = p; + } + if (removed) + rebuild_preset_list(); + return removed; +} + +/* load voice information data */ +static int +awe_load_info(awe_patch_info *patch, const char __user *addr, int count) +{ + int offset; + awe_voice_rec_hdr hdr; + int i; + int total_size; + sf_list *sf; + awe_voice_list *rec; + + if (count < AWE_VOICE_REC_SIZE) { + printk(KERN_WARNING "AWE32 Error: invalid patch info length\n"); + return -EINVAL; + } + + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user((char*)&hdr, addr + offset, AWE_VOICE_REC_SIZE)) + return -EFAULT; + offset += AWE_VOICE_REC_SIZE; + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk(KERN_WARNING "AWE32 Error: Invalid voice number %d\n", hdr.nvoices); + return -EINVAL; + } + total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices; + if (count < total_size) { + printk(KERN_WARNING "AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return -EINVAL; + } + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) + return -ENOMEM; + + switch (hdr.write_mode) { + case AWE_WR_EXCLUSIVE: + /* exclusive mode - if the instrument already exists, + return error */ + for (rec = sf->infos; rec; rec = rec->next) { + if (rec->type == V_ST_NORMAL && + rec->bank == hdr.bank && + rec->instr == hdr.instr) + return -EINVAL; + } + break; + case AWE_WR_REPLACE: + /* replace mode - remove the instrument if it already exists */ + remove_info(sf, hdr.bank, hdr.instr); + break; + } + + /* append new layers */ + for (i = 0; i < hdr.nvoices; i++) { + rec = alloc_new_info(); + if (rec == NULL) + return -ENOMEM; + + rec->bank = hdr.bank; + rec->instr = hdr.instr; + rec->type = V_ST_NORMAL; + rec->disabled = FALSE; + + /* copy awe_voice_info parameters */ + if (copy_from_user(&rec->v, addr + offset, AWE_VOICE_INFO_SIZE)) { + kfree(rec); + return -EFAULT; + } + offset += AWE_VOICE_INFO_SIZE; +#ifdef AWE_ALLOW_SAMPLE_SHARING + if (sf && sf->shared) { + if (info_duplicated(sf, rec)) { + kfree(rec); + continue; + } + } +#endif /* allow sharing */ + if (rec->v.mode & AWE_MODE_INIT_PARM) + awe_init_voice_parm(&rec->v.parm); + add_sf_info(sf, rec); + awe_set_sample(rec); + add_info_list(rec); + } + + return 0; +} + + +/* remove instrument layers */ +static int +awe_remove_info(awe_patch_info *patch, const char __user *addr, int count) +{ + unsigned char bank, instr; + sf_list *sf; + + if (! patch_opened || (sf = sftail) == NULL) { + printk(KERN_WARNING "AWE32: remove_info: patch not opened\n"); + return -EINVAL; + } + + bank = ((unsigned short)patch->optarg >> 8) & 0xff; + instr = (unsigned short)patch->optarg & 0xff; + if (! remove_info(sf, bank, instr)) + return -EINVAL; + return 0; +} + + +/* load wave sample data */ +static int +awe_load_data(awe_patch_info *patch, const char __user *addr, int count) +{ + int offset, size; + int rc; + awe_sample_info tmprec; + awe_sample_list *rec; + sf_list *sf; + + if ((sf = check_patch_opened(AWE_PAT_TYPE_MISC, NULL)) == NULL) + return -ENOMEM; + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user(&tmprec, addr + offset, AWE_SAMPLE_INFO_SIZE)) + return -EFAULT; + offset += AWE_SAMPLE_INFO_SIZE; + if (size != tmprec.size) { + printk(KERN_WARNING "AWE32: load: sample size differed (%d != %d)\n", + tmprec.size, size); + return -EINVAL; + } + + if (search_sample_index(sf, tmprec.sample) != NULL) { +#ifdef AWE_ALLOW_SAMPLE_SHARING + /* if shared sample, skip this data */ + if (sf->type & AWE_PAT_SHARED) + return 0; +#endif /* allow sharing */ + DEBUG(1,printk("AWE32: sample data %d already present\n", tmprec.sample)); + return -EINVAL; + } + + if ((rec = alloc_new_sample()) == NULL) + return -ENOMEM; + + memcpy(&rec->v, &tmprec, sizeof(tmprec)); + + if (rec->v.size > 0) { + if ((rc = awe_write_wave_data(addr, offset, rec, -1)) < 0) { + kfree(rec); + return rc; + } + sf->mem_ptr += rc; + } + + add_sf_sample(sf, rec); + return 0; +} + + +/* replace wave sample data */ +static int +awe_replace_data(awe_patch_info *patch, const char __user *addr, int count) +{ + int offset; + int size; + int rc; + int channels; + awe_sample_info cursmp; + int save_mem_ptr; + sf_list *sf; + awe_sample_list *rec; + + if (! patch_opened || (sf = sftail) == NULL) { + printk(KERN_WARNING "AWE32: replace: patch not opened\n"); + return -EINVAL; + } + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + if (copy_from_user(&cursmp, addr + offset, AWE_SAMPLE_INFO_SIZE)) + return -EFAULT; + offset += AWE_SAMPLE_INFO_SIZE; + if (cursmp.size == 0 || size != cursmp.size) { + printk(KERN_WARNING "AWE32: replace: invalid sample size (%d!=%d)\n", + cursmp.size, size); + return -EINVAL; + } + channels = patch->optarg; + if (channels <= 0 || channels > AWE_NORMAL_VOICES) { + printk(KERN_WARNING "AWE32: replace: invalid channels %d\n", channels); + return -EINVAL; + } + + for (rec = sf->samples; rec; rec = rec->next) { + if (rec->v.sample == cursmp.sample) + break; + } + if (rec == NULL) { + printk(KERN_WARNING "AWE32: replace: cannot find existing sample data %d\n", + cursmp.sample); + return -EINVAL; + } + + if (rec->v.size != cursmp.size) { + printk(KERN_WARNING "AWE32: replace: exiting size differed (%d!=%d)\n", + rec->v.size, cursmp.size); + return -EINVAL; + } + + save_mem_ptr = awe_free_mem_ptr(); + sftail->mem_ptr = rec->v.start - awe_mem_start; + memcpy(&rec->v, &cursmp, sizeof(cursmp)); + rec->v.sf_id = current_sf_id; + if ((rc = awe_write_wave_data(addr, offset, rec, channels)) < 0) + return rc; + sftail->mem_ptr = save_mem_ptr; + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static const char __user *readbuf_addr; +static int readbuf_offs; +static int readbuf_flags; + +/* initialize read buffer */ +static int +readbuf_init(const char __user *addr, int offset, awe_sample_info *sp) +{ + readbuf_addr = addr; + readbuf_offs = offset; + readbuf_flags = sp->mode_flags; + return 0; +} + +/* read directly from user buffer */ +static unsigned short +readbuf_word(int pos) +{ + unsigned short c; + /* read from user buffer */ + if (readbuf_flags & AWE_SAMPLE_8BITS) { + unsigned char cc; + get_user(cc, (unsigned char __user *)(readbuf_addr + readbuf_offs + pos)); + c = (unsigned short)cc << 8; /* convert 8bit -> 16bit */ + } else { + get_user(c, (unsigned short __user *)(readbuf_addr + readbuf_offs + pos * 2)); + } + if (readbuf_flags & AWE_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ + return c; +} + +#define readbuf_word_cache readbuf_word +#define readbuf_end() /**/ + +/*----------------------------------------------------------------*/ + +#define BLANK_LOOP_START 8 +#define BLANK_LOOP_END 40 +#define BLANK_LOOP_SIZE 48 + +/* loading onto memory - return the actual written size */ +static int +awe_write_wave_data(const char __user *addr, int offset, awe_sample_list *list, int channels) +{ + int i, truesize, dram_offset; + awe_sample_info *sp = &list->v; + int rc; + + /* be sure loop points start < end */ + if (sp->loopstart > sp->loopend) { + int tmp = sp->loopstart; + sp->loopstart = sp->loopend; + sp->loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->size; + if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) + truesize += sp->loopend - sp->loopstart; + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + if (awe_free_mem_ptr() + truesize >= memsize/2) { + DEBUG(-1,printk("AWE32 Error: Sample memory full\n")); + return -ENOSPC; + } + + /* recalculate address offset */ + sp->end -= sp->start; + sp->loopstart -= sp->start; + sp->loopend -= sp->start; + + dram_offset = awe_free_mem_ptr() + awe_mem_start; + sp->start = dram_offset; + sp->end += dram_offset; + sp->loopstart += dram_offset; + sp->loopend += dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + if (sp->size == 0) + sp->checksum = 0; + else + sp->checksum = truesize; + + if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0) + return rc; + + if (readbuf_init(addr, offset, sp) < 0) + return -ENOSPC; + + for (i = 0; i < sp->size; i++) { + unsigned short c; + c = readbuf_word(i); + awe_write_dram(c); + if (i == sp->loopend && + (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) { + int looplen = sp->loopend - sp->loopstart; + /* copy reverse loop */ + int k; + for (k = 1; k <= looplen; k++) { + c = readbuf_word_cache(i - k); + awe_write_dram(c); + } + if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) { + sp->end += looplen; + } else { + sp->start += looplen; + sp->end += looplen; + } + } + } + readbuf_end(); + + /* if no blank loop is attached in the sample, add it */ + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) + awe_write_dram(0); + if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) { + sp->loopstart = sp->end + BLANK_LOOP_START; + sp->loopend = sp->end + BLANK_LOOP_END; + } + } + + awe_close_dram(); + + /* initialize FM */ + awe_init_fm(); + + return truesize; +} + + +/*----------------------------------------------------------------*/ + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* calculate GUS envelope time: + * is this correct? i have no idea.. + */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2]) +#define calc_gus_attenuation(val) vol_table[(val)/2] + +/* load GUS patch */ +static int +awe_load_guspatch(const char __user *addr, int offs, int size, int pmgr_flag) +{ + struct patch_info patch; + awe_voice_info *rec; + awe_sample_info *smp; + awe_voice_list *vrec; + awe_sample_list *smprec; + int sizeof_patch; + int note, rc; + sf_list *sf; + + sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ + if (size < sizeof_patch) { + printk(KERN_WARNING "AWE32 Error: Patch header too short\n"); + return -EINVAL; + } + if (copy_from_user(((char*)&patch) + offs, addr + offs, sizeof_patch - offs)) + return -EFAULT; + size -= sizeof_patch; + if (size < patch.len) { + printk(KERN_WARNING "AWE32 Error: Patch record too short (%d<%d)\n", + size, patch.len); + return -EINVAL; + } + if ((sf = check_patch_opened(AWE_PAT_TYPE_GUS, NULL)) == NULL) + return -ENOMEM; + if ((smprec = alloc_new_sample()) == NULL) + return -ENOMEM; + if ((vrec = alloc_new_info()) == NULL) { + kfree(smprec); + return -ENOMEM; + } + + smp = &smprec->v; + smp->sample = sf->num_sample; + smp->start = 0; + smp->end = patch.len; + smp->loopstart = patch.loop_start; + smp->loopend = patch.loop_end; + smp->size = patch.len; + + /* set up mode flags */ + smp->mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->mode_flags |= AWE_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->mode_flags |= AWE_SAMPLE_UNSIGNED; + smp->mode_flags |= AWE_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->mode_flags |= AWE_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP; + + DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags)); + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->size /= 2; + smp->end /= 2; + smp->loopstart /= 2; + smp->loopend /= 2; + } + smp->checksum_flag = 0; + smp->checksum = 0; + + if ((rc = awe_write_wave_data(addr, sizeof_patch, smprec, -1)) < 0) { + kfree(vrec); + return rc; + } + sf->mem_ptr += rc; + add_sf_sample(sf, smprec); + + /* set up voice info */ + rec = &vrec->v; + awe_init_voice_info(rec); + rec->sample = sf->num_info; /* the last sample */ + rec->rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + rec->root = note / 100; + rec->tune = -(note % 100); + rec->low = freq_to_note(patch.low_note) / 100; + rec->high = freq_to_note(patch.high_note) / 100; + DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n", + rec->rate_offset, note, + rec->low, rec->high, + patch.low_note, patch.high_note)); + /* panning position; -128 - 127 => 0-127 */ + rec->pan = (patch.panning + 128) / 2; + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + rec->parm.volatkhld = (calc_parm_hold(hold) << 8) | + calc_parm_attack(attack); + rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + calc_parm_decay(decay); + rec->parm.volrelease = 0x8000 | calc_parm_decay(release); + DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release)); + rec->attenuation = calc_gus_attenuation(patch.env_offset[0]); + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + /* append to the tail of the list */ + vrec->bank = ctrls[AWE_MD_GUS_BANK]; + vrec->instr = patch.instr_no; + vrec->disabled = FALSE; + vrec->type = V_ST_NORMAL; + + add_sf_info(sf, vrec); + add_info_list(vrec); + + /* set the voice index */ + awe_set_sample(vrec); + + return 0; +} + +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + +/* + * sample and voice list handlers + */ + +/* append this to the current sf list */ +static void add_sf_info(sf_list *sf, awe_voice_list *rec) +{ + if (sf == NULL) + return; + rec->holder = sf; + rec->v.sf_id = sf->sf_id; + if (sf->last_infos) + sf->last_infos->next = rec; + else + sf->infos = rec; + sf->last_infos = rec; + rec->next = NULL; + sf->num_info++; +} + +/* prepend this sample to sf list */ +static void add_sf_sample(sf_list *sf, awe_sample_list *rec) +{ + if (sf == NULL) + return; + rec->holder = sf; + rec->v.sf_id = sf->sf_id; + if (sf->last_samples) + sf->last_samples->next = rec; + else + sf->samples = rec; + sf->last_samples = rec; + rec->next = NULL; + sf->num_sample++; +} + +/* purge the old records which don't belong with the same file id */ +static void purge_old_list(awe_voice_list *rec, awe_voice_list *next) +{ + rec->next_instr = next; + if (rec->bank == AWE_DRUM_BANK) { + /* remove samples with the same note range */ + awe_voice_list *cur, *prev = rec; + int low = rec->v.low; + int high = rec->v.high; + for (cur = next; cur; cur = cur->next_instr) { + if (cur->v.low == low && + cur->v.high == high && + ! is_identical_holder(cur->holder, rec->holder)) + prev->next_instr = cur->next_instr; + else + prev = cur; + } + } else { + if (! is_identical_holder(next->holder, rec->holder)) + /* remove all samples */ + rec->next_instr = NULL; + } +} + +/* prepend to top of the preset table */ +static void add_info_list(awe_voice_list *rec) +{ + awe_voice_list *prev, *cur; + int key; + + if (rec->disabled) + return; + + key = awe_search_key(rec->bank, rec->instr, rec->v.low); + prev = NULL; + for (cur = preset_table[key]; cur; cur = cur->next_bank) { + /* search the first record with the same bank number */ + if (cur->instr == rec->instr && cur->bank == rec->bank) { + /* replace the list with the new record */ + rec->next_bank = cur->next_bank; + if (prev) + prev->next_bank = rec; + else + preset_table[key] = rec; + purge_old_list(rec, cur); + return; + } + prev = cur; + } + + /* this is the first bank record.. just add this */ + rec->next_instr = NULL; + rec->next_bank = preset_table[key]; + preset_table[key] = rec; +} + +/* remove samples later than the specified sf_id */ +static void +awe_remove_samples(int sf_id) +{ + sf_list *p, *prev; + + if (sf_id <= 0) { + awe_reset_samples(); + return; + } + /* already removed? */ + if (current_sf_id <= sf_id) + return; + + for (p = sftail; p; p = prev) { + if (p->sf_id <= sf_id) + break; + prev = p->prev; + awe_free_sf(p); + } + sftail = p; + if (sftail) { + sf_id = sftail->sf_id; + sftail->next = NULL; + } else { + sf_id = 0; + sfhead = NULL; + } + current_sf_id = sf_id; + if (locked_sf_id > sf_id) + locked_sf_id = sf_id; + + rebuild_preset_list(); +} + +/* rebuild preset search list */ +static void rebuild_preset_list(void) +{ + sf_list *p; + awe_voice_list *rec; + + memset(preset_table, 0, sizeof(preset_table)); + + for (p = sfhead; p; p = p->next) { + for (rec = p->infos; rec; rec = rec->next) + add_info_list(rec); + } +} + +/* compare the given sf_id pair */ +static int is_identical_holder(sf_list *sf1, sf_list *sf2) +{ + if (sf1 == NULL || sf2 == NULL) + return FALSE; + if (sf1 == sf2) + return TRUE; +#ifdef AWE_ALLOW_SAMPLE_SHARING + { + /* compare with the sharing id */ + sf_list *p; + int counter = 0; + if (sf1->sf_id < sf2->sf_id) { /* make sure id1 > id2 */ + sf_list *tmp; tmp = sf1; sf1 = sf2; sf2 = tmp; + } + for (p = sf1->shared; p; p = p->shared) { + if (counter++ > current_sf_id) + break; /* strange sharing loop.. quit */ + if (p == sf2) + return TRUE; + } + } +#endif /* allow sharing */ + return FALSE; +} + +/* search the sample index matching with the given sample id */ +static awe_sample_list * +search_sample_index(sf_list *sf, int sample) +{ + awe_sample_list *p; +#ifdef AWE_ALLOW_SAMPLE_SHARING + int counter = 0; + while (sf) { + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample) + return p; + } + sf = sf->shared; + if (counter++ > current_sf_id) + break; /* strange sharing loop.. quit */ + } +#else + if (sf) { + for (p = sf->samples; p; p = p->next) { + if (p->v.sample == sample) + return p; + } + } +#endif + return NULL; +} + +/* search the specified sample */ +/* non-zero = found */ +static short +awe_set_sample(awe_voice_list *rec) +{ + awe_sample_list *smp; + awe_voice_info *vp = &rec->v; + + vp->index = 0; + if ((smp = search_sample_index(rec->holder, vp->sample)) == NULL) + return 0; + + /* set the actual sample offsets */ + vp->start += smp->v.start; + vp->end += smp->v.end; + vp->loopstart += smp->v.loopstart; + vp->loopend += smp->v.loopend; + /* copy mode flags */ + vp->mode = smp->v.mode_flags; + /* set flag */ + vp->index = 1; + + return 1; +} + + +/* + * voice allocation + */ + +/* look for all voices associated with the specified note & velocity */ +static int +awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, + awe_voice_info **vlist) +{ + int nvoices; + + nvoices = 0; + for (; rec; rec = rec->next_instr) { + if (note >= rec->v.low && + note <= rec->v.high && + velocity >= rec->v.vellow && + velocity <= rec->v.velhigh) { + if (rec->type == V_ST_MAPPED) { + /* mapper */ + vlist[0] = &rec->v; + return -1; + } + vlist[nvoices++] = &rec->v; + if (nvoices >= AWE_MAX_VOICES) + break; + } + } + return nvoices; +} + +/* store the voice list from the specified note and velocity. + if the preset is mapped, seek for the destination preset, and rewrite + the note number if necessary. + */ +static int +really_alloc_voices(int bank, int instr, int *note, int velocity, awe_voice_info **vlist) +{ + int nvoices; + awe_voice_list *vrec; + int level = 0; + + for (;;) { + vrec = awe_search_instr(bank, instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + if (nvoices == 0) { + if (bank == AWE_DRUM_BANK) + /* search default drumset */ + vrec = awe_search_instr(bank, ctrls[AWE_MD_DEF_DRUM], *note); + else + /* search default preset */ + vrec = awe_search_instr(ctrls[AWE_MD_DEF_BANK], instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + } + if (nvoices == 0) { + if (bank == AWE_DRUM_BANK && ctrls[AWE_MD_DEF_DRUM] != 0) + /* search default drumset */ + vrec = awe_search_instr(bank, 0, *note); + else if (bank != AWE_DRUM_BANK && ctrls[AWE_MD_DEF_BANK] != 0) + /* search default preset */ + vrec = awe_search_instr(0, instr, *note); + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + } + if (nvoices < 0) { /* mapping */ + int key = vlist[0]->fixkey; + instr = vlist[0]->start; + bank = vlist[0]->end; + if (level++ > 5) { + printk(KERN_ERR "AWE32: too deep mapping level\n"); + return 0; + } + if (key >= 0) + *note = key; + } else + break; + } + + return nvoices; +} + +/* allocate voices corresponding note and velocity; supports multiple insts. */ +static void +awe_alloc_multi_voices(int ch, int note, int velocity, int key) +{ + int i, v, nvoices, bank; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) + bank = AWE_DRUM_BANK; /* always search drumset */ + else + bank = channels[ch].bank; + + /* check the possible voices; note may be changeable if mapped */ + nvoices = really_alloc_voices(bank, channels[ch].instr, + ¬e, velocity, vlist); + + /* set the voices */ + current_alloc_time++; + for (i = 0; i < nvoices; i++) { + v = awe_clear_voice(); + voices[v].key = key; + voices[v].ch = ch; + voices[v].note = note; + voices[v].velocity = velocity; + voices[v].time = current_alloc_time; + voices[v].cinfo = &channels[ch]; + voices[v].sample = vlist[i]; + voices[v].state = AWE_ST_MARK; + voices[v].layer = nvoices - i - 1; /* in reverse order */ + } + + /* clear the mark in allocated voices */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state == AWE_ST_MARK) + voices[i].state = AWE_ST_OFF; + + } +} + + +/* search an empty voice. + if no empty voice is found, at least terminate a voice + */ +static int +awe_clear_voice(void) +{ + enum { + OFF=0, RELEASED, SUSTAINED, PLAYING, END + }; + struct voice_candidate_t { + int best; + int time; + int vtarget; + } candidate[END]; + int i, type, vtarget; + + vtarget = 0xffff; + for (type = OFF; type < END; type++) { + candidate[type].best = -1; + candidate[type].time = current_alloc_time + 1; + candidate[type].vtarget = vtarget; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state & AWE_ST_OFF) + type = OFF; + else if (voices[i].state & AWE_ST_RELEASED) + type = RELEASED; + else if (voices[i].state & AWE_ST_SUSTAINED) + type = SUSTAINED; + else if (voices[i].state & ~AWE_ST_MARK) + type = PLAYING; + else + continue; +#ifdef AWE_CHECK_VTARGET + /* get current volume */ + vtarget = (awe_peek_dw(AWE_VTFT(i)) >> 16) & 0xffff; +#endif + if (candidate[type].best < 0 || + vtarget < candidate[type].vtarget || + (vtarget == candidate[type].vtarget && + voices[i].time < candidate[type].time)) { + candidate[type].best = i; + candidate[type].time = voices[i].time; + candidate[type].vtarget = vtarget; + } + } + + for (type = OFF; type < END; type++) { + if ((i = candidate[type].best) >= 0) { + if (voices[i].state != AWE_ST_OFF) + awe_terminate(i); + awe_voice_init(i, TRUE); + return i; + } + } + return 0; +} + + +/* search sample for the specified note & velocity and set it on the voice; + * note that voice is the voice index (not channel index) + */ +static void +awe_alloc_one_voice(int voice, int note, int velocity) +{ + int ch, nvoices, bank; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + ch = voices[voice].ch; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice)) + bank = AWE_DRUM_BANK; /* always search drumset */ + else + bank = voices[voice].cinfo->bank; + + nvoices = really_alloc_voices(bank, voices[voice].cinfo->instr, + ¬e, velocity, vlist); + if (nvoices > 0) { + voices[voice].time = ++current_alloc_time; + voices[voice].sample = vlist[0]; /* use the first one */ + voices[voice].layer = 0; + voices[voice].note = note; + voices[voice].velocity = velocity; + } +} + + +/* + * sequencer2 functions + */ + +/* search an empty voice; used by sequencer2 */ +static int +awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + playing_mode = AWE_PLAY_MULTI2; + awe_info.nr_voices = AWE_MAX_CHANNELS; + return awe_clear_voice(); +} + + +/* set up voice; used by sequencer2 */ +static void +awe_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info; + if (synth_devs[dev] == NULL || + (info = &synth_devs[dev]->chn_info[chn]) == NULL) + return; + + if (voice < 0 || voice >= awe_max_voices) + return; + + DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn)); + channels[chn].expression_vol = info->controllers[CTL_EXPRESSION]; + channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME]; + channels[chn].panning = + info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */ + channels[chn].bender = info->bender_value; /* zero center */ + channels[chn].bank = info->controllers[CTL_BANK_SELECT]; + channels[chn].sustained = info->controllers[CTL_SUSTAIN]; + if (info->controllers[CTL_EXT_EFF_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_REVERB, + info->controllers[CTL_EXT_EFF_DEPTH] * 2); + } + if (info->controllers[CTL_CHORUS_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_CHORUS, + info->controllers[CTL_CHORUS_DEPTH] * 2); + } + awe_set_instr(dev, chn, info->pgm_num); +} + + +#ifdef CONFIG_AWE32_MIXER +/* + * AWE32 mixer device control + */ + +static int awe_mixer_ioctl(int dev, unsigned int cmd, void __user *arg); + +static int my_mixerdev = -1; + +static struct mixer_operations awe_mixer_operations = { + .owner = THIS_MODULE, + .id = "AWE", + .name = "AWE32 Equalizer", + .ioctl = awe_mixer_ioctl, +}; + +static void __init attach_mixer(void) +{ + if ((my_mixerdev = sound_alloc_mixerdev()) >= 0) { + mixer_devs[my_mixerdev] = &awe_mixer_operations; + } +} + +static void unload_mixer(void) +{ + if (my_mixerdev >= 0) + sound_unload_mixerdev(my_mixerdev); +} + +static int +awe_mixer_ioctl(int dev, unsigned int cmd, void __user * arg) +{ + int i, level, value; + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (get_user(level, (int __user *)arg)) + return -EFAULT; + level = ((level & 0xff) + (level >> 8)) / 2; + DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + value = level * 12 / 100; + if (value >= 12) + value = 11; + ctrls[AWE_MD_BASS_LEVEL] = value; + awe_update_equalizer(); + break; + case SOUND_MIXER_TREBLE: + value = level * 12 / 100; + if (value >= 12) + value = 11; + ctrls[AWE_MD_TREBLE_LEVEL] = value; + awe_update_equalizer(); + break; + case SOUND_MIXER_VOLUME: + level = level * 127 / 100; + if (level >= 128) level = 127; + atten_relative = FALSE; + atten_offset = vol_table[level]; + awe_update_volume(); + break; + } + } + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + level = ctrls[AWE_MD_BASS_LEVEL] * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_TREBLE: + level = ctrls[AWE_MD_TREBLE_LEVEL] * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_VOLUME: + value = atten_offset; + if (atten_relative) + value += ctrls[AWE_MD_ZERO_ATTEN]; + for (i = 127; i > 0; i--) { + if (value <= vol_table[i]) + break; + } + level = i * 100 / 127; + level = (level << 8) | level; + break; + case SOUND_MIXER_DEVMASK: + level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME; + break; + default: + level = 0; + break; + } + if (put_user(level, (int __user *)arg)) + return -EFAULT; + return level; +} +#endif /* CONFIG_AWE32_MIXER */ + + +/* + * initialization of Emu8000 + */ + +/* intiailize audio channels */ +static void +awe_init_audio(void) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_DCYSUSV(ch), 0x80); + } + + /* reset all other parameters to zero */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_ENVVOL(ch), 0); + awe_poke(AWE_ENVVAL(ch), 0); + awe_poke(AWE_DCYSUS(ch), 0); + awe_poke(AWE_ATKHLDV(ch), 0); + awe_poke(AWE_LFO1VAL(ch), 0); + awe_poke(AWE_ATKHLD(ch), 0); + awe_poke(AWE_LFO2VAL(ch), 0); + awe_poke(AWE_IP(ch), 0); + awe_poke(AWE_IFATN(ch), 0); + awe_poke(AWE_PEFE(ch), 0); + awe_poke(AWE_FMMOD(ch), 0); + awe_poke(AWE_TREMFRQ(ch), 0); + awe_poke(AWE_FM2FRQ2(ch), 0); + awe_poke_dw(AWE_PTRX(ch), 0); + awe_poke_dw(AWE_VTFT(ch), 0); + awe_poke_dw(AWE_PSST(ch), 0); + awe_poke_dw(AWE_CSL(ch), 0); + awe_poke_dw(AWE_CCCA(ch), 0); + } + + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke_dw(AWE_CPF(ch), 0); + awe_poke_dw(AWE_CVCF(ch), 0); + } +} + + +/* initialize DMA address */ +static void +awe_init_dma(void) +{ + awe_poke_dw(AWE_SMALR, 0); + awe_poke_dw(AWE_SMARR, 0); + awe_poke_dw(AWE_SMALW, 0); + awe_poke_dw(AWE_SMARW, 0); +} + + +/* initialization arrays; from ADIP */ + +static unsigned short init1[128] = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + + +/* send initialization arrays to start up */ +static void +awe_init_array(void) +{ + awe_send_array(init1); + awe_wait(1024); + awe_send_array(init2); + awe_send_array(init3); + awe_poke_dw(AWE_HWCF4, 0); + awe_poke_dw(AWE_HWCF5, 0x83); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_send_array(init4); +} + +/* send an initialization array */ +static void +awe_send_array(unsigned short *data) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT1(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT2(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT3(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT4(i), *p); +} + + +/* + * set up awe32 channels to some known state. + */ + +/* set the envelope & LFO parameters to the default values; see ADIP */ +static void +awe_tweak_voice(int i) +{ + /* set all mod/vol envelope shape to minimum */ + awe_poke(AWE_ENVVOL(i), 0x8000); + awe_poke(AWE_ENVVAL(i), 0x8000); + awe_poke(AWE_DCYSUS(i), 0x7F7F); + awe_poke(AWE_ATKHLDV(i), 0x7F7F); + awe_poke(AWE_ATKHLD(i), 0x7F7F); + awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */ + awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */ + awe_poke(AWE_LFO2VAL(i), 0x8000); + awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */ + awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */ + awe_poke(AWE_FMMOD(i), 0); + awe_poke(AWE_TREMFRQ(i), 0); + awe_poke(AWE_FM2FRQ2(i), 0); +} + +static void +awe_tweak(void) +{ + int i; + /* reset all channels */ + for (i = 0; i < awe_max_voices; i++) + awe_tweak_voice(i); +} + + +/* + * initializes the FM section of AWE32; + * see Vince Vu's unofficial AWE32 programming guide + */ + +static void +awe_init_fm(void) +{ +#ifndef AWE_ALWAYS_INIT_FM + /* if no extended memory is on board.. */ + if (memsize <= 0) + return; +#endif + DEBUG(3,printk("AWE32: initializing FM\n")); + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + awe_poke(AWE_DCYSUSV(30), 0x80); + awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */ + awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(30), 0); + awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + awe_poke(AWE_DCYSUSV(31), 0x80); + awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */ + awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(31), 0x8000); + awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3); + + /* skew volume & cutoff */ + awe_poke_dw(AWE_VTFT(30), 0x8000FFFF); + awe_poke_dw(AWE_VTFT(31), 0x8000FFFF); + + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* change maximum channels to 30 */ + awe_max_voices = AWE_NORMAL_VOICES; + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + voice_alloc->max_voice = awe_max_voices; +} + +/* + * AWE32 DRAM access routines + */ + +/* open DRAM write accessing mode */ +static int +awe_open_dram_for_write(int offset, int channels) +{ + int vidx[AWE_NORMAL_VOICES]; + int i; + + if (channels < 0 || channels >= AWE_NORMAL_VOICES) { + channels = AWE_NORMAL_VOICES; + for (i = 0; i < AWE_NORMAL_VOICES; i++) + vidx[i] = i; + } else { + for (i = 0; i < channels; i++) { + vidx[i] = awe_clear_voice(); + voices[vidx[i]].state = AWE_ST_MARK; + } + } + + /* use all channels for DMA transfer */ + for (i = 0; i < channels; i++) { + if (vidx[i] < 0) continue; + awe_poke(AWE_DCYSUSV(vidx[i]), 0x80); + awe_poke_dw(AWE_VTFT(vidx[i]), 0); + awe_poke_dw(AWE_CVCF(vidx[i]), 0); + awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000); + awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000); + awe_poke_dw(AWE_PSST(vidx[i]), 0); + awe_poke_dw(AWE_CSL(vidx[i]), 0); + awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000); + voices[vidx[i]].state = AWE_ST_DRAM; + } + /* point channels 31 & 32 to ROM samples for DRAM refresh */ + awe_poke_dw(AWE_VTFT(30), 0); + awe_poke_dw(AWE_PSST(30), 0x1d8); + awe_poke_dw(AWE_CSL(30), 0x1e0); + awe_poke_dw(AWE_CCCA(30), 0x1d8); + awe_poke_dw(AWE_VTFT(31), 0); + awe_poke_dw(AWE_PSST(31), 0x1d8); + awe_poke_dw(AWE_CSL(31), 0x1e0); + awe_poke_dw(AWE_CCCA(31), 0x1d8); + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* if full bit is on, not ready to write on */ + if (awe_peek_dw(AWE_SMALW) & 0x80000000) { + for (i = 0; i < channels; i++) { + awe_poke_dw(AWE_CCCA(vidx[i]), 0); + voices[vidx[i]].state = AWE_ST_OFF; + } + printk("awe: not ready to write..\n"); + return -EPERM; + } + + /* set address to write */ + awe_poke_dw(AWE_SMALW, offset); + + return 0; +} + +/* open DRAM for RAM size detection */ +static void +awe_open_dram_for_check(void) +{ + int i; + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + awe_poke(AWE_DCYSUSV(i), 0x80); + awe_poke_dw(AWE_VTFT(i), 0); + awe_poke_dw(AWE_CVCF(i), 0); + awe_poke_dw(AWE_PTRX(i), 0x40000000); + awe_poke_dw(AWE_CPF(i), 0x40000000); + awe_poke_dw(AWE_PSST(i), 0); + awe_poke_dw(AWE_CSL(i), 0); + if (i & 1) /* DMA write */ + awe_poke_dw(AWE_CCCA(i), 0x06000000); + else /* DMA read */ + awe_poke_dw(AWE_CCCA(i), 0x04000000); + voices[i].state = AWE_ST_DRAM; + } +} + + +/* close dram access */ +static void +awe_close_dram(void) +{ + int i; + /* wait until FULL bit in SMAxW register be false */ + for (i = 0; i < 10000; i++) { + if (!(awe_peek_dw(AWE_SMALW) & 0x80000000)) + break; + awe_wait(10); + } + + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + if (voices[i].state == AWE_ST_DRAM) { + awe_poke_dw(AWE_CCCA(i), 0); + awe_poke(AWE_DCYSUSV(i), 0x807F); + voices[i].state = AWE_ST_OFF; + } + } +} + + +/* + * check dram size on AWE board + */ + +/* any three numbers you like */ +#define UNIQUE_ID1 0x1234 +#define UNIQUE_ID2 0x4321 +#define UNIQUE_ID3 0xABCD + +static void __init +awe_check_dram(void) +{ + if (awe_present) /* already initialized */ + return; + + if (memsize >= 0) { /* given by config file or module option */ + memsize *= 1024; /* convert to Kbytes */ + return; + } + + awe_open_dram_for_check(); + + memsize = 0; + + /* set up unique two id numbers */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); + awe_poke(AWE_SMLD, UNIQUE_ID1); + awe_poke(AWE_SMLD, UNIQUE_ID2); + + while (memsize < AWE_MAX_DRAM_SIZE) { + awe_wait(5); + /* read a data on the DRAM start address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID1) + break; + if (awe_peek(AWE_SMLD) != UNIQUE_ID2) + break; + memsize += 512; /* increment 512kbytes */ + /* Write a unique data on the test address; + * if the address is out of range, the data is written on + * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are + * broken by this data. + */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + memsize*512L); + awe_poke(AWE_SMLD, UNIQUE_ID3); + awe_wait(5); + /* read a data on the just written DRAM address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + memsize*512L); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID3) + break; + } + awe_close_dram(); + + DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", memsize)); + + /* convert to Kbytes */ + memsize *= 1024; +} + + +/*----------------------------------------------------------------*/ + +/* + * chorus and reverb controls; from VV's guide + */ + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[AWE_CHORUS_NUMBERS]; +static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */ +}; + +static int +awe_load_chorus_fx(awe_patch_info *patch, const char __user *addr, int count) +{ + if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { + printk(KERN_WARNING "AWE32 Error: invalid chorus mode %d for uploading\n", patch->optarg); + return -EINVAL; + } + if (count < sizeof(awe_chorus_fx_rec)) { + printk(KERN_WARNING "AWE32 Error: too short chorus fx parameters\n"); + return -EINVAL; + } + if (copy_from_user(&chorus_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, + sizeof(awe_chorus_fx_rec))) + return -EFAULT; + chorus_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_chorus_mode(int effect) +{ + if (effect < 0 || effect >= AWE_CHORUS_NUMBERS || + (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback); + awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset); + awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth); + awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay); + awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_poke_dw(AWE_HWCF7, 0x0000); +} + +static void +awe_update_chorus_mode(void) +{ + awe_set_chorus_mode(ctrls[AWE_MD_CHORUS_MODE]); +} + +/*----------------------------------------------------------------*/ + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[AWE_CHORUS_NUMBERS]; +static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +static struct ReverbCmdPair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +static int +awe_load_reverb_fx(awe_patch_info *patch, const char __user *addr, int count) +{ + if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { + printk(KERN_WARNING "AWE32 Error: invalid reverb mode %d for uploading\n", patch->optarg); + return -EINVAL; + } + if (count < sizeof(awe_reverb_fx_rec)) { + printk(KERN_WARNING "AWE32 Error: too short reverb fx parameters\n"); + return -EINVAL; + } + if (copy_from_user(&reverb_parm[patch->optarg], addr + AWE_PATCH_INFO_SIZE, + sizeof(awe_reverb_fx_rec))) + return -EFAULT; + reverb_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_reverb_mode(int effect) +{ + int i; + if (effect < 0 || effect >= AWE_REVERB_NUMBERS || + (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) + awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port, + reverb_parm[effect].parms[i]); +} + +static void +awe_update_reverb_mode(void) +{ + awe_set_reverb_mode(ctrls[AWE_MD_REVERB_MODE]); +} + +/* + * treble/bass equalizer control + */ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC348, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +static void +awe_equalizer(int bass, int treble) +{ + unsigned short w; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]); + awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]); + awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]); + awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]); + awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]); + awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]); + awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]); + awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]); + awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]); + awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262)); + awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362)); +} + +static void awe_update_equalizer(void) +{ + awe_equalizer(ctrls[AWE_MD_BASS_LEVEL], ctrls[AWE_MD_TREBLE_LEVEL]); +} + + +/*----------------------------------------------------------------*/ + +#ifdef CONFIG_AWE32_MIDIEMU + +/* + * Emu8000 MIDI Emulation + */ + +/* + * midi queue record + */ + +/* queue type */ +enum { Q_NONE, Q_VARLEN, Q_READ, Q_SYSEX, }; + +#define MAX_MIDIBUF 64 + +/* midi status */ +typedef struct MidiStatus { + int queue; /* queue type */ + int qlen; /* queue length */ + int read; /* chars read */ + int status; /* current status */ + int chan; /* current channel */ + unsigned char buf[MAX_MIDIBUF]; +} MidiStatus; + +/* MIDI mode type */ +enum { MODE_GM, MODE_GS, MODE_XG, }; + +/* NRPN / CC -> Emu8000 parameter converter */ +typedef struct { + int control; + int awe_effect; + unsigned short (*convert)(int val); +} ConvTable; + + +/* + * prototypes + */ + +static int awe_midi_open(int dev, int mode, void (*input)(int,unsigned char), void (*output)(int)); +static void awe_midi_close(int dev); +static int awe_midi_ioctl(int dev, unsigned cmd, void __user * arg); +static int awe_midi_outputc(int dev, unsigned char midi_byte); + +static void init_midi_status(MidiStatus *st); +static void clear_rpn(void); +static void get_midi_char(MidiStatus *st, int c); +/*static void queue_varlen(MidiStatus *st, int c);*/ +static void special_event(MidiStatus *st, int c); +static void queue_read(MidiStatus *st, int c); +static void midi_note_on(MidiStatus *st); +static void midi_note_off(MidiStatus *st); +static void midi_key_pressure(MidiStatus *st); +static void midi_channel_pressure(MidiStatus *st); +static void midi_pitch_wheel(MidiStatus *st); +static void midi_program_change(MidiStatus *st); +static void midi_control_change(MidiStatus *st); +static void midi_select_bank(MidiStatus *st, int val); +static void midi_nrpn_event(MidiStatus *st); +static void midi_rpn_event(MidiStatus *st); +static void midi_detune(int chan, int coarse, int fine); +static void midi_system_exclusive(MidiStatus *st); +static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); +static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val); +static int xg_control_change(MidiStatus *st, int cmd, int val); + +#define numberof(ary) (sizeof(ary)/sizeof(ary[0])) + + +/* + * OSS Midi device record + */ + +static struct midi_operations awe_midi_operations = +{ + .owner = THIS_MODULE, + .info = {"AWE Midi Emu", 0, 0, SNDCARD_SB}, + .in_info = {0}, + .open = awe_midi_open, /*open*/ + .close = awe_midi_close, /*close*/ + .ioctl = awe_midi_ioctl, /*ioctl*/ + .outputc = awe_midi_outputc, /*outputc*/ +}; + +static int my_mididev = -1; + +static void __init attach_midiemu(void) +{ + if ((my_mididev = sound_alloc_mididev()) < 0) + printk ("Sound: Too many midi devices detected\n"); + else + midi_devs[my_mididev] = &awe_midi_operations; +} + +static void unload_midiemu(void) +{ + if (my_mididev >= 0) + sound_unload_mididev(my_mididev); +} + + +/* + * open/close midi device + */ + +static int midi_opened = FALSE; + +static int midi_mode; +static int coarsetune, finetune; + +static int xg_mapping = TRUE; +static int xg_bankmode; + +/* effect sensitivity */ + +#define FX_CUTOFF 0 +#define FX_RESONANCE 1 +#define FX_ATTACK 2 +#define FX_RELEASE 3 +#define FX_VIBRATE 4 +#define FX_VIBDEPTH 5 +#define FX_VIBDELAY 6 +#define FX_NUMS 7 + +#define DEF_FX_CUTOFF 170 +#define DEF_FX_RESONANCE 6 +#define DEF_FX_ATTACK 50 +#define DEF_FX_RELEASE 50 +#define DEF_FX_VIBRATE 30 +#define DEF_FX_VIBDEPTH 4 +#define DEF_FX_VIBDELAY 1500 + +/* effect sense: */ +static int gs_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; +static int xg_sense[] = +{ + DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, + DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY +}; + + +/* current status */ +static MidiStatus curst; + + +static int +awe_midi_open (int dev, int mode, + void (*input)(int,unsigned char), + void (*output)(int)) +{ + if (midi_opened) + return -EBUSY; + + midi_opened = TRUE; + + midi_mode = MODE_GM; + + curst.queue = Q_NONE; + curst.qlen = 0; + curst.read = 0; + curst.status = 0; + curst.chan = 0; + memset(curst.buf, 0, sizeof(curst.buf)); + + init_midi_status(&curst); + + return 0; +} + +static void +awe_midi_close (int dev) +{ + midi_opened = FALSE; +} + + +static int +awe_midi_ioctl (int dev, unsigned cmd, void __user *arg) +{ + return -EPERM; +} + +static int +awe_midi_outputc (int dev, unsigned char midi_byte) +{ + if (! midi_opened) + return 1; + + /* force to change playing mode */ + playing_mode = AWE_PLAY_MULTI; + + get_midi_char(&curst, midi_byte); + return 1; +} + + +/* + * initialize + */ + +static void init_midi_status(MidiStatus *st) +{ + clear_rpn(); + coarsetune = 0; + finetune = 0; +} + + +/* + * RPN & NRPN + */ + +#define MAX_MIDI_CHANNELS 16 + +/* RPN & NRPN */ +static unsigned char nrpn[MAX_MIDI_CHANNELS]; /* current event is NRPN? */ +static int msb_bit; /* current event is msb for RPN/NRPN */ +/* RPN & NRPN indeces */ +static unsigned char rpn_msb[MAX_MIDI_CHANNELS], rpn_lsb[MAX_MIDI_CHANNELS]; +/* RPN & NRPN values */ +static int rpn_val[MAX_MIDI_CHANNELS]; + +static void clear_rpn(void) +{ + int i; + for (i = 0; i < MAX_MIDI_CHANNELS; i++) { + nrpn[i] = 0; + rpn_msb[i] = 127; + rpn_lsb[i] = 127; + rpn_val[i] = 0; + } + msb_bit = 0; +} + + +/* + * process midi queue + */ + +/* status event types */ +typedef void (*StatusEvent)(MidiStatus *st); +static struct StatusEventList { + StatusEvent process; + int qlen; +} status_event[8] = { + {midi_note_off, 2}, + {midi_note_on, 2}, + {midi_key_pressure, 2}, + {midi_control_change, 2}, + {midi_program_change, 1}, + {midi_channel_pressure, 1}, + {midi_pitch_wheel, 2}, + {NULL, 0}, +}; + + +/* read a char from fifo and process it */ +static void get_midi_char(MidiStatus *st, int c) +{ + if (c == 0xfe) { + /* ignore active sense */ + st->queue = Q_NONE; + return; + } + + switch (st->queue) { + /* case Q_VARLEN: queue_varlen(st, c); break;*/ + case Q_READ: + case Q_SYSEX: + queue_read(st, c); + break; + case Q_NONE: + st->read = 0; + if ((c & 0xf0) == 0xf0) { + special_event(st, c); + } else if (c & 0x80) { /* status change */ + st->status = (c >> 4) & 0x07; + st->chan = c & 0x0f; + st->queue = Q_READ; + st->qlen = status_event[st->status].qlen; + if (st->qlen == 0) + st->queue = Q_NONE; + } + break; + } +} + +/* 0xfx events */ +static void special_event(MidiStatus *st, int c) +{ + switch (c) { + case 0xf0: /* system exclusive */ + st->queue = Q_SYSEX; + st->qlen = 0; + break; + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* song select */ + st->queue = Q_READ; + st->qlen = 1; + break; + case 0xf2: /* song position */ + st->queue = Q_READ; + st->qlen = 2; + break; + } +} + +#if 0 +/* read variable length value */ +static void queue_varlen(MidiStatus *st, int c) +{ + st->qlen += (c & 0x7f); + if (c & 0x80) { + st->qlen <<= 7; + return; + } + if (st->qlen <= 0) { + st->qlen = 0; + st->queue = Q_NONE; + } + st->queue = Q_READ; + st->read = 0; +} +#endif + + +/* read a char */ +static void queue_read(MidiStatus *st, int c) +{ + if (st->read < MAX_MIDIBUF) { + if (st->queue != Q_SYSEX) + c &= 0x7f; + st->buf[st->read] = (unsigned char)c; + } + st->read++; + if (st->queue == Q_SYSEX && c == 0xf7) { + midi_system_exclusive(st); + st->queue = Q_NONE; + } else if (st->queue == Q_READ && st->read >= st->qlen) { + if (status_event[st->status].process) + status_event[st->status].process(st); + st->queue = Q_NONE; + } +} + + +/* + * status events + */ + +/* note on */ +static void midi_note_on(MidiStatus *st) +{ + DEBUG(2,printk("midi: note_on (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); + if (st->buf[1] == 0) + midi_note_off(st); + else + awe_start_note(0, st->chan, st->buf[0], st->buf[1]); +} + +/* note off */ +static void midi_note_off(MidiStatus *st) +{ + DEBUG(2,printk("midi: note_off (%d) %d %d\n", st->chan, st->buf[0], st->buf[1])); + awe_kill_note(0, st->chan, st->buf[0], st->buf[1]); +} + +/* key pressure change */ +static void midi_key_pressure(MidiStatus *st) +{ + awe_key_pressure(0, st->chan, st->buf[0], st->buf[1]); +} + +/* channel pressure change */ +static void midi_channel_pressure(MidiStatus *st) +{ + channels[st->chan].chan_press = st->buf[0]; + awe_modwheel_change(st->chan, st->buf[0]); +} + +/* pitch wheel change */ +static void midi_pitch_wheel(MidiStatus *st) +{ + int val = (int)st->buf[1] * 128 + st->buf[0]; + awe_bender(0, st->chan, val); +} + +/* program change */ +static void midi_program_change(MidiStatus *st) +{ + int preset; + preset = st->buf[0]; + if (midi_mode == MODE_GS && IS_DRUM_CHANNEL(st->chan) && preset == 127) + preset = 0; + else if (midi_mode == MODE_XG && xg_mapping && IS_DRUM_CHANNEL(st->chan)) + preset += 64; + + awe_set_instr(0, st->chan, preset); +} + +#define send_effect(chan,type,val) awe_send_effect(chan,-1,type,val) +#define add_effect(chan,type,val) awe_send_effect(chan,-1,(type)|0x80,val) +#define unset_effect(chan,type) awe_send_effect(chan,-1,(type)|0x40,0) + +/* midi control change */ +static void midi_control_change(MidiStatus *st) +{ + int cmd = st->buf[0]; + int val = st->buf[1]; + + DEBUG(2,printk("midi: control (%d) %d %d\n", st->chan, cmd, val)); + if (midi_mode == MODE_XG) { + if (xg_control_change(st, cmd, val)) + return; + } + + /* controls #31 - #64 are LSB of #0 - #31 */ + msb_bit = 1; + if (cmd >= 0x20 && cmd < 0x40) { + msb_bit = 0; + cmd -= 0x20; + } + + switch (cmd) { + case CTL_SOFT_PEDAL: + if (val == 127) + add_effect(st->chan, AWE_FX_CUTOFF, -160); + else + unset_effect(st->chan, AWE_FX_CUTOFF); + break; + + case CTL_BANK_SELECT: + midi_select_bank(st, val); + break; + + /* set RPN/NRPN parameter */ + case CTL_REGIST_PARM_NUM_MSB: + nrpn[st->chan]=0; rpn_msb[st->chan]=val; + break; + case CTL_REGIST_PARM_NUM_LSB: + nrpn[st->chan]=0; rpn_lsb[st->chan]=val; + break; + case CTL_NONREG_PARM_NUM_MSB: + nrpn[st->chan]=1; rpn_msb[st->chan]=val; + break; + case CTL_NONREG_PARM_NUM_LSB: + nrpn[st->chan]=1; rpn_lsb[st->chan]=val; + break; + + /* send RPN/NRPN entry */ + case CTL_DATA_ENTRY: + if (msb_bit) + rpn_val[st->chan] = val * 128; + else + rpn_val[st->chan] |= val; + if (nrpn[st->chan]) + midi_nrpn_event(st); + else + midi_rpn_event(st); + break; + + /* increase/decrease data entry */ + case CTL_DATA_INCREMENT: + rpn_val[st->chan]++; + midi_rpn_event(st); + break; + case CTL_DATA_DECREMENT: + rpn_val[st->chan]--; + midi_rpn_event(st); + break; + + /* default */ + default: + awe_controller(0, st->chan, cmd, val); + break; + } +} + +/* tone bank change */ +static void midi_select_bank(MidiStatus *st, int val) +{ + if (midi_mode == MODE_XG && msb_bit) { + xg_bankmode = val; + /* XG MSB value; not normal bank selection */ + switch (val) { + case 127: /* remap to drum channel */ + awe_controller(0, st->chan, CTL_BANK_SELECT, 128); + break; + default: /* remap to normal channel */ + awe_controller(0, st->chan, CTL_BANK_SELECT, val); + break; + } + return; + } else if (midi_mode == MODE_GS && !msb_bit) + /* ignore LSB bank in GS mode (used for mapping) */ + return; + + /* normal bank controls; accept both MSB and LSB */ + if (! IS_DRUM_CHANNEL(st->chan)) { + if (midi_mode == MODE_XG) { + if (xg_bankmode) return; + if (val == 64 || val == 126) + val = 0; + } else if (midi_mode == MODE_GS && val == 127) + val = 0; + awe_controller(0, st->chan, CTL_BANK_SELECT, val); + } +} + + +/* + * RPN events + */ + +static void midi_rpn_event(MidiStatus *st) +{ + int type; + type = (rpn_msb[st->chan]<<8) | rpn_lsb[st->chan]; + switch (type) { + case 0x0000: /* Pitch bend sensitivity */ + /* MSB only / 1 semitone per 128 */ + if (msb_bit) { + channels[st->chan].bender_range = + rpn_val[st->chan] * 100 / 128; + } + break; + + case 0x0001: /* fine tuning: */ + /* MSB/LSB, 8192=center, 100/8192 cent step */ + finetune = rpn_val[st->chan] - 8192; + midi_detune(st->chan, coarsetune, finetune); + break; + + case 0x0002: /* coarse tuning */ + /* MSB only / 8192=center, 1 semitone per 128 */ + if (msb_bit) { + coarsetune = rpn_val[st->chan] - 8192; + midi_detune(st->chan, coarsetune, finetune); + } + break; + + case 0x7F7F: /* "lock-in" RPN */ + break; + } +} + + +/* tuning: + * coarse = -8192 to 8192 (100 cent per 128) + * fine = -8192 to 8192 (max=100cent) + */ +static void midi_detune(int chan, int coarse, int fine) +{ + /* 4096 = 1200 cents in AWE parameter */ + int val; + val = coarse * 4096 / (12 * 128); + val += fine / 24; + if (val) + send_effect(chan, AWE_FX_INIT_PITCH, val); + else + unset_effect(chan, AWE_FX_INIT_PITCH); +} + + +/* + * system exclusive message + * GM/GS/XG macros are accepted + */ + +static void midi_system_exclusive(MidiStatus *st) +{ + /* GM on */ + static unsigned char gm_on_macro[] = { + 0x7e,0x7f,0x09,0x01, + }; + /* XG on */ + static unsigned char xg_on_macro[] = { + 0x43,0x10,0x4c,0x00,0x00,0x7e,0x00, + }; + /* GS prefix + * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off + * reverb mode: XX=0x01, YY=0x30, ZZ=0-7 + * chorus mode: XX=0x01, YY=0x38, ZZ=0-7 + */ + static unsigned char gs_pfx_macro[] = { + 0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/ + }; + +#if 0 + /* SC88 system mode set + * single module mode: XX=1 + * double module mode: XX=0 + */ + static unsigned char gs_mode_macro[] = { + 0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/ + }; + /* SC88 display macro: XX=01:bitmap, 00:text + */ + static unsigned char gs_disp_macro[] = { + 0x41,0x10,0x45,0x12,0x10,/*XX,00*/ + }; +#endif + + /* GM on */ + if (memcmp(st->buf, gm_on_macro, sizeof(gm_on_macro)) == 0) { + if (midi_mode != MODE_GS && midi_mode != MODE_XG) + midi_mode = MODE_GM; + init_midi_status(st); + } + + /* GS macros */ + else if (memcmp(st->buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) { + if (midi_mode != MODE_GS && midi_mode != MODE_XG) + midi_mode = MODE_GS; + + if (st->buf[5] == 0x00 && st->buf[6] == 0x7f && st->buf[7] == 0x00) { + /* GS reset */ + init_midi_status(st); + } + + else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x15) { + /* drum pattern */ + int p = st->buf[5] & 0x0f; + if (p == 0) p = 9; + else if (p < 10) p--; + if (st->buf[7] == 0) + DRUM_CHANNEL_OFF(p); + else + DRUM_CHANNEL_ON(p); + + } else if ((st->buf[5] & 0xf0) == 0x10 && st->buf[6] == 0x21) { + /* program */ + int p = st->buf[5] & 0x0f; + if (p == 0) p = 9; + else if (p < 10) p--; + if (! IS_DRUM_CHANNEL(p)) + awe_set_instr(0, p, st->buf[7]); + + } else if (st->buf[5] == 0x01 && st->buf[6] == 0x30) { + /* reverb mode */ + awe_set_reverb_mode(st->buf[7]); + + } else if (st->buf[5] == 0x01 && st->buf[6] == 0x38) { + /* chorus mode */ + awe_set_chorus_mode(st->buf[7]); + + } else if (st->buf[5] == 0x00 && st->buf[6] == 0x04) { + /* master volume */ + awe_change_master_volume(st->buf[7]); + + } + } + + /* XG on */ + else if (memcmp(st->buf, xg_on_macro, sizeof(xg_on_macro)) == 0) { + midi_mode = MODE_XG; + xg_mapping = TRUE; + xg_bankmode = 0; + } +} + + +/*----------------------------------------------------------------*/ + +/* + * convert NRPN/control values + */ + +static int send_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + send_effect(st->chan, table[i].awe_effect, cval); + return TRUE; + } + } + return FALSE; +} + +static int add_converted_effect(ConvTable *table, int num_tables, MidiStatus *st, int type, int val) +{ + int i, cval; + for (i = 0; i < num_tables; i++) { + if (table[i].control == type) { + cval = table[i].convert(val); + add_effect(st->chan, table[i].awe_effect|0x80, cval); + return TRUE; + } + } + return FALSE; +} + + +/* + * AWE32 NRPN effects + */ + +static unsigned short fx_delay(int val); +static unsigned short fx_attack(int val); +static unsigned short fx_hold(int val); +static unsigned short fx_decay(int val); +static unsigned short fx_the_value(int val); +static unsigned short fx_twice_value(int val); +static unsigned short fx_conv_pitch(int val); +static unsigned short fx_conv_Q(int val); + +/* function for each NRPN */ /* [range] units */ +#define fx_env1_delay fx_delay /* [0,5900] 4msec */ +#define fx_env1_attack fx_attack /* [0,5940] 1msec */ +#define fx_env1_hold fx_hold /* [0,8191] 1msec */ +#define fx_env1_decay fx_decay /* [0,5940] 4msec */ +#define fx_env1_release fx_decay /* [0,5940] 4msec */ +#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ +#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ + +#define fx_env2_delay fx_delay /* [0,5900] 4msec */ +#define fx_env2_attack fx_attack /* [0,5940] 1msec */ +#define fx_env2_hold fx_hold /* [0,8191] 1msec */ +#define fx_env2_decay fx_decay /* [0,5940] 4msec */ +#define fx_env2_release fx_decay /* [0,5940] 4msec */ +#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ + +#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ +#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ +#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ + +#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ +#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ +#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ + +#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ +#define fx_chorus fx_the_value /* [0,255] -- */ +#define fx_reverb fx_the_value /* [0,255] -- */ +#define fx_cutoff fx_twice_value /* [0,127] 62Hz */ +#define fx_filterQ fx_conv_Q /* [0,127] -- */ + +static unsigned short fx_delay(int val) +{ + return (unsigned short)calc_parm_delay(val); +} + +static unsigned short fx_attack(int val) +{ + return (unsigned short)calc_parm_attack(val); +} + +static unsigned short fx_hold(int val) +{ + return (unsigned short)calc_parm_hold(val); +} + +static unsigned short fx_decay(int val) +{ + return (unsigned short)calc_parm_decay(val); +} + +static unsigned short fx_the_value(int val) +{ + return (unsigned short)(val & 0xff); +} + +static unsigned short fx_twice_value(int val) +{ + return (unsigned short)((val * 2) & 0xff); +} + +static unsigned short fx_conv_pitch(int val) +{ + return (short)(val * 4096 / 1200); +} + +static unsigned short fx_conv_Q(int val) +{ + return (unsigned short)((val / 8) & 0xff); +} + + +static ConvTable awe_effects[] = +{ + { 0, AWE_FX_LFO1_DELAY, fx_lfo1_delay}, + { 1, AWE_FX_LFO1_FREQ, fx_lfo1_freq}, + { 2, AWE_FX_LFO2_DELAY, fx_lfo2_delay}, + { 3, AWE_FX_LFO2_FREQ, fx_lfo2_freq}, + + { 4, AWE_FX_ENV1_DELAY, fx_env1_delay}, + { 5, AWE_FX_ENV1_ATTACK,fx_env1_attack}, + { 6, AWE_FX_ENV1_HOLD, fx_env1_hold}, + { 7, AWE_FX_ENV1_DECAY, fx_env1_decay}, + { 8, AWE_FX_ENV1_SUSTAIN, fx_env1_sustain}, + { 9, AWE_FX_ENV1_RELEASE, fx_env1_release}, + + {10, AWE_FX_ENV2_DELAY, fx_env2_delay}, + {11, AWE_FX_ENV2_ATTACK, fx_env2_attack}, + {12, AWE_FX_ENV2_HOLD, fx_env2_hold}, + {13, AWE_FX_ENV2_DECAY, fx_env2_decay}, + {14, AWE_FX_ENV2_SUSTAIN, fx_env2_sustain}, + {15, AWE_FX_ENV2_RELEASE, fx_env2_release}, + + {16, AWE_FX_INIT_PITCH, fx_init_pitch}, + {17, AWE_FX_LFO1_PITCH, fx_lfo1_pitch}, + {18, AWE_FX_LFO2_PITCH, fx_lfo2_pitch}, + {19, AWE_FX_ENV1_PITCH, fx_env1_pitch}, + {20, AWE_FX_LFO1_VOLUME, fx_lfo1_volume}, + {21, AWE_FX_CUTOFF, fx_cutoff}, + {22, AWE_FX_FILTERQ, fx_filterQ}, + {23, AWE_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, + {24, AWE_FX_ENV1_CUTOFF, fx_env1_cutoff}, + {25, AWE_FX_CHORUS, fx_chorus}, + {26, AWE_FX_REVERB, fx_reverb}, +}; + +static int num_awe_effects = numberof(awe_effects); + + +/* + * GS(SC88) NRPN effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static unsigned short gs_cutoff(int val) +{ + return (val - 64) * gs_sense[FX_CUTOFF] / 50; +} + +/* resonance: 0 to 15(max) */ +static unsigned short gs_filterQ(int val) +{ + return (val - 64) * gs_sense[FX_RESONANCE] / 50; +} + +/* attack: */ +static unsigned short gs_attack(int val) +{ + return -(val - 64) * gs_sense[FX_ATTACK] / 50; +} + +/* decay: */ +static unsigned short gs_decay(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* release: */ +static unsigned short gs_release(int val) +{ + return -(val - 64) * gs_sense[FX_RELEASE] / 50; +} + +/* vibrato freq: 0.042Hz step, max=255 */ +static unsigned short gs_vib_rate(int val) +{ + return (val - 64) * gs_sense[FX_VIBRATE] / 50; +} + +/* vibrato depth: max=127, 1 octave */ +static unsigned short gs_vib_depth(int val) +{ + return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; +} + +/* vibrato delay: -0.725msec step */ +static unsigned short gs_vib_delay(int val) +{ + return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; +} + +static ConvTable gs_effects[] = +{ + {32, AWE_FX_CUTOFF, gs_cutoff}, + {33, AWE_FX_FILTERQ, gs_filterQ}, + {99, AWE_FX_ENV2_ATTACK, gs_attack}, + {100, AWE_FX_ENV2_DECAY, gs_decay}, + {102, AWE_FX_ENV2_RELEASE, gs_release}, + {8, AWE_FX_LFO1_FREQ, gs_vib_rate}, + {9, AWE_FX_LFO1_VOLUME, gs_vib_depth}, + {10, AWE_FX_LFO1_DELAY, gs_vib_delay}, +}; + +static int num_gs_effects = numberof(gs_effects); + + +/* + * NRPN events: accept as AWE32/SC88 specific controls + */ + +static void midi_nrpn_event(MidiStatus *st) +{ + if (rpn_msb[st->chan] == 127 && rpn_lsb[st->chan] <= 26) { + if (! msb_bit) /* both MSB/LSB necessary */ + send_converted_effect(awe_effects, num_awe_effects, + st, rpn_lsb[st->chan], + rpn_val[st->chan] - 8192); + } else if (rpn_msb[st->chan] == 1) { + if (msb_bit) /* only MSB is valid */ + add_converted_effect(gs_effects, num_gs_effects, + st, rpn_lsb[st->chan], + rpn_val[st->chan] / 128); + } +} + + +/* + * XG control effects; still experimental + */ + +/* cutoff: quarter semitone step, max=255 */ +static unsigned short xg_cutoff(int val) +{ + return (val - 64) * xg_sense[FX_CUTOFF] / 64; +} + +/* resonance: 0(open) to 15(most nasal) */ +static unsigned short xg_filterQ(int val) +{ + return (val - 64) * xg_sense[FX_RESONANCE] / 64; +} + +/* attack: */ +static unsigned short xg_attack(int val) +{ + return -(val - 64) * xg_sense[FX_ATTACK] / 64; +} + +/* release: */ +static unsigned short xg_release(int val) +{ + return -(val - 64) * xg_sense[FX_RELEASE] / 64; +} + +static ConvTable xg_effects[] = +{ + {71, AWE_FX_CUTOFF, xg_cutoff}, + {74, AWE_FX_FILTERQ, xg_filterQ}, + {72, AWE_FX_ENV2_RELEASE, xg_release}, + {73, AWE_FX_ENV2_ATTACK, xg_attack}, +}; + +static int num_xg_effects = numberof(xg_effects); + +static int xg_control_change(MidiStatus *st, int cmd, int val) +{ + return add_converted_effect(xg_effects, num_xg_effects, st, cmd, val); +} + +#endif /* CONFIG_AWE32_MIDIEMU */ + + +/*----------------------------------------------------------------*/ + + +/* + * initialization of AWE driver + */ + +static void +awe_initialize(void) +{ + DEBUG(0,printk("AWE32: initializing..\n")); + + /* initialize hardware configuration */ + awe_poke(AWE_HWCF1, 0x0059); + awe_poke(AWE_HWCF2, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + awe_poke(AWE_HWCF3, 0); + + /* initialize audio channels */ + awe_init_audio(); + + /* initialize DMA */ + awe_init_dma(); + + /* initialize init array */ + awe_init_array(); + + /* check DRAM memory size */ + awe_check_dram(); + + /* initialize the FM section of the AWE32 */ + awe_init_fm(); + + /* set up voice envelopes */ + awe_tweak(); + + /* enable audio */ + awe_poke(AWE_HWCF3, 0x0004); + + /* set default values */ + awe_init_ctrl_parms(TRUE); + + /* set equalizer */ + awe_update_equalizer(); + + /* set reverb & chorus modes */ + awe_update_reverb_mode(); + awe_update_chorus_mode(); +} + + +/* + * Core Device Management Functions + */ + +/* store values to i/o port array */ +static void setup_ports(int port1, int port2, int port3) +{ + awe_ports[0] = port1; + if (port2 == 0) + port2 = port1 + 0x400; + awe_ports[1] = port2; + awe_ports[2] = port2 + 2; + if (port3 == 0) + port3 = port1 + 0x800; + awe_ports[3] = port3; + awe_ports[4] = port3 + 2; + + port_setuped = TRUE; +} + +/* + * port request + * 0x620-623, 0xA20-A23, 0xE20-E23 + */ + +static int +awe_request_region(void) +{ + if (! port_setuped) + return 0; + if (! request_region(awe_ports[0], 4, "sound driver (AWE32)")) + return 0; + if (! request_region(awe_ports[1], 4, "sound driver (AWE32)")) + goto err_out; + if (! request_region(awe_ports[3], 4, "sound driver (AWE32)")) + goto err_out1; + return 1; +err_out1: + release_region(awe_ports[1], 4); +err_out: + release_region(awe_ports[0], 4); + return 0; +} + +static void +awe_release_region(void) +{ + if (! port_setuped) return; + release_region(awe_ports[0], 4); + release_region(awe_ports[1], 4); + release_region(awe_ports[3], 4); +} + +static int awe_attach_device(void) +{ + if (awe_present) return 0; /* for OSS38.. called twice? */ + + /* reserve I/O ports for awedrv */ + if (! awe_request_region()) { + printk(KERN_ERR "AWE32: I/O area already used.\n"); + return 0; + } + + /* set buffers to NULL */ + sfhead = sftail = NULL; + + my_dev = sound_alloc_synthdev(); + if (my_dev == -1) { + printk(KERN_ERR "AWE32 Error: too many synthesizers\n"); + awe_release_region(); + return 0; + } + + voice_alloc = &awe_operations.alloc; + voice_alloc->max_voice = awe_max_voices; + synth_devs[my_dev] = &awe_operations; + +#ifdef CONFIG_AWE32_MIXER + attach_mixer(); +#endif +#ifdef CONFIG_AWE32_MIDIEMU + attach_midiemu(); +#endif + + /* clear all samples */ + awe_reset_samples(); + + /* initialize AWE32 hardware */ + awe_initialize(); + + sprintf(awe_info.name, "AWE32-%s (RAM%dk)", + AWEDRV_VERSION, memsize/1024); + printk(KERN_INFO "\n", memsize/1024); + + awe_present = TRUE; + + return 1; +} + +static void awe_dettach_device(void) +{ + if (awe_present) { + awe_reset_samples(); + awe_release_region(); + free_tables(); +#ifdef CONFIG_AWE32_MIXER + unload_mixer(); +#endif +#ifdef CONFIG_AWE32_MIDIEMU + unload_midiemu(); +#endif + sound_unload_synthdev(my_dev); + awe_present = FALSE; + } +} + + +/* + * Legacy device Probing + */ + +/* detect emu8000 chip on the specified address; from VV's guide */ + +static int __init +awe_detect_base(int addr) +{ + setup_ports(addr, 0, 0); + if ((awe_peek(AWE_U1) & 0x000F) != 0x000C) + return 0; + if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058) + return 0; + if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003) + return 0; + DEBUG(0,printk("AWE32 found at %x\n", addr)); + return 1; +} + +static int __init awe_detect_legacy_devices(void) +{ + int base; + for (base = 0x620; base <= 0x680; base += 0x20) + if (awe_detect_base(base)) { + awe_attach_device(); + return 1; + } + DEBUG(0,printk("AWE32 Legacy detection failed\n")); + return 0; +} + + +/* + * PnP device Probing + */ + +static struct pnp_device_id awe_pnp_ids[] = { + {.id = "CTL0021", .driver_data = 0}, /* AWE32 WaveTable */ + {.id = "CTL0022", .driver_data = 0}, /* AWE64 WaveTable */ + {.id = "CTL0023", .driver_data = 0}, /* AWE64 Gold WaveTable */ + { } /* terminator */ +}; + +MODULE_DEVICE_TABLE(pnp, awe_pnp_ids); + +static int awe_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + int io1, io2, io3; + + if (awe_present) { + printk(KERN_ERR "AWE32: This driver only supports one AWE32 device, skipping.\n"); + } + + if (!pnp_port_valid(dev,0) || + !pnp_port_valid(dev,1) || + !pnp_port_valid(dev,2)) { + printk(KERN_ERR "AWE32: The PnP device does not have the required resources.\n"); + return -EINVAL; + } + io1 = pnp_port_start(dev,0); + io2 = pnp_port_start(dev,1); + io3 = pnp_port_start(dev,2); + printk(KERN_INFO "AWE32: A PnP Wave Table was detected at IO's %#x,%#x,%#x.\n", + io1, io2, io3); + setup_ports(io1, io2, io3); + + awe_attach_device(); + return 0; +} + +static void awe_pnp_remove(struct pnp_dev *dev) +{ + awe_dettach_device(); +} + +static struct pnp_driver awe_pnp_driver = { + .name = "AWE32", + .id_table = awe_pnp_ids, + .probe = awe_pnp_probe, + .remove = awe_pnp_remove, +}; + +static int __init awe_detect_pnp_devices(void) +{ + int ret; + + ret = pnp_register_driver(&awe_pnp_driver); + if (ret<0) + printk(KERN_ERR "AWE32: PnP support is unavailable.\n"); + return ret; +} + + +/* + * device / lowlevel (module) interface + */ + +static int __init +awe_detect(void) +{ + printk(KERN_INFO "AWE32: Probing for WaveTable...\n"); + if (isapnp) { + if (awe_detect_pnp_devices()>=0) + return 1; + } else + printk(KERN_INFO "AWE32: Skipping PnP detection.\n"); + + if (awe_detect_legacy_devices()) + return 1; + + return 0; +} + +static int __init attach_awe(void) +{ + return awe_detect() ? 0 : -ENODEV; +} + +static void __exit unload_awe(void) +{ + pnp_unregister_driver(&awe_pnp_driver); + awe_dettach_device(); +} + + +module_init(attach_awe); +module_exit(unload_awe); + +#ifndef MODULE +static int __init setup_awe(char *str) +{ + /* io, memsize, isapnp */ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + memsize = ints[2]; + isapnp = ints[3]; + + return 1; +} + +__setup("awe=", setup_awe); +#endif diff --git a/trunk/sound/oss/awe_wave.h b/trunk/sound/oss/awe_wave.h new file mode 100644 index 000000000000..fe584810608f --- /dev/null +++ b/trunk/sound/oss/awe_wave.h @@ -0,0 +1,77 @@ +/* + * sound/oss/awe_wave.h + * + * Configuration of AWE32/SB32/AWE64 wave table synth driver. + * version 0.4.4; Jan. 4, 2000 + * + * Copyright (C) 1996-1998 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * chorus & reverb effects send for FM chip: from 0 to 0xff + * larger numbers often cause weird sounds. + */ + +#define DEF_FM_CHORUS_DEPTH 0x10 +#define DEF_FM_REVERB_DEPTH 0x10 + + +/* + * other compile conditions + */ + +/* initialize FM passthrough even without extended RAM */ +#undef AWE_ALWAYS_INIT_FM + +/* debug on */ +#define AWE_DEBUG_ON + +/* GUS compatible mode */ +#define AWE_HAS_GUS_COMPATIBILITY + +/* add MIDI emulation by wavetable */ +#define CONFIG_AWE32_MIDIEMU + +/* add mixer control of emu8000 equalizer */ +#undef CONFIG_AWE32_MIXER + +/* use new volume calculation method as default */ +#define AWE_USE_NEW_VOLUME_CALC + +/* check current volume target for searching empty voices */ +#define AWE_CHECK_VTARGET + +/* allow sample sharing */ +#define AWE_ALLOW_SAMPLE_SHARING + +/* + * AWE32 card configuration: + * uncomment the following lines *ONLY* when auto detection doesn't + * work properly on your machine. + */ + +/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */ +/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */ + +/* + * AWE driver version number + */ +#define AWE_MAJOR_VERSION 0 +#define AWE_MINOR_VERSION 4 +#define AWE_TINY_VERSION 4 +#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION) +#define AWEDRV_VERSION "0.4.4" diff --git a/trunk/sound/oss/cmpci.c b/trunk/sound/oss/cmpci.c new file mode 100644 index 000000000000..20628aa07a25 --- /dev/null +++ b/trunk/sound/oss/cmpci.c @@ -0,0 +1,3380 @@ +/* + * cmpci.c -- C-Media PCI audio driver. + * + * Copyright (C) 1999 C-media support (support@cmedia.com.tw) + * + * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * For update, visit: + * http://www.cmedia.com.tw + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi, Jan Pfeifer + * + * + * Module command line parameters: + * none so far + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.98 0.1 Initial release + * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in cm_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.98 0.4 Don't allow excessive interrupt rates + * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.98 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.98 0.7 Fix realplayer problems - dac.count issues + * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 18.08.99 1.5 Only deallocate DMA buffer when unloading. + * 02.09.99 1.6 Enable SPDIF LOOP + * Change the mixer read back + * 21.09.99 2.33 Use RCS version as driver version. + * Add support for modem, S/PDIF loop and 4 channels. + * (8738 only) + * Fix bug cause x11amp cannot play. + * + * Fixes: + * Arnaldo Carvalho de Melo + * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it + * was calling prog_dmabuf with s->lock held, call missing + * unlock_kernel in cm_midi_release + * 08/10/2001 - use set_current_state in some more places + * + * Carlos Eduardo Gorges + * Fri May 25 2001 + * - SMP support ( spin[un]lock* revision ) + * - speaker mixer support + * Mon Aug 13 2001 + * - optimizations and cleanups + * + * 03/01/2003 - open_mode fixes from Georg Acher + * Simon Braunschmidt + * Sat Jan 31 2004 + * - provide support for opl3 FM by releasing IO range after initialization + * + * ChenLi Tien + * Mar 9 2004 + * - Fix S/PDIF out if spdif_loop enabled + * - Load opl3 driver if enabled (fmio in proper range) + * - Load mpu401 if enabled (mpuio in proper range) + * Apr 5 2004 + * - Fix DUAL_DAC dma synchronization bug + * - Check exist FM/MPU401 I/O before activate. + * - Add AFTM_S16_BE format support, so MPlayer/Xine can play AC3/mutlichannel + * on Mac + * - Change to support kernel 2.6 so only small patch needed + * - All parameters default to 0 + * - Add spdif_out to send PCM through S/PDIF out jack + * - Add hw_copy to get 4-spaker output for general PCM/analog output + * + * Stefan Thater + * Apr 5 2004 + * - Fix mute single channel for CD/Line-in/AUX-in + */ +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SOUND_CMPCI_MIDI +#include "sound_config.h" +#include "mpu401.h" +#endif +#ifdef CONFIG_SOUND_CMPCI_FM +#include "opl3.h" +#endif +#ifdef CONFIG_SOUND_CMPCI_JOYSTICK +#include +#include + +#endif + +/* --------------------------------------------------------------------- */ +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#undef DMABYTEIO +#define DBG(x) {} +/* --------------------------------------------------------------------- */ + +#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A) + +/* CM8338 registers definition ****************/ + +#define CODEC_CMI_FUNCTRL0 (0x00) +#define CODEC_CMI_FUNCTRL1 (0x04) +#define CODEC_CMI_CHFORMAT (0x08) +#define CODEC_CMI_INT_HLDCLR (0x0C) +#define CODEC_CMI_INT_STATUS (0x10) +#define CODEC_CMI_LEGACY_CTRL (0x14) +#define CODEC_CMI_MISC_CTRL (0x18) +#define CODEC_CMI_TDMA_POS (0x1C) +#define CODEC_CMI_MIXER (0x20) +#define CODEC_SB16_DATA (0x22) +#define CODEC_SB16_ADDR (0x23) +#define CODEC_CMI_MIXER1 (0x24) +#define CODEC_CMI_MIXER2 (0x25) +#define CODEC_CMI_AUX_VOL (0x26) +#define CODEC_CMI_MISC (0x27) +#define CODEC_CMI_AC97 (0x28) + +#define CODEC_CMI_CH0_FRAME1 (0x80) +#define CODEC_CMI_CH0_FRAME2 (0x84) +#define CODEC_CMI_CH1_FRAME1 (0x88) +#define CODEC_CMI_CH1_FRAME2 (0x8C) + +#define CODEC_CMI_SPDIF_CTRL (0x90) +#define CODEC_CMI_MISC_CTRL2 (0x92) + +#define CODEC_CMI_EXT_REG (0xF0) + +/* Mixer registers for SB16 ******************/ + +#define DSP_MIX_DATARESETIDX ((unsigned char)(0x00)) + +#define DSP_MIX_MASTERVOLIDX_L ((unsigned char)(0x30)) +#define DSP_MIX_MASTERVOLIDX_R ((unsigned char)(0x31)) +#define DSP_MIX_VOICEVOLIDX_L ((unsigned char)(0x32)) +#define DSP_MIX_VOICEVOLIDX_R ((unsigned char)(0x33)) +#define DSP_MIX_FMVOLIDX_L ((unsigned char)(0x34)) +#define DSP_MIX_FMVOLIDX_R ((unsigned char)(0x35)) +#define DSP_MIX_CDVOLIDX_L ((unsigned char)(0x36)) +#define DSP_MIX_CDVOLIDX_R ((unsigned char)(0x37)) +#define DSP_MIX_LINEVOLIDX_L ((unsigned char)(0x38)) +#define DSP_MIX_LINEVOLIDX_R ((unsigned char)(0x39)) + +#define DSP_MIX_MICVOLIDX ((unsigned char)(0x3A)) +#define DSP_MIX_SPKRVOLIDX ((unsigned char)(0x3B)) + +#define DSP_MIX_OUTMIXIDX ((unsigned char)(0x3C)) + +#define DSP_MIX_ADCMIXIDX_L ((unsigned char)(0x3D)) +#define DSP_MIX_ADCMIXIDX_R ((unsigned char)(0x3E)) + +#define DSP_MIX_INGAINIDX_L ((unsigned char)(0x3F)) +#define DSP_MIX_INGAINIDX_R ((unsigned char)(0x40)) +#define DSP_MIX_OUTGAINIDX_L ((unsigned char)(0x41)) +#define DSP_MIX_OUTGAINIDX_R ((unsigned char)(0x42)) + +#define DSP_MIX_AGCIDX ((unsigned char)(0x43)) + +#define DSP_MIX_TREBLEIDX_L ((unsigned char)(0x44)) +#define DSP_MIX_TREBLEIDX_R ((unsigned char)(0x45)) +#define DSP_MIX_BASSIDX_L ((unsigned char)(0x46)) +#define DSP_MIX_BASSIDX_R ((unsigned char)(0x47)) +#define DSP_MIX_EXTENSION ((unsigned char)(0xf0)) +// pseudo register for AUX +#define DSP_MIX_AUXVOL_L ((unsigned char)(0x50)) +#define DSP_MIX_AUXVOL_R ((unsigned char)(0x51)) + +// I/O length +#define CM_EXTENT_CODEC 0x100 +#define CM_EXTENT_MIDI 0x2 +#define CM_EXTENT_SYNTH 0x4 +#define CM_EXTENT_GAME 0x8 + +// Function Control Register 0 (00h) +#define CHADC0 0x01 +#define CHADC1 0x02 +#define PAUSE0 0x04 +#define PAUSE1 0x08 + +// Function Control Register 0+2 (02h) +#define CHEN0 0x01 +#define CHEN1 0x02 +#define RST_CH0 0x04 +#define RST_CH1 0x08 + +// Function Control Register 1 (04h) +#define JYSTK_EN 0x02 +#define UART_EN 0x04 +#define SPDO2DAC 0x40 +#define SPDFLOOP 0x80 + +// Function Control Register 1+1 (05h) +#define SPDF_0 0x01 +#define SPDF_1 0x02 +#define ASFC 0x1c +#define DSFC 0xe0 +#define SPDIF2DAC (SPDF_1 << 8 | SPDO2DAC) + +// Channel Format Register (08h) +#define CM_CFMT_STEREO 0x01 +#define CM_CFMT_16BIT 0x02 +#define CM_CFMT_MASK 0x03 +#define POLVALID 0x20 +#define INVSPDIFI 0x80 + +// Channel Format Register+2 (0ah) +#define SPD24SEL 0x20 + +// Channel Format Register+3 (0bh) +#define CHB3D 0x20 +#define CHB3D5C 0x80 + +// Interrupt Hold/Clear Register+2 (0eh) +#define CH0_INT_EN 0x01 +#define CH1_INT_EN 0x02 + +// Interrupt Register (10h) +#define CHINT0 0x01 +#define CHINT1 0x02 +#define CH0BUSY 0x04 +#define CH1BUSY 0x08 + +// Legacy Control/Status Register+1 (15h) +#define EXBASEN 0x10 +#define BASE2LIN 0x20 +#define CENTR2LIN 0x40 +#define CB2LIN (BASE2LIN | CENTR2LIN) +#define CHB3D6C 0x80 + +// Legacy Control/Status Register+2 (16h) +#define DAC2SPDO 0x20 +#define SPDCOPYRHT 0x40 +#define ENSPDOUT 0x80 + +// Legacy Control/Status Register+3 (17h) +#define FMSEL 0x03 +#define VSBSEL 0x0c +#define VMPU 0x60 +#define NXCHG 0x80 + +// Miscellaneous Control Register (18h) +#define REAR2LIN 0x20 +#define MUTECH1 0x40 +#define ENCENTER 0x80 + +// Miscellaneous Control Register+1 (19h) +#define SELSPDIFI2 0x01 +#define SPDF_AC97 0x80 + +// Miscellaneous Control Register+2 (1ah) +#define AC3_EN 0x04 +#define FM_EN 0x08 +#define SPD32SEL 0x20 +#define XCHGDAC 0x40 +#define ENDBDAC 0x80 + +// Miscellaneous Control Register+3 (1bh) +#define SPDIFI48K 0x01 +#define SPDO5V 0x02 +#define N4SPK3D 0x04 +#define RESET 0x40 +#define PWD 0x80 +#define SPDIF48K (SPDIFI48K << 24 | SPDF_AC97 << 8) + +// Mixer1 (24h) +#define CDPLAY 0x01 +#define X3DEN 0x02 +#define REAR2FRONT 0x10 +#define SPK4 0x20 +#define WSMUTE 0x40 +#define FMMUTE 0x80 + +// Miscellaneous Register (27h) +#define SPDVALID 0x02 +#define CENTR2MIC 0x04 + +// Miscellaneous Register2 (92h) +#define SPD32KFMT 0x10 + +#define CM_CFMT_DACSHIFT 2 +#define CM_CFMT_ADCSHIFT 0 +#define CM_FREQ_DACSHIFT 5 +#define CM_FREQ_ADCSHIFT 2 +#define RSTDAC RST_CH1 +#define RSTADC RST_CH0 +#define ENDAC CHEN1 +#define ENADC CHEN0 +#define PAUSEDAC PAUSE1 +#define PAUSEADC PAUSE0 +#define CODEC_CMI_ADC_FRAME1 CODEC_CMI_CH0_FRAME1 +#define CODEC_CMI_ADC_FRAME2 CODEC_CMI_CH0_FRAME2 +#define CODEC_CMI_DAC_FRAME1 CODEC_CMI_CH1_FRAME1 +#define CODEC_CMI_DAC_FRAME2 CODEC_CMI_CH1_FRAME2 +#define DACINT CHINT1 +#define ADCINT CHINT0 +#define DACBUSY CH1BUSY +#define ADCBUSY CH0BUSY +#define ENDACINT CH1_INT_EN +#define ENADCINT CH0_INT_EN + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define SND_DEV_DSP16 5 + +#define NR_DEVICE 3 /* maximum number of devices */ + +#define set_dac1_rate set_adc_rate +#define set_dac1_rate_unlocked set_adc_rate_unlocked +#define stop_dac1 stop_adc +#define stop_dac1_unlocked stop_adc_unlocked +#define get_dmadac1 get_dmaadc + +static unsigned int devindex = 0; + +//*********************************************/ + +struct cm_state { + /* magic */ + unsigned int magic; + + /* list of cmedia devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + int dev_audio; /* soundcore stuff */ + int dev_mixer; + + unsigned int iosb, iobase, iosynth, + iomidi, iogame, irq; /* hardware resources */ + unsigned short deviceid; /* pci_id */ + + struct { /* mixer stuff */ + unsigned int modcnt; + unsigned short vol[13]; + } mix; + + unsigned int rateadc, ratedac; /* wave stuff */ + unsigned char fmt, enable; + + spinlock_t lock; + struct mutex open_mutex; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + + unsigned fragsize; /* redundant, but makes calculations easier */ + unsigned dmasize; + unsigned fragsamples; + unsigned dmasamples; + + unsigned mapped:1; /* OSS stuff */ + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + +#ifdef CONFIG_SOUND_CMPCI_MIDI + int midi_devc; + struct address_info mpu_data; +#endif +#ifdef CONFIG_SOUND_CMPCI_JOYSTICK + struct gameport *gameport; +#endif + + int chip_version; + int max_channels; + int curr_channels; + int capability; /* HW capability, various for chip versions */ + + int status; /* HW or SW state */ + + int spdif_counter; /* spdif frame counter */ +}; + +/* flags used for capability */ +#define CAN_AC3_HW 0x00000001 /* 037 or later */ +#define CAN_AC3_SW 0x00000002 /* 033 or later */ +#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW) +#define CAN_DUAL_DAC 0x00000004 /* 033 or later */ +#define CAN_MULTI_CH_HW 0x00000008 /* 039 or later */ +#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC) +#define CAN_LINE_AS_REAR 0x00000010 /* 033 or later */ +#define CAN_LINE_AS_BASS 0x00000020 /* 039 or later */ +#define CAN_MIC_AS_BASS 0x00000040 /* 039 or later */ + +/* flags used for status */ +#define DO_AC3_HW 0x00000001 +#define DO_AC3_SW 0x00000002 +#define DO_AC3 (DO_AC3_HW | DO_AC3_SW) +#define DO_DUAL_DAC 0x00000004 +#define DO_MULTI_CH_HW 0x00000008 +#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC) +#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */ +#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */ +#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */ +#define DO_SPDIF_OUT 0x00000100 +#define DO_SPDIF_IN 0x00000200 +#define DO_SPDIF_LOOP 0x00000400 +#define DO_BIGENDIAN_W 0x00001000 /* used in PowerPC */ +#define DO_BIGENDIAN_R 0x00002000 /* used in PowerPC */ + +static LIST_HEAD(devs); + +static int mpuio; +static int fmio; +static int joystick; +static int spdif_inverse; +static int spdif_loop; +static int spdif_out; +static int use_line_as_rear; +static int use_line_as_bass; +static int use_mic_as_bass; +static int mic_boost; +static int hw_copy; +module_param(mpuio, int, 0); +module_param(fmio, int, 0); +module_param(joystick, bool, 0); +module_param(spdif_inverse, bool, 0); +module_param(spdif_loop, bool, 0); +module_param(spdif_out, bool, 0); +module_param(use_line_as_rear, bool, 0); +module_param(use_line_as_bass, bool, 0); +module_param(use_mic_as_bass, bool, 0); +module_param(mic_boost, bool, 0); +module_param(hw_copy, bool, 0); +MODULE_PARM_DESC(mpuio, "(0x330, 0x320, 0x310, 0x300) Base of MPU-401, 0 to disable"); +MODULE_PARM_DESC(fmio, "(0x388, 0x3C8, 0x3E0) Base of OPL3, 0 to disable"); +MODULE_PARM_DESC(joystick, "(1/0) Enable joystick interface, still need joystick driver"); +MODULE_PARM_DESC(spdif_inverse, "(1/0) Invert S/PDIF-in signal"); +MODULE_PARM_DESC(spdif_loop, "(1/0) Route S/PDIF-in to S/PDIF-out directly"); +MODULE_PARM_DESC(spdif_out, "(1/0) Send PCM to S/PDIF-out (PCM volume will not function)"); +MODULE_PARM_DESC(use_line_as_rear, "(1/0) Use line-in jack as rear-out"); +MODULE_PARM_DESC(use_line_as_bass, "(1/0) Use line-in jack as bass/center"); +MODULE_PARM_DESC(use_mic_as_bass, "(1/0) Use mic-in jack as bass/center"); +MODULE_PARM_DESC(mic_boost, "(1/0) Enable microphone boost"); +MODULE_PARM_DESC(hw_copy, "Copy front channel to surround channel"); + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned exp=16,l=5,r=0; + static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; + + /* num: 2, 4, 16, 256, 65536 */ + /* exp: 1, 2, 4, 8, 16 */ + + while(l--) { + if( x >= num[l] ) { + if(num[l]>2) x >>= exp; + r+=exp; + } + exp>>=1; + } + + return r; +} + +/* --------------------------------------------------------------------- */ + +static void maskb(unsigned int addr, unsigned int mask, unsigned int value) +{ + outb((inb(addr) & mask) | value, addr); +} + +static void maskw(unsigned int addr, unsigned int mask, unsigned int value) +{ + outw((inw(addr) & mask) | value, addr); +} + +static void maskl(unsigned int addr, unsigned int mask, unsigned int value) +{ + outl((inl(addr) & mask) | value, addr); +} + +static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count) +{ + if (addr) + outl(addr, s->iobase + CODEC_CMI_ADC_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC0, 0); +} + +static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count) +{ + outl(addr, s->iobase + CODEC_CMI_ADC_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, CHADC0); +} + +static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count) +{ + outl(addr, s->iobase + CODEC_CMI_DAC_FRAME1); + outw(count - 1, s->iobase + CODEC_CMI_DAC_FRAME2); + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC1, 0); + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, 0, count); +} + +static void set_countadc(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_ADC_FRAME2 + 2); +} + +static void set_countdac(struct cm_state *s, unsigned count) +{ + outw(count - 1, s->iobase + CODEC_CMI_DAC_FRAME2 + 2); + if (s->status & DO_DUAL_DAC) + set_countadc(s, count); +} + +static unsigned get_dmadac(struct cm_state *s) +{ + unsigned int curr_addr; + + curr_addr = inw(s->iobase + CODEC_CMI_DAC_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; + curr_addr = s->dma_dac.dmasize - curr_addr; + + return curr_addr; +} + +static unsigned get_dmaadc(struct cm_state *s) +{ + unsigned int curr_addr; + + curr_addr = inw(s->iobase + CODEC_CMI_ADC_FRAME2) + 1; + curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; + curr_addr = s->dma_adc.dmasize - curr_addr; + + return curr_addr; +} + +static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data) +{ + unsigned char regval, pseudo; + + // pseudo register + if (idx == DSP_MIX_AUXVOL_L) { + data >>= 4; + data &= 0x0f; + regval = inb(s->iobase + CODEC_CMI_AUX_VOL) & ~0x0f; + outb(regval | data, s->iobase + CODEC_CMI_AUX_VOL); + return; + } + if (idx == DSP_MIX_AUXVOL_R) { + data &= 0xf0; + regval = inb(s->iobase + CODEC_CMI_AUX_VOL) & ~0xf0; + outb(regval | data, s->iobase + CODEC_CMI_AUX_VOL); + return; + } + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + // pseudo bits + if (idx == DSP_MIX_OUTMIXIDX) { + pseudo = data & ~0x1f; + pseudo >>= 1; + regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x30; + outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); + } + if (idx == DSP_MIX_ADCMIXIDX_L) { + pseudo = data & 0x80; + pseudo >>= 1; + regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x40; + outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); + } + if (idx == DSP_MIX_ADCMIXIDX_R) { + pseudo = data & 0x80; + regval = inb(s->iobase + CODEC_CMI_MIXER2) & ~0x80; + outb(regval | pseudo, s->iobase + CODEC_CMI_MIXER2); + } + outb(data, s->iobase + CODEC_SB16_DATA); + udelay(10); +} + +static unsigned char rdmixer(struct cm_state *s, unsigned char idx) +{ + unsigned char v, pseudo; + + // pseudo register + if (idx == DSP_MIX_AUXVOL_L) { + v = inb(s->iobase + CODEC_CMI_AUX_VOL) & 0x0f; + v <<= 4; + return v; + } + if (idx == DSP_MIX_AUXVOL_L) { + v = inb(s->iobase + CODEC_CMI_AUX_VOL) & 0xf0; + return v; + } + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + v = inb(s->iobase + CODEC_SB16_DATA); + udelay(10); + // pseudo bits + if (idx == DSP_MIX_OUTMIXIDX) { + pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x30; + pseudo <<= 1; + v |= pseudo; + } + if (idx == DSP_MIX_ADCMIXIDX_L) { + pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x40; + pseudo <<= 1; + v |= pseudo; + } + if (idx == DSP_MIX_ADCMIXIDX_R) { + pseudo = inb(s->iobase + CODEC_CMI_MIXER2) & 0x80; + v |= pseudo; + } + return v; +} + +static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data) +{ + if (mask && s->chip_version > 0) { /* 8338 cannot keep this */ + s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); + udelay(10); +} + +static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_fmt_unlocked(s,mask,data); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx, s->iobase + CODEC_SB16_ADDR); + udelay(10); + outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); + udelay(10); +} + +static struct { + unsigned rate; + unsigned lower; + unsigned upper; + unsigned char freq; +} rate_lookup[] = +{ + { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 }, + { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 }, + { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 }, + { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 }, + { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 }, + { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 }, + { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 }, + { 48000, (44100 + 48000) / 2, 48000, 7 } +}; + +static void set_spdif_copyright(struct cm_state *s, int spdif_copyright) +{ + /* enable SPDIF-in Copyright */ + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~SPDCOPYRHT, spdif_copyright ? SPDCOPYRHT : 0); +} + +static void set_spdif_loop(struct cm_state *s, int spdif_loop) +{ + /* enable SPDIF loop */ + if (spdif_loop) { + s->status |= DO_SPDIF_LOOP; + /* turn on spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, SPDFLOOP); + } else { + s->status &= ~DO_SPDIF_LOOP; + /* turn off spdif-in to spdif-out */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~SPDFLOOP, 0); + } +} + +static void set_spdif_monitor(struct cm_state *s, int channel) +{ + // SPDO2DAC + maskw(s->iobase + CODEC_CMI_FUNCTRL1, ~SPDO2DAC, channel == 2 ? SPDO2DAC : 0); + // CDPLAY + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~CDPLAY, channel ? CDPLAY : 0); +} + +static void set_spdifout_level(struct cm_state *s, int level5v) +{ + /* SPDO5V */ + if (s->chip_version > 0) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~SPDO5V, level5v ? SPDO5V : 0); +} + +static void set_spdifin_inverse(struct cm_state *s, int spdif_inverse) +{ + if (s->chip_version == 0) /* 8338 has not this feature */ + return; + if (spdif_inverse) { + /* turn on spdif-in inverse */ + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_CHFORMAT, ~0, INVSPDIFI); + else + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 1); + } else { + /* turn off spdif-ininverse */ + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_CHFORMAT, ~INVSPDIFI, 0); + else + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~1, 0); + } +} + +static void set_spdifin_channel2(struct cm_state *s, int channel2) +{ + /* SELSPDIFI2 */ + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 1, ~SELSPDIFI2, channel2 ? SELSPDIFI2 : 0); +} + +static void set_spdifin_valid(struct cm_state *s, int valid) +{ + /* SPDVALID */ + maskb(s->iobase + CODEC_CMI_MISC, ~SPDVALID, valid ? SPDVALID : 0); +} + +static void set_spdifout_unlocked(struct cm_state *s, unsigned rate) +{ + if (rate != 48000 && rate != 44100) + rate = 0; + if (rate == 48000 || rate == 44100) { + set_spdif_loop(s, 0); + // SPDF_1 + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0, SPDF_1); + // SPDIFI48K SPDF_AC97 + maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~SPDIF48K, rate == 48000 ? SPDIF48K : 0); + if (s->chip_version >= 55) + // SPD32KFMT + maskb(s->iobase + CODEC_CMI_MISC_CTRL2, ~SPD32KFMT, rate == 48000 ? SPD32KFMT : 0); + if (s->chip_version > 0) + // ENSPDOUT + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~0, ENSPDOUT); + // monitor SPDIF out + set_spdif_monitor(s, 2); + s->status |= DO_SPDIF_OUT; + } else { + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~SPDF_1, 0); + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 2, ~ENSPDOUT, 0); + // monitor none + set_spdif_monitor(s, 0); + s->status &= ~DO_SPDIF_OUT; + } +} + +static void set_spdifout(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout_unlocked(s,rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_spdifin_unlocked(struct cm_state *s, unsigned rate) +{ + if (rate == 48000 || rate == 44100) { + // SPDF_1 + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~0, SPDF_1); + // SPDIFI48K SPDF_AC97 + maskl(s->iobase + CODEC_CMI_MISC_CTRL, ~SPDIF48K, rate == 48000 ? SPDIF48K : 0); + s->status |= DO_SPDIF_IN; + } else { + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~SPDF_1, 0); + s->status &= ~DO_SPDIF_IN; + } +} + +static void set_spdifin(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifin_unlocked(s,rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* find parity for bit 4~30 */ +static unsigned parity(unsigned data) +{ + unsigned parity = 0; + int counter = 4; + + data >>= 4; // start from bit 4 + while (counter <= 30) { + if (data & 1) + parity++; + data >>= 1; + counter++; + } + return parity & 1; +} + +static void set_ac3_unlocked(struct cm_state *s, unsigned rate) +{ + if (!(s->capability & CAN_AC3)) + return; + /* enable AC3 */ + if (rate && rate != 44100) + rate = 48000; + if (rate == 48000 || rate == 44100) { + // mute DAC + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, WSMUTE); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~0, MUTECH1); + // AC3EN for 039, 0x04 + if (s->chip_version >= 39) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, AC3_EN); + if (s->chip_version == 55) + maskb(s->iobase + CODEC_CMI_SPDIF_CTRL, ~2, 0); + // AC3EN for 037, 0x10 + } else if (s->chip_version == 37) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x10); + if (s->capability & CAN_AC3_HW) { + // SPD24SEL for 039, 0x20, but cannot be set + if (s->chip_version == 39) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, SPD24SEL); + // SPD24SEL for 037, 0x02 + else if (s->chip_version == 37) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~0, 0x02); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~CDPLAY, 0); + + s->status |= DO_AC3_HW; + } else { + // SPD32SEL for 037 & 039 + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, SPD32SEL); + // set 176K sample rate to fix 033 HW bug + if (s->chip_version == 33) { + if (rate == 48000) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0, 0x08); + else + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + } + s->status |= DO_AC3_SW; + } + } else { + maskb(s->iobase + CODEC_CMI_MIXER1, ~WSMUTE, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~MUTECH1, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 2, ~(SPD24SEL|0x12), 0); + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~(SPD32SEL|AC3_EN), 0); + if (s->chip_version == 33) + maskb(s->iobase + CODEC_CMI_CHFORMAT + 1, ~0x08, 0); + if (s->chip_version >= 39) + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, CDPLAY); + s->status &= ~DO_AC3; + } + s->spdif_counter = 0; +} + +static void set_line_as_rear(struct cm_state *s, int use_line_as_rear) +{ + if (!(s->capability & CAN_LINE_AS_REAR)) + return; + if (use_line_as_rear) { + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, SPK4); + s->status |= DO_LINE_AS_REAR; + } else { + maskb(s->iobase + CODEC_CMI_MIXER1, ~SPK4, 0); + s->status &= ~DO_LINE_AS_REAR; + } +} + +static void set_line_as_bass(struct cm_state *s, int use_line_as_bass) +{ + if (!(s->capability & CAN_LINE_AS_BASS)) + return; + if (use_line_as_bass) { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0, CB2LIN); + s->status |= DO_LINE_AS_BASS; + } else { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CB2LIN, 0); + s->status &= ~DO_LINE_AS_BASS; + } +} + +static void set_mic_as_bass(struct cm_state *s, int use_mic_as_bass) +{ + if (!(s->capability & CAN_MIC_AS_BASS)) + return; + if (use_mic_as_bass) { + maskb(s->iobase + CODEC_CMI_MISC, ~0, 0x04); + s->status |= DO_MIC_AS_BASS; + } else { + maskb(s->iobase + CODEC_CMI_MISC, ~0x04, 0); + s->status &= ~DO_MIC_AS_BASS; + } +} + +static void set_hw_copy(struct cm_state *s, int hw_copy) +{ + if (s->max_channels > 2 && hw_copy) + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, N4SPK3D); + else + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~N4SPK3D, 0); +} + +static void set_ac3(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + set_spdifout_unlocked(s, rate); + set_ac3_unlocked(s, rate); + spin_unlock_irqrestore(&s->lock, flags); +} + +static int trans_ac3(struct cm_state *s, void *dest, const char __user *source, int size) +{ + int i = size / 2; + unsigned long data; + unsigned short data16; + unsigned long *dst = (unsigned long *) dest; + unsigned short __user *src = (unsigned short __user *)source; + int err; + + do { + if ((err = __get_user(data16, src++))) + return err; + data = (unsigned long)le16_to_cpu(data16); + data <<= 12; // ok for 16-bit data + if (s->spdif_counter == 2 || s->spdif_counter == 3) + data |= 0x40000000; // indicate AC-3 raw data + if (parity(data)) + data |= 0x80000000; // parity + if (s->spdif_counter == 0) + data |= 3; // preamble 'M' + else if (s->spdif_counter & 1) + data |= 5; // odd, 'W' + else + data |= 9; // even, 'M' + *dst++ = cpu_to_le32(data); + s->spdif_counter++; + if (s->spdif_counter == 384) + s->spdif_counter = 0; + } while (--i); + + return 0; +} + +static void set_adc_rate_unlocked(struct cm_state *s, unsigned rate) +{ + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->rateadc = rate; + freq <<= CM_FREQ_ADCSHIFT; + + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~ASFC, freq); +} + +static void set_adc_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->rateadc = rate; + freq <<= CM_FREQ_ADCSHIFT; + + spin_lock_irqsave(&s->lock, flags); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~ASFC, freq); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac_rate(struct cm_state *s, unsigned rate) +{ + unsigned long flags; + unsigned char freq = 4; + int i; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++) { + if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper) { + rate = rate_lookup[i].rate; + freq = rate_lookup[i].freq; + break; + } + } + s->ratedac = rate; + freq <<= CM_FREQ_DACSHIFT; + + spin_lock_irqsave(&s->lock, flags); + maskb(s->iobase + CODEC_CMI_FUNCTRL1 + 1, ~DSFC, freq); + spin_unlock_irqrestore(&s->lock, flags); + + if (s->curr_channels <= 2 && spdif_out) + set_spdifout(s, rate); + if (s->status & DO_DUAL_DAC) + set_dac1_rate(s, rate); +} + +/* --------------------------------------------------------------------- */ +static inline void reset_adc(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | RSTADC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + udelay(10); + outb(s->enable & ~RSTADC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); +} + +static inline void reset_dac(struct cm_state *s) +{ + /* reset bus master */ + outb(s->enable | RSTDAC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + udelay(10); + outb(s->enable & ~RSTDAC, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + if (s->status & DO_DUAL_DAC) + reset_adc(s); +} + +static inline void pause_adc(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, PAUSEADC); +} + +static inline void pause_dac(struct cm_state *s) +{ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, PAUSEDAC); + if (s->status & DO_DUAL_DAC) + pause_adc(s); +} + +static inline void disable_adc(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~ENADC; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_adc(s); +} + +static inline void disable_dac(struct cm_state *s) +{ + /* disable channel */ + s->enable &= ~ENDAC; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + reset_dac(s); + if (s->status & DO_DUAL_DAC) + disable_adc(s); +} + +static inline void enable_adc(struct cm_state *s) +{ + if (!(s->enable & ENADC)) { + /* enable channel */ + s->enable |= ENADC; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~PAUSEADC, 0); +} + +static inline void enable_dac_unlocked(struct cm_state *s) +{ + if (!(s->enable & ENDAC)) { + /* enable channel */ + s->enable |= ENDAC; + outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2); + } + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~PAUSEDAC, 0); + + if (s->status & DO_DUAL_DAC) + enable_adc(s); +} + +static inline void stop_adc_unlocked(struct cm_state *s) +{ + if (s->enable & ENADC) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~ENADCINT, 0); + disable_adc(s); + } +} + +static inline void stop_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); + +} + +static inline void stop_dac_unlocked(struct cm_state *s) +{ + if (s->enable & ENDAC) { + /* disable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~ENDACINT, 0); + disable_dac(s); + } + if (s->status & DO_DUAL_DAC) + stop_dac1_unlocked(s); +} + +static inline void stop_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + stop_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void start_adc_unlocked(struct cm_state *s) +{ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, ENADCINT); + enable_adc(s); + } +} + +static void start_adc(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + start_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1_unlocked(struct cm_state *s) +{ + if ((s->dma_adc.mapped || s->dma_adc.count > 0) && s->dma_adc.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, ENADCINT); + enable_dac_unlocked(s); + } +} + +static void start_dac_unlocked(struct cm_state *s) +{ + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + /* enable interrupt */ + maskb(s->iobase + CODEC_CMI_INT_HLDCLR + 2, ~0, ENDACINT); + enable_dac_unlocked(s); + } + if (s->status & DO_DUAL_DAC) + start_dac1_unlocked(s); +} + +static void start_dac(struct cm_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + start_dac_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); +} + +static int prog_dmabuf(struct cm_state *s, unsigned rec); + +static int set_dac_channels(struct cm_state *s, int channels) +{ + unsigned long flags; + static unsigned int fmmute = 0; + + spin_lock_irqsave(&s->lock, flags); + + if ((channels > 2) && (channels <= s->max_channels) + && (((s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK) == (CM_CFMT_STEREO | CM_CFMT_16BIT))) { + set_spdifout_unlocked(s, 0); + if (s->capability & CAN_MULTI_CH_HW) { + // NXCHG + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0, NXCHG); + // CHB3D or CHB3D5C + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~(CHB3D5C|CHB3D), channels > 4 ? CHB3D5C : CHB3D); + // CHB3D6C + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CHB3D6C, channels == 6 ? CHB3D6C : 0); + // ENCENTER + maskb(s->iobase + CODEC_CMI_MISC_CTRL, ~ENCENTER, channels == 6 ? ENCENTER : 0); + s->status |= DO_MULTI_CH_HW; + } else if (s->capability & CAN_DUAL_DAC) { + unsigned char fmtm = ~0, fmts = 0; + ssize_t ret; + + // ENDBDAC, turn on double DAC mode + // XCHGDAC, CH0 -> back, CH1->front + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, ENDBDAC|XCHGDAC); + // mute FM + fmmute = inb(s->iobase + CODEC_CMI_MIXER1) & FMMUTE; + maskb(s->iobase + CODEC_CMI_MIXER1, ~0, FMMUTE); + s->status |= DO_DUAL_DAC; + // prepare secondary buffer + spin_unlock_irqrestore(&s->lock, flags); + ret = prog_dmabuf(s, 1); + if (ret) return ret; + spin_lock_irqsave(&s->lock, flags); + + // copy the hw state + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + // the HW only support 16-bit stereo + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + + set_fmt_unlocked(s, fmtm, fmts); + set_adc_rate_unlocked(s, s->ratedac); + } + // disable 4 speaker mode (analog duplicate) + set_hw_copy(s, 0); + s->curr_channels = channels; + + // enable jack redirect + set_line_as_rear(s, use_line_as_rear); + if (channels > 4) { + set_line_as_bass(s, use_line_as_bass); + set_mic_as_bass(s, use_mic_as_bass); + } + } else { + if (s->status & DO_MULTI_CH_HW) { + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~NXCHG, 0); + maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~(CHB3D5C|CHB3D), 0); + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~CHB3D6C, 0); + } else if (s->status & DO_DUAL_DAC) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~ENDBDAC, 0); + maskb(s->iobase + CODEC_CMI_MIXER1, ~FMMUTE, fmmute); + } + // enable 4 speaker mode (analog duplicate) + set_hw_copy(s, hw_copy); + s->status &= ~DO_MULTI_CH; + s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + // disable jack redirect + set_line_as_rear(s, hw_copy ? use_line_as_rear : 0); + set_line_as_bass(s, 0); + set_mic_as_bass(s, 0); + } + spin_unlock_irqrestore(&s->lock, flags); + return s->curr_channels; +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void dealloc_dmabuf(struct cm_state *s, struct dmabuf *db) +{ + struct page *pstart, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) + ClearPageReserved(pstart); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +/* Ch1 is used for playback, Ch0 is used for recording */ + +static int prog_dmabuf(struct cm_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *pstart, *pend; + unsigned char fmt; + unsigned long flags; + + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= CM_CFMT_ADCSHIFT; + } else { + stop_dac(s); + fmt >>= CM_CFMT_DACSHIFT; + } + + fmt &= CM_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf || !db->dmaaddr) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++) + SetPageReserved(pstart); + } + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + /* to make fragsize >= 4096 */ + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + db->dmasamples = db->dmasize >> sample_shift[fmt]; + memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + if (s->status & DO_DUAL_DAC) + set_dmadac1(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); + else + set_dmaadc(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); + /* program sample counts */ + set_countdac(s, db->fragsamples); + } else { + set_dmadac(s, db->dmaaddr, db->dmasize >> sample_shift[fmt]); + /* program sample counts */ + set_countdac(s, db->fragsamples); + } + spin_unlock_irqrestore(&s->lock, flags); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline void clear_advance(struct cm_state *s) +{ + unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned char *buf1 = s->dma_adc.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); + if (s->status & DO_DUAL_DAC) + memset(buf1 + bptr, c, len); +} + +/* call with spinlock held! */ +static void cm_update_ptr(struct cm_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + if (s->status & DO_DUAL_DAC) { + /* the dac part will finish for this */ + } else { + hwptr = get_dmaadc(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + pause_adc(s); + s->dma_adc.error++; + } + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmadac(s) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->status & DO_DUAL_DAC) { + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + } + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->status & DO_DUAL_DAC) + s->dma_adc.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->status & DO_DUAL_DAC) + s->dma_adc.count -= diff; + if (s->dma_dac.count <= 0) { + pause_dac(s); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + if (s->status & DO_DUAL_DAC) + s->dma_adc.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +static irqreturn_t cm_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cm_state *s = (struct cm_state *)dev_id; + unsigned int intsrc, intstat; + unsigned char mask = 0; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS); + if (!(intsrc & 0x80000000)) + return IRQ_NONE; + spin_lock(&s->lock); + intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2); + /* acknowledge interrupt */ + if (intsrc & ADCINT) + mask |= ENADCINT; + if (intsrc & DACINT) + mask |= ENDACINT; + outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); + cm_update_ptr(s); + spin_unlock(&s->lock); +#ifdef CONFIG_SOUND_CMPCI_MIDI + if (intsrc & 0x00010000) { // UART interrupt + if (s->midi_devc && intchk_mpu401((void *)s->midi_devc)) + mpuintr(irq, (void *)s->midi_devc, regs); + else + inb(s->iomidi);// dummy read + } +#endif + return IRQ_HANDLED; +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CM_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 +#define MT_5MUTEMONO 5 + +static const struct { + unsigned left; + unsigned right; + unsigned type; + unsigned rec; + unsigned play; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x06 }, + [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x18 }, + [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, DSP_MIX_MICVOLIDX, MT_5MUTEMONO, 0x01, 0x01 }, + [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 }, + [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, + [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }, + [SOUND_MIXER_LINE1] = { DSP_MIX_AUXVOL_L, DSP_MIX_AUXVOL_R, MT_5MUTE, 0x80, 0x60 }, + [SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX, DSP_MIX_SPKRVOLIDX, MT_5MUTEMONO, 0x00, 0x01 } +}; + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + [SOUND_MIXER_CD] = 1, + [SOUND_MIXER_LINE] = 2, + [SOUND_MIXER_MIC] = 3, + [SOUND_MIXER_SYNTH] = 4, + [SOUND_MIXER_VOLUME] = 5, + [SOUND_MIXER_PCM] = 6, + [SOUND_MIXER_LINE1] = 7, + [SOUND_MIXER_SPEAKER]= 8 +}; + +static unsigned mixer_outmask(struct cm_state *s) +{ + unsigned long flags; + int i, j, k; + + spin_lock_irqsave(&s->lock, flags); + j = rdmixer(s, DSP_MIX_OUTMIXIDX); + spin_unlock_irqrestore(&s->lock, flags); + for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (j & mixtable[i].play) + k |= 1 << i; + return k; +} + +static unsigned mixer_recmask(struct cm_state *s) +{ + unsigned long flags; + int i, j, k; + + spin_lock_irqsave(&s->lock, flags); + j = rdmixer(s, DSP_MIX_ADCMIXIDX_L); + spin_unlock_irqrestore(&s->lock, flags); + for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (j & mixtable[i].rec) + k |= 1 << i; + return k; +} + +static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val, j; + unsigned char l, r, rl, rr; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.id, "cmpci", sizeof(info.id)); + strlcpy(info.name, "C-Media PCI", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.id, "cmpci", sizeof(info.id)); + strlcpy(info.name, "C-Media cmpci", sizeof(info.name)); + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, p); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + val = mixer_recmask(s); + return put_user(val, p); + + case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ + val = mixer_outmask(s); + return put_user(val, p); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].play) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_CAPS: + return put_user(0, p); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (!volidx[i]) + return -EINVAL; + return put_user(s->mix.vol[volidx[i]-1], p); + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, p)) + return -EFAULT; + i = hweight32(val); + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].rec) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].rec; + } + spin_lock_irqsave(&s->lock, flags); + wrmixer(s, DSP_MIX_ADCMIXIDX_L, j); + wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | (j>>1) | (j & 0x80)); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, p)) + return -EFAULT; + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].play) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].play; + } + spin_lock_irqsave(&s->lock, flags); + wrmixer(s, DSP_MIX_OUTMIXIDX, j); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + l = val & 0xff; + r = (val >> 8) & 0xff; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rl = (l < 4 ? 0 : (l - 5) / 3) & 31; + rr = (rl >> 2) & 7; + wrmixer(s, mixtable[i].left, rl<<3); + if (i == SOUND_MIXER_MIC) + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); + break; + + case MT_5MUTEMONO: + rl = l < 4 ? 0 : (l - 5) / 3; + wrmixer(s, mixtable[i].left, rl<<3); + l = rdmixer(s, DSP_MIX_OUTMIXIDX) & ~mixtable[i].play; + r = rl ? mixtable[i].play : 0; + wrmixer(s, DSP_MIX_OUTMIXIDX, l | r); + /* for recording */ + if (i == SOUND_MIXER_MIC) { + if (s->chip_version >= 37) { + rr = rl >> 1; + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, (rr&0x07)<<1); + frobindir(s, DSP_MIX_EXTENSION, ~0x01, rr>>3); + } else { + rr = rl >> 2; + maskb(s->iobase + CODEC_CMI_MIXER2, ~0x0e, rr<<1); + } + } + break; + + case MT_5MUTE: + rl = l < 4 ? 0 : (l - 5) / 3; + rr = r < 4 ? 0 : (r - 5) / 3; + wrmixer(s, mixtable[i].left, rl<<3); + wrmixer(s, mixtable[i].right, rr<<3); + l = rdmixer(s, DSP_MIX_OUTMIXIDX); + l &= ~mixtable[i].play; + r = (rl|rr) ? mixtable[i].play : 0; + wrmixer(s, DSP_MIX_OUTMIXIDX, l | r); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x00; + else + rl = l * 2 / 3; + if (r < 6) + rr = 0x00; + else + rr = r * 2 / 3; + wrmixer(s, mixtable[i].left, rl); + wrmixer(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); + + if (!volidx[i]) + return -EINVAL; + s->mix.vol[volidx[i]-1] = val; + return put_user(s->mix.vol[volidx[i]-1], p); + } +} + +/* --------------------------------------------------------------------- */ + +static int cm_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct list_head *list; + struct cm_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct cm_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return nonseekable_open(inode, file); +} + +static int cm_release_mixdev(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations cm_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = cm_ioctl_mixdev, + .open = cm_open_mixdev, + .release = cm_release_mixdev, +}; + + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct cm_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; + tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "cmpci: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t cm_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "cmpci: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + set_dmaadc(s, s->dma_adc.dmaaddr, s->dma_adc.dmasamples); + /* program sample counts */ + set_countadc(s, s->dma_adc.fragsamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + continue; + } + if (s->status & DO_BIGENDIAN_R) { + int i, err; + unsigned char *src; + char __user *dst = buffer; + unsigned char data[2]; + + src = (unsigned char *) (s->dma_adc.rawbuf + swptr); + // copy left/right sample at one time + for (i = 0; i < cnt / 2; i++) { + data[0] = src[1]; + data[1] = src[0]; + if ((err = __put_user(data[0], dst++))) { + ret = err; + goto out; + } + if ((err = __put_user(data[1], dst++))) { + ret = err; + goto out; + } + src += 2; + } + } else if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc_unlocked(s); + spin_unlock_irqrestore(&s->lock, flags); + } +out: + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t cm_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + } + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->status & DO_DUAL_DAC) { + s->dma_adc.swptr = s->dma_dac.swptr; + s->dma_adc.count = s->dma_dac.count; + s->dma_adc.endcleared = s->dma_dac.endcleared; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->status & DO_AC3_SW) { + if (s->dma_dac.count + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - s->dma_dac.count) / 2; + } else { + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + } + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if ((s->status & DO_DUAL_DAC) && (cnt > count / 2)) + cnt = count / 2; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "cmpci: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + spin_lock_irqsave(&s->lock, flags); + stop_dac_unlocked(s); + set_dmadac(s, s->dma_dac.dmaaddr, s->dma_dac.dmasamples); + /* program sample counts */ + set_countdac(s, s->dma_dac.fragsamples); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + if (s->status & DO_DUAL_DAC) { + set_dmadac1(s, s->dma_adc.dmaaddr, s->dma_adc.dmasamples); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + continue; + } + if (s->status & DO_AC3_SW) { + int err; + + // clip exceeded data, caught by 033 and 037 + if (swptr + 2 * cnt > s->dma_dac.dmasize) + cnt = (s->dma_dac.dmasize - swptr) / 2; + if ((err = trans_ac3(s, s->dma_dac.rawbuf + swptr, buffer, cnt))) { + ret = err; + goto out; + } + swptr = (swptr + 2 * cnt) % s->dma_dac.dmasize; + } else if ((s->status & DO_DUAL_DAC) && (s->status & DO_BIGENDIAN_W)) { + int i, err; + const char __user *src = buffer; + unsigned char *dst0, *dst1; + unsigned char data[8]; + + dst0 = (unsigned char *) (s->dma_dac.rawbuf + swptr); + dst1 = (unsigned char *) (s->dma_adc.rawbuf + swptr); + // copy left/right sample at one time + for (i = 0; i < cnt / 4; i++) { + if ((err = __get_user(data[0], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[1], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[2], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[3], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[4], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[5], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[6], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[7], src++))) { + ret = err; + goto out; + } + dst0[0] = data[1]; + dst0[1] = data[0]; + dst0[2] = data[3]; + dst0[3] = data[2]; + dst1[0] = data[5]; + dst1[1] = data[4]; + dst1[2] = data[7]; + dst1[3] = data[6]; + dst0 += 4; + dst1 += 4; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } else if (s->status & DO_DUAL_DAC) { + int i, err; + unsigned long __user *src = (unsigned long __user *) buffer; + unsigned long *dst0, *dst1; + + dst0 = (unsigned long *) (s->dma_dac.rawbuf + swptr); + dst1 = (unsigned long *) (s->dma_adc.rawbuf + swptr); + // copy left/right sample at one time + for (i = 0; i < cnt / 4; i++) { + if ((err = __get_user(*dst0++, src++))) { + ret = err; + goto out; + } + if ((err = __get_user(*dst1++, src++))) { + ret = err; + goto out; + } + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } else if (s->status & DO_BIGENDIAN_W) { + int i, err; + const char __user *src = buffer; + unsigned char *dst; + unsigned char data[2]; + + dst = (unsigned char *) (s->dma_dac.rawbuf + swptr); + // swap hi/lo bytes for each sample + for (i = 0; i < cnt / 2; i++) { + if ((err = __get_user(data[0], src++))) { + ret = err; + goto out; + } + if ((err = __get_user(data[1], src++))) { + ret = err; + goto out; + } + dst[0] = data[1]; + dst[1] = data[0]; + dst += 2; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } else { + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + if (s->status & DO_AC3_SW) + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->status & DO_DUAL_DAC) { + count -= cnt; + buffer += cnt; + ret += cnt; + } + if (s->dma_dac.enabled) + start_dac(s); + } +out: + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 0)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 1)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int cm_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EINVAL; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +#define SNDCTL_SPDIF_COPYRIGHT _SIOW('S', 0, int) // set/reset S/PDIF copy protection +#define SNDCTL_SPDIF_LOOP _SIOW('S', 1, int) // set/reset S/PDIF loop +#define SNDCTL_SPDIF_MONITOR _SIOW('S', 2, int) // set S/PDIF monitor +#define SNDCTL_SPDIF_LEVEL _SIOW('S', 3, int) // set/reset S/PDIF out level +#define SNDCTL_SPDIF_INV _SIOW('S', 4, int) // set/reset S/PDIF in inverse +#define SNDCTL_SPDIF_SEL2 _SIOW('S', 5, int) // set S/PDIF in #2 +#define SNDCTL_SPDIF_VALID _SIOW('S', 6, int) // set S/PDIF valid +#define SNDCTL_SPDIFOUT _SIOW('S', 7, int) // set S/PDIF out +#define SNDCTL_SPDIFIN _SIOW('S', 8, int) // set S/PDIF out + +static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_BIND, p); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + s->dma_adc.ready = 0; + set_adc_rate_unlocked(s, val); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (s->status & DO_DUAL_DAC) + s->dma_adc.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + } + set_fmt(s, fmtm, fmtd); + if ((s->capability & CAN_MULTI_CH) + && (file->f_mode & FMODE_WRITE)) { + val = set_dac_channels(s, val); + return put_user(val, p); + } + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) + : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_BE|AFMT_S16_LE|AFMT_U8| + ((s->capability & CAN_AC3) ? AFMT_AC3 : 0), p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_BE || val == AFMT_S16_LE) + fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT); + if (val == AFMT_S16_BE) + s->status |= DO_BIGENDIAN_R; + else + s->status &= ~DO_BIGENDIAN_R; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_BE || val == AFMT_S16_LE || val == AFMT_AC3) + fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + else + fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT); + if (val == AFMT_AC3) { + fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT; + set_ac3(s, 48000); + } else + set_ac3(s, 0); + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ready = 0; + if (val == AFMT_S16_BE || val == AFMT_S16_LE) + fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT; + else + fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT); + } + if (val == AFMT_S16_BE) + s->status |= DO_BIGENDIAN_W; + else + s->status &= ~DO_BIGENDIAN_W; + } + set_fmt(s, fmtm, fmtd); + } + if (s->status & DO_AC3) return put_user(AFMT_AC3, p); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) + : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? val : AFMT_U8, p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (s->status & DO_DUAL_DAC) { + if (file->f_mode & FMODE_WRITE && + (s->enable & ENDAC) && + (s->enable & ENADC)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + } + if (file->f_mode & FMODE_READ && s->enable & ENADC) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & ENDAC) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (s->status & DO_DUAL_DAC) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + } + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & ENDAC) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->enable & ENADC) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cm_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + if (s->status & DO_DUAL_DAC) { + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + if (s->status & DO_DUAL_DAC) { + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(2 * s->dma_dac.fragsize, p); + } + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if (s->status & DO_DUAL_DAC) { + s->dma_adc.ossfragshift = s->dma_dac.ossfragshift; + s->dma_adc.ossmaxfrags = s->dma_dac.ossmaxfrags; + } + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.subdivision = val; + if (s->status & DO_DUAL_DAC) + s->dma_adc.subdivision = val; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, p); + + case SOUND_PCM_READ_FILTER: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SNDCTL_DSP_GETCHANNELMASK: + return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE|DSP_BIND_SPDIF, p); + + case SNDCTL_DSP_BIND_CHANNEL: + if (get_user(val, p)) + return -EFAULT; + if (val == DSP_BIND_QUERY) { + val = DSP_BIND_FRONT; + if (s->status & DO_SPDIF_OUT) + val |= DSP_BIND_SPDIF; + else { + if (s->curr_channels == 4) + val |= DSP_BIND_SURR; + if (s->curr_channels > 4) + val |= DSP_BIND_CENTER_LFE; + } + } else { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val & DSP_BIND_SPDIF) { + set_spdifin(s, s->rateadc); + if (!(s->status & DO_SPDIF_OUT)) + val &= ~DSP_BIND_SPDIF; + } + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val & DSP_BIND_SPDIF) { + set_spdifout(s, s->ratedac); + set_dac_channels(s, s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1); + if (!(s->status & DO_SPDIF_OUT)) + val &= ~DSP_BIND_SPDIF; + } else { + int channels; + int mask; + + mask = val & (DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE); + switch (mask) { + case DSP_BIND_FRONT: + channels = 2; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR: + channels = 4; + break; + case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE: + channels = 6; + break; + default: + channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1; + break; + } + set_dac_channels(s, channels); + } + } + } + return put_user(val, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + return -EINVAL; + case SNDCTL_SPDIF_COPYRIGHT: + if (get_user(val, p)) + return -EFAULT; + set_spdif_copyright(s, val); + return 0; + case SNDCTL_SPDIF_LOOP: + if (get_user(val, p)) + return -EFAULT; + set_spdif_loop(s, val); + return 0; + case SNDCTL_SPDIF_MONITOR: + if (get_user(val, p)) + return -EFAULT; + set_spdif_monitor(s, val); + return 0; + case SNDCTL_SPDIF_LEVEL: + if (get_user(val, p)) + return -EFAULT; + set_spdifout_level(s, val); + return 0; + case SNDCTL_SPDIF_INV: + if (get_user(val, p)) + return -EFAULT; + set_spdifin_inverse(s, val); + return 0; + case SNDCTL_SPDIF_SEL2: + if (get_user(val, p)) + return -EFAULT; + set_spdifin_channel2(s, val); + return 0; + case SNDCTL_SPDIF_VALID: + if (get_user(val, p)) + return -EFAULT; + set_spdifin_valid(s, val); + return 0; + case SNDCTL_SPDIFOUT: + if (get_user(val, p)) + return -EFAULT; + set_spdifout(s, val ? s->ratedac : 0); + return 0; + case SNDCTL_SPDIFIN: + if (get_user(val, p)) + return -EFAULT; + set_spdifin(s, val ? s->rateadc : 0); + return 0; + } + return mixer_ioctl(s, cmd, arg); +} + +static int cm_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned char fmtm = ~0, fmts = 0; + struct list_head *list; + struct cm_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct cm_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + if (file->f_mode & FMODE_READ) { + s->status &= ~DO_BIGENDIAN_R; + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + set_adc_rate(s, 8000); + // spdif-in is turnned off by default + set_spdifin(s, 0); + } + if (file->f_mode & FMODE_WRITE) { + s->status &= ~DO_BIGENDIAN_W; + fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + set_dac_rate(s, 8000); + // clear previous multichannel, spdif, ac3 state + set_spdifout(s, 0); + set_ac3(s, 0); + set_dac_channels(s, 1); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int cm_release(struct inode *inode, struct file *file) +{ + struct cm_state *s = (struct cm_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_mutex); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + + dealloc_dmabuf(s, &s->dma_dac); + if (s->status & DO_DUAL_DAC) + dealloc_dmabuf(s, &s->dma_adc); + + if (s->status & DO_MULTI_CH) + set_dac_channels(s, 1); + if (s->status & DO_AC3) + set_ac3(s, 0); + if (s->status & DO_SPDIF_OUT) + set_spdifout(s, 0); + /* enable SPDIF loop */ + set_spdif_loop(s, spdif_loop); + s->status &= ~DO_BIGENDIAN_W; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + s->status &= ~DO_BIGENDIAN_R; + } + s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); + mutex_unlock(&s->open_mutex); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations cm_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = cm_read, + .write = cm_write, + .poll = cm_poll, + .ioctl = cm_ioctl, + .mmap = cm_mmap, + .open = cm_open, + .release = cm_release, +}; + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __devinitdata = { + { SOUND_MIXER_WRITE_CD, 0x4f4f }, + { SOUND_MIXER_WRITE_LINE, 0x4f4f }, + { SOUND_MIXER_WRITE_MIC, 0x4f4f }, + { SOUND_MIXER_WRITE_SYNTH, 0x4f4f }, + { SOUND_MIXER_WRITE_VOLUME, 0x4f4f }, + { SOUND_MIXER_WRITE_PCM, 0x4f4f } +}; + +/* check chip version and capability */ +static int query_chip(struct cm_state *s) +{ + int ChipVersion = -1; + unsigned char RegValue; + + // check reg 0Ch, bit 24-31 + RegValue = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 3); + if (RegValue == 0) { + // check reg 08h, bit 24-28 + RegValue = inb(s->iobase + CODEC_CMI_CHFORMAT + 3); + RegValue &= 0x1f; + if (RegValue == 0) { + ChipVersion = 33; + s->max_channels = 4; + s->capability |= CAN_AC3_SW; + s->capability |= CAN_DUAL_DAC; + } else { + ChipVersion = 37; + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + } + } else { + // check reg 0Ch, bit 26 + if (RegValue & (1 << (26-24))) { + ChipVersion = 39; + if (RegValue & (1 << (24-24))) + s->max_channels = 6; + else + s->max_channels = 4; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + s->capability |= CAN_LINE_AS_BASS; + s->capability |= CAN_MIC_AS_BASS; + } else { + ChipVersion = 55; // 4 or 6 channels + s->max_channels = 6; + s->capability |= CAN_AC3_HW; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_MULTI_CH_HW; + s->capability |= CAN_LINE_AS_BASS; + s->capability |= CAN_MIC_AS_BASS; + } + } + s->capability |= CAN_LINE_AS_REAR; + return ChipVersion; +} + +#ifdef CONFIG_SOUND_CMPCI_JOYSTICK +static int __devinit cm_create_gameport(struct cm_state *s, int io_port) +{ + struct gameport *gp; + + if (!request_region(io_port, CM_EXTENT_GAME, "cmpci GAME")) { + printk(KERN_ERR "cmpci: gameport io ports 0x%#x in use\n", io_port); + return -EBUSY; + } + + if (!(s->gameport = gp = gameport_allocate_port())) { + printk(KERN_ERR "cmpci: can not allocate memory for gameport\n"); + release_region(io_port, CM_EXTENT_GAME); + return -ENOMEM; + } + + gameport_set_name(gp, "C-Media GP"); + gameport_set_phys(gp, "pci%s/gameport0", pci_name(s->dev)); + gp->dev.parent = &s->dev->dev; + gp->io = io_port; + + /* enable joystick */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x02); + + gameport_register_port(gp); + + return 0; +} + +static void __devexit cm_free_gameport(struct cm_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + + gameport_unregister_port(s->gameport); + s->gameport = NULL; + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); + release_region(gpio, CM_EXTENT_GAME); + } +} +#else +static inline int cm_create_gameport(struct cm_state *s, int io_port) { return -ENOSYS; } +static inline void cm_free_gameport(struct cm_state *s) { } +#endif + +#define echo_option(x)\ +if (x) strcat(options, "" #x " ") + +static int __devinit cm_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct cm_state *s; + mm_segment_t fs; + int i, val, ret; + unsigned char reg_mask; + int timeout; + struct resource *ports; + struct { + unsigned short deviceid; + char *devicename; + } devicetable[] = { + { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" }, + { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" }, + { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" }, + { PCI_DEVICE_ID_CMEDIA_CM8738B, "CM8738B" }, + }; + char *devicename = "unknown"; + char options[256]; + + if ((ret = pci_enable_device(pcidev))) + return ret; + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + i = pci_set_dma_mask(pcidev, DMA_32BIT_MASK); + if (i) { + printk(KERN_WARNING "cmpci: architecture does not support 32bit PCI busmaster DMA\n"); + return i; + } + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + printk(KERN_WARNING "cmpci: out of memory\n"); + return -ENOMEM; + } + /* search device name */ + for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++) { + if (devicetable[i].deviceid == pcidev->device) { + devicename = devicetable[i].devicename; + break; + } + } + memset(s, 0, sizeof(struct cm_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + mutex_init(&s->open_mutex); + spin_lock_init(&s->lock); + s->magic = CM_MAGIC; + s->dev = pcidev; + s->iobase = pci_resource_start(pcidev, 0); + s->iosynth = fmio; + s->iomidi = mpuio; +#ifdef CONFIG_SOUND_CMPCI_MIDI + s->midi_devc = 0; +#endif + s->status = 0; + if (s->iobase == 0) + return -ENODEV; + s->irq = pcidev->irq; + + if (!request_region(s->iobase, CM_EXTENT_CODEC, "cmpci")) { + printk(KERN_ERR "cmpci: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1); + ret = -EBUSY; + goto err_region5; + } + /* dump parameters */ + strcpy(options, "cmpci: "); + echo_option(joystick); + echo_option(spdif_inverse); + echo_option(spdif_loop); + echo_option(spdif_out); + echo_option(use_line_as_rear); + echo_option(use_line_as_bass); + echo_option(use_mic_as_bass); + echo_option(mic_boost); + echo_option(hw_copy); + printk(KERN_INFO "%s\n", options); + + /* initialize codec registers */ + outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ + /* reset mixer */ + wrmixer(s, DSP_MIX_DATARESETIDX, 0); + + /* request irq */ + if ((ret = request_irq(s->irq, cm_interrupt, IRQF_SHARED, "cmpci", s))) { + printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "cmpci: found %s adapter at io %#x irq %u\n", + devicename, s->iobase, s->irq); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + pci_set_master(pcidev); /* enable bus mastering */ + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + /* set mixer output */ + frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f); + /* set mixer input */ + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* use channel 1 for playback, channel 0 for record */ + maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~CHADC1, CHADC0); + /* turn off VMIC3 - mic boost */ + if (mic_boost) + maskb(s->iobase + CODEC_CMI_MIXER2, ~1, 0); + else + maskb(s->iobase + CODEC_CMI_MIXER2, ~0, 1); + s->deviceid = pcidev->device; + + if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738 + || pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { + + /* chip version and hw capability check */ + s->chip_version = query_chip(s); + printk(KERN_INFO "cmpci: chip version = 0%d\n", s->chip_version); + + /* set SPDIF-in inverse before enable SPDIF loop */ + set_spdifin_inverse(s, spdif_inverse); + + /* use SPDIF in #1 */ + set_spdifin_channel2(s, 0); + } else { + s->chip_version = 0; + /* 8338 will fall here */ + s->max_channels = 4; + s->capability |= CAN_DUAL_DAC; + s->capability |= CAN_LINE_AS_REAR; + } + /* enable SPDIF loop */ + set_spdif_loop(s, spdif_loop); + + // enable 4 speaker mode (analog duplicate) + set_hw_copy(s, hw_copy); + + reg_mask = 0; +#ifdef CONFIG_SOUND_CMPCI_FM + /* disable FM */ + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); + if (s->iosynth) { + /* don't enable OPL3 if there is one */ + if (opl3_detect(s->iosynth, NULL)) { + s->iosynth = 0; + } else { + /* set IO based at 0x388 */ + switch (s->iosynth) { + case 0x388: + reg_mask = 0; + break; + case 0x3C8: + reg_mask = 0x01; + break; + case 0x3E0: + reg_mask = 0x02; + break; + case 0x3E8: + reg_mask = 0x03; + break; + default: + s->iosynth = 0; + break; + } + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x03, reg_mask); + /* enable FM */ + if (s->iosynth) { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0, 8); + if (opl3_detect(s->iosynth, NULL)) + ret = opl3_init(s->iosynth, NULL, THIS_MODULE); + else { + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); + s->iosynth = 0; + } + } + } + } +#endif +#ifdef CONFIG_SOUND_CMPCI_MIDI + switch (s->iomidi) { + case 0x330: + reg_mask = 0; + break; + case 0x320: + reg_mask = 0x20; + break; + case 0x310: + reg_mask = 0x40; + break; + case 0x300: + reg_mask = 0x60; + break; + default: + s->iomidi = 0; + goto skip_mpu; + } + ports = request_region(s->iomidi, 2, "mpu401"); + if (!ports) + goto skip_mpu; + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); + s->mpu_data.name = "cmpci mpu"; + s->mpu_data.io_base = s->iomidi; + s->mpu_data.irq = -s->irq; // tell mpu401 to share irq + if (probe_mpu401(&s->mpu_data, ports)) { + release_region(s->iomidi, 2); + s->iomidi = 0; + goto skip_mpu; + } + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x60, reg_mask); + /* enable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + /* clear all previously received interrupt */ + for (timeout = 900000; timeout > 0; timeout--) { + if ((inb(s->iomidi + 1) && 0x80) == 0) + inb(s->iomidi); + else + break; + } + if (!probe_mpu401(&s->mpu_data, ports)) { + release_region(s->iomidi, 2); + s->iomidi = 0; + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + } else { + attach_mpu401(&s->mpu_data, THIS_MODULE); + s->midi_devc = s->mpu_data.slots[1]; + } +skip_mpu: +#endif + /* disable joystick port */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x02, 0); + if (joystick) + cm_create_gameport(s, 0x200); + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + +err_dev2: + unregister_sound_dsp(s->dev_audio); +err_dev1: + printk(KERN_ERR "cmpci: cannot register misc device\n"); + free_irq(s->irq, s); +err_irq: + release_region(s->iobase, CM_EXTENT_CODEC); +err_region5: + kfree(s); + return ret; +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("ChenLi Tien, cltien@cmedia.com.tw"); +MODULE_DESCRIPTION("CM8x38 Audio Driver"); +MODULE_LICENSE("GPL"); + +static void __devexit cm_remove(struct pci_dev *dev) +{ + struct cm_state *s = pci_get_drvdata(dev); + + if (!s) + return; + + cm_free_gameport(s); + +#ifdef CONFIG_SOUND_CMPCI_FM + if (s->iosynth) { + /* disable FM */ + maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~8, 0); + } +#endif +#ifdef CONFIG_SOUND_CMPCI_MIDI + if (s->iomidi) { + unload_mpu401(&s->mpu_data); + /* disable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); + } +#endif + set_spdif_loop(s, 0); + list_del(&s->devs); + outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */ + synchronize_irq(s->irq); + outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* disable channels */ + free_irq(s->irq, s); + + /* reset mixer */ + wrmixer(s, DSP_MIX_DATARESETIDX, 0); + + release_region(s->iobase, CM_EXTENT_CODEC); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver cm_driver = { + .name = "cmpci", + .id_table = id_table, + .probe = cm_probe, + .remove = __devexit_p(cm_remove) +}; + +static int __init init_cmpci(void) +{ + printk(KERN_INFO "cmpci: version $Revision: 6.82 $ time " __TIME__ " " __DATE__ "\n"); + return pci_register_driver(&cm_driver); +} + +static void __exit cleanup_cmpci(void) +{ + printk(KERN_INFO "cmpci: unloading\n"); + pci_unregister_driver(&cm_driver); +} + +module_init(init_cmpci); +module_exit(cleanup_cmpci); diff --git a/trunk/sound/oss/cs4281/Makefile b/trunk/sound/oss/cs4281/Makefile new file mode 100644 index 000000000000..6d527e8530d6 --- /dev/null +++ b/trunk/sound/oss/cs4281/Makefile @@ -0,0 +1,6 @@ +# Makefile for Cirrus Logic-Crystal CS4281 +# + +obj-$(CONFIG_SOUND_CS4281) += cs4281.o + +cs4281-objs += cs4281m.o diff --git a/trunk/sound/oss/cs4281/cs4281_hwdefs.h b/trunk/sound/oss/cs4281/cs4281_hwdefs.h new file mode 100644 index 000000000000..701d595e33f5 --- /dev/null +++ b/trunk/sound/oss/cs4281/cs4281_hwdefs.h @@ -0,0 +1,1234 @@ +//**************************************************************************** +// +// HWDEFS.H - Definitions of the registers and data structures used by the +// CS4281 +// +// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp. +// +//**************************************************************************** + +#ifndef _H_HWDEFS +#define _H_HWDEFS + +//**************************************************************************** +// +// The following define the offsets of the registers located in the PCI +// configuration space of the CS4281 part. +// +//**************************************************************************** +#define PCICONFIG_DEVID_VENID 0x00000000L +#define PCICONFIG_STATUS_COMMAND 0x00000004L +#define PCICONFIG_CLASS_REVISION 0x00000008L +#define PCICONFIG_LATENCY_TIMER 0x0000000CL +#define PCICONFIG_BA0 0x00000010L +#define PCICONFIG_BA1 0x00000014L +#define PCICONFIG_SUBSYSID_SUBSYSVENID 0x0000002CL +#define PCICONFIG_INTERRUPT 0x0000003CL + +//**************************************************************************** +// +// The following define the offsets of the registers accessed via base address +// register zero on the CS4281 part. +// +//**************************************************************************** +#define BA0_HISR 0x00000000L +#define BA0_HICR 0x00000008L +#define BA0_HIMR 0x0000000CL +#define BA0_IIER 0x00000010L +#define BA0_HDSR0 0x000000F0L +#define BA0_HDSR1 0x000000F4L +#define BA0_HDSR2 0x000000F8L +#define BA0_HDSR3 0x000000FCL +#define BA0_DCA0 0x00000110L +#define BA0_DCC0 0x00000114L +#define BA0_DBA0 0x00000118L +#define BA0_DBC0 0x0000011CL +#define BA0_DCA1 0x00000120L +#define BA0_DCC1 0x00000124L +#define BA0_DBA1 0x00000128L +#define BA0_DBC1 0x0000012CL +#define BA0_DCA2 0x00000130L +#define BA0_DCC2 0x00000134L +#define BA0_DBA2 0x00000138L +#define BA0_DBC2 0x0000013CL +#define BA0_DCA3 0x00000140L +#define BA0_DCC3 0x00000144L +#define BA0_DBA3 0x00000148L +#define BA0_DBC3 0x0000014CL +#define BA0_DMR0 0x00000150L +#define BA0_DCR0 0x00000154L +#define BA0_DMR1 0x00000158L +#define BA0_DCR1 0x0000015CL +#define BA0_DMR2 0x00000160L +#define BA0_DCR2 0x00000164L +#define BA0_DMR3 0x00000168L +#define BA0_DCR3 0x0000016CL +#define BA0_DLMR 0x00000170L +#define BA0_DLSR 0x00000174L +#define BA0_FCR0 0x00000180L +#define BA0_FCR1 0x00000184L +#define BA0_FCR2 0x00000188L +#define BA0_FCR3 0x0000018CL +#define BA0_FPDR0 0x00000190L +#define BA0_FPDR1 0x00000194L +#define BA0_FPDR2 0x00000198L +#define BA0_FPDR3 0x0000019CL +#define BA0_FCHS 0x0000020CL +#define BA0_FSIC0 0x00000210L +#define BA0_FSIC1 0x00000214L +#define BA0_FSIC2 0x00000218L +#define BA0_FSIC3 0x0000021CL +#define BA0_PCICFG00 0x00000300L +#define BA0_PCICFG04 0x00000304L +#define BA0_PCICFG08 0x00000308L +#define BA0_PCICFG0C 0x0000030CL +#define BA0_PCICFG10 0x00000310L +#define BA0_PCICFG14 0x00000314L +#define BA0_PCICFG18 0x00000318L +#define BA0_PCICFG1C 0x0000031CL +#define BA0_PCICFG20 0x00000320L +#define BA0_PCICFG24 0x00000324L +#define BA0_PCICFG28 0x00000328L +#define BA0_PCICFG2C 0x0000032CL +#define BA0_PCICFG30 0x00000330L +#define BA0_PCICFG34 0x00000334L +#define BA0_PCICFG38 0x00000338L +#define BA0_PCICFG3C 0x0000033CL +#define BA0_PCICFG40 0x00000340L +#define BA0_PMCS 0x00000344L +#define BA0_CWPR 0x000003E0L +#define BA0_EPPMC 0x000003E4L +#define BA0_GPIOR 0x000003E8L +#define BA0_SPMC 0x000003ECL +#define BA0_CFLR 0x000003F0L +#define BA0_IISR 0x000003F4L +#define BA0_TMS 0x000003F8L +#define BA0_SSVID 0x000003FCL +#define BA0_CLKCR1 0x00000400L +#define BA0_FRR 0x00000410L +#define BA0_SLT12O 0x0000041CL +#define BA0_SERMC 0x00000420L +#define BA0_SERC1 0x00000428L +#define BA0_SERC2 0x0000042CL +#define BA0_SLT12M 0x0000045CL +#define BA0_ACCTL 0x00000460L +#define BA0_ACSTS 0x00000464L +#define BA0_ACOSV 0x00000468L +#define BA0_ACCAD 0x0000046CL +#define BA0_ACCDA 0x00000470L +#define BA0_ACISV 0x00000474L +#define BA0_ACSAD 0x00000478L +#define BA0_ACSDA 0x0000047CL +#define BA0_JSPT 0x00000480L +#define BA0_JSCTL 0x00000484L +#define BA0_MIDCR 0x00000490L +#define BA0_MIDCMD 0x00000494L +#define BA0_MIDSR 0x00000494L +#define BA0_MIDWP 0x00000498L +#define BA0_MIDRP 0x0000049CL +#define BA0_AODSD1 0x000004A8L +#define BA0_AODSD2 0x000004ACL +#define BA0_CFGI 0x000004B0L +#define BA0_SLT12M2 0x000004DCL +#define BA0_ACSTS2 0x000004E4L +#define BA0_ACISV2 0x000004F4L +#define BA0_ACSAD2 0x000004F8L +#define BA0_ACSDA2 0x000004FCL +#define BA0_IOTGP 0x00000500L +#define BA0_IOTSB 0x00000504L +#define BA0_IOTFM 0x00000508L +#define BA0_IOTDMA 0x0000050CL +#define BA0_IOTAC0 0x00000500L +#define BA0_IOTAC1 0x00000504L +#define BA0_IOTAC2 0x00000508L +#define BA0_IOTAC3 0x0000050CL +#define BA0_IOTPCP 0x0000052CL +#define BA0_IOTCC 0x00000530L +#define BA0_IOTCR 0x0000058CL +#define BA0_PCPRR 0x00000600L +#define BA0_PCPGR 0x00000604L +#define BA0_PCPCR 0x00000608L +#define BA0_PCPCIEN 0x00000608L +#define BA0_SBMAR 0x00000700L +#define BA0_SBMDR 0x00000704L +#define BA0_SBRR 0x00000708L +#define BA0_SBRDP 0x0000070CL +#define BA0_SBWDP 0x00000710L +#define BA0_SBWBS 0x00000710L +#define BA0_SBRBS 0x00000714L +#define BA0_FMSR 0x00000730L +#define BA0_B0AP 0x00000730L +#define BA0_FMDP 0x00000734L +#define BA0_B1AP 0x00000738L +#define BA0_B1DP 0x0000073CL +#define BA0_SSPM 0x00000740L +#define BA0_DACSR 0x00000744L +#define BA0_ADCSR 0x00000748L +#define BA0_SSCR 0x0000074CL +#define BA0_FMLVC 0x00000754L +#define BA0_FMRVC 0x00000758L +#define BA0_SRCSA 0x0000075CL +#define BA0_PPLVC 0x00000760L +#define BA0_PPRVC 0x00000764L +#define BA0_PASR 0x00000768L +#define BA0_CASR 0x0000076CL + +//**************************************************************************** +// +// The following define the offsets of the AC97 shadow registers, which appear +// as a virtual extension to the base address register zero memory range. +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007EL +#define AC97_CODEC_NUMBER_MASK 0x00003000L + +#define BA0_AC97_RESET 0x00001000L +#define BA0_AC97_MASTER_VOLUME 0x00001002L +#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L +#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L +#define BA0_AC97_MASTER_TONE 0x00001008L +#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL +#define BA0_AC97_PHONE_VOLUME 0x0000100CL +#define BA0_AC97_MIC_VOLUME 0x0000100EL +#define BA0_AC97_LINE_IN_VOLUME 0x00001010L +#define BA0_AC97_CD_VOLUME 0x00001012L +#define BA0_AC97_VIDEO_VOLUME 0x00001014L +#define BA0_AC97_AUX_VOLUME 0x00001016L +#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L +#define BA0_AC97_RECORD_SELECT 0x0000101AL +#define BA0_AC97_RECORD_GAIN 0x0000101CL +#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL +#define BA0_AC97_GENERAL_PURPOSE 0x00001020L +#define BA0_AC97_3D_CONTROL 0x00001022L +#define BA0_AC97_MODEM_RATE 0x00001024L +#define BA0_AC97_POWERDOWN 0x00001026L +#define BA0_AC97_EXT_AUDIO_ID 0x00001028L +#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL +#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL +#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL +#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L +#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L +#define BA0_AC97_MIC_ADC_RATE 0x00001034L +#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L +#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L +#define BA0_AC97_RESERVED_3A 0x0000103AL +#define BA0_AC97_EXT_MODEM_ID 0x0000103CL +#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL +#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L +#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L +#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L +#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L +#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L +#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL +#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL +#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL +#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L +#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L +#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L +#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L +#define BA0_AC97_RESERVED_58 0x00001058L +#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL +#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL +#define BA0_AC97_AC_MODE 0x0000105EL +#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L +#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L +#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L +#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L +#define BA0_AC97_SPDIF_CONTROL 0x00001068L +#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL +#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL +#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL +#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L +#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L +#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L +#define BA0_AC97_CAL_ADDRESS 0x00001076L +#define BA0_AC97_CAL_DATA 0x00001078L +#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL +#define BA0_AC97_VENDOR_ID1 0x0000107CL +#define BA0_AC97_VENDOR_ID2 0x0000107EL + +//**************************************************************************** +// +// The following define the offsets of the registers and memories accessed via +// base address register one on the CS4281 part. +// +//**************************************************************************** + +//**************************************************************************** +// +// The following defines are for the flags in the PCI device ID/vendor ID +// register. +// +//**************************************************************************** +#define PDV_VENID_MASK 0x0000FFFFL +#define PDV_DEVID_MASK 0xFFFF0000L +#define PDV_VENID_SHIFT 0L +#define PDV_DEVID_SHIFT 16L +#define VENID_CIRRUS_LOGIC 0x1013L +#define DEVID_CS4281 0x6005L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI status and command +// register. +// +//**************************************************************************** +#define PSC_IO_SPACE_ENABLE 0x00000001L +#define PSC_MEMORY_SPACE_ENABLE 0x00000002L +#define PSC_BUS_MASTER_ENABLE 0x00000004L +#define PSC_SPECIAL_CYCLES 0x00000008L +#define PSC_MWI_ENABLE 0x00000010L +#define PSC_VGA_PALETTE_SNOOP 0x00000020L +#define PSC_PARITY_RESPONSE 0x00000040L +#define PSC_WAIT_CONTROL 0x00000080L +#define PSC_SERR_ENABLE 0x00000100L +#define PSC_FAST_B2B_ENABLE 0x00000200L +#define PSC_UDF_MASK 0x007F0000L +#define PSC_FAST_B2B_CAPABLE 0x00800000L +#define PSC_PARITY_ERROR_DETECTED 0x01000000L +#define PSC_DEVSEL_TIMING_MASK 0x06000000L +#define PSC_TARGET_ABORT_SIGNALLED 0x08000000L +#define PSC_RECEIVED_TARGET_ABORT 0x10000000L +#define PSC_RECEIVED_MASTER_ABORT 0x20000000L +#define PSC_SIGNALLED_SERR 0x40000000L +#define PSC_DETECTED_PARITY_ERROR 0x80000000L +#define PSC_UDF_SHIFT 16L +#define PSC_DEVSEL_TIMING_SHIFT 25L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI class/revision ID +// register. +// +//**************************************************************************** +#define PCR_REVID_MASK 0x000000FFL +#define PCR_INTERFACE_MASK 0x0000FF00L +#define PCR_SUBCLASS_MASK 0x00FF0000L +#define PCR_CLASS_MASK 0xFF000000L +#define PCR_REVID_SHIFT 0L +#define PCR_INTERFACE_SHIFT 8L +#define PCR_SUBCLASS_SHIFT 16L +#define PCR_CLASS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI latency timer register. +// +//**************************************************************************** +#define PLT_CACHE_LINE_SIZE_MASK 0x000000FFL +#define PLT_LATENCY_TIMER_MASK 0x0000FF00L +#define PLT_HEADER_TYPE_MASK 0x00FF0000L +#define PLT_BIST_MASK 0xFF000000L +#define PLT_CACHE_LINE_SIZE_SHIFT 0L +#define PLT_LATENCY_TIMER_SHIFT 8L +#define PLT_HEADER_TYPE_SHIFT 16L +#define PLT_BIST_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI base address registers. +// +//**************************************************************************** +#define PBAR_MEMORY_SPACE_INDICATOR 0x00000001L +#define PBAR_LOCATION_TYPE_MASK 0x00000006L +#define PBAR_NOT_PREFETCHABLE 0x00000008L +#define PBAR_ADDRESS_MASK 0xFFFFFFF0L +#define PBAR_LOCATION_TYPE_SHIFT 1L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI subsystem ID/subsystem +// vendor ID register. +// +//**************************************************************************** +#define PSS_SUBSYSTEM_VENDOR_ID_MASK 0x0000FFFFL +#define PSS_SUBSYSTEM_ID_MASK 0xFFFF0000L +#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT 0L +#define PSS_SUBSYSTEM_ID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI interrupt register. +// +//**************************************************************************** +#define PI_LINE_MASK 0x000000FFL +#define PI_PIN_MASK 0x0000FF00L +#define PI_MIN_GRANT_MASK 0x00FF0000L +#define PI_MAX_LATENCY_MASK 0xFF000000L +#define PI_LINE_SHIFT 0L +#define PI_PIN_SHIFT 8L +#define PI_MIN_GRANT_SHIFT 16L +#define PI_MAX_LATENCY_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt status +// register. +// +//**************************************************************************** +#define HISR_HVOLMASK 0x00000003L +#define HISR_VDNI 0x00000001L +#define HISR_VUPI 0x00000002L +#define HISR_GP1I 0x00000004L +#define HISR_GP3I 0x00000008L +#define HISR_GPSI 0x00000010L +#define HISR_GPPI 0x00000020L +#define HISR_DMAI 0x00040000L +#define HISR_FIFOI 0x00100000L +#define HISR_HVOL 0x00200000L +#define HISR_MIDI 0x00400000L +#define HISR_SBINT 0x00800000L +#define HISR_INTENA 0x80000000L +#define HISR_DMA_MASK 0x00000F00L +#define HISR_FIFO_MASK 0x0000F000L +#define HISR_DMA_SHIFT 8L +#define HISR_FIFO_SHIFT 12L +#define HISR_FIFO0 0x00001000L +#define HISR_FIFO1 0x00002000L +#define HISR_FIFO2 0x00004000L +#define HISR_FIFO3 0x00008000L +#define HISR_DMA0 0x00000100L +#define HISR_DMA1 0x00000200L +#define HISR_DMA2 0x00000400L +#define HISR_DMA3 0x00000800L +#define HISR_RESERVED 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt control +// register. +// +//**************************************************************************** +#define HICR_IEV 0x00000001L +#define HICR_CHGM 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Mode Register n +// (DMRn) +// +//**************************************************************************** +#define DMRn_TR_MASK 0x0000000CL +#define DMRn_TR_SHIFT 2L +#define DMRn_AUTO 0x00000010L +#define DMRn_TR_READ 0x00000008L +#define DMRn_TR_WRITE 0x00000004L +#define DMRn_TYPE_MASK 0x000000C0L +#define DMRn_TYPE_SHIFT 6L +#define DMRn_SIZE8 0x00010000L +#define DMRn_MONO 0x00020000L +#define DMRn_BEND 0x00040000L +#define DMRn_USIGN 0x00080000L +#define DMRn_SIZE20 0x00100000L +#define DMRn_SWAPC 0x00400000L +#define DMRn_CBC 0x01000000L +#define DMRn_TBC 0x02000000L +#define DMRn_POLL 0x10000000L +#define DMRn_DMA 0x20000000L +#define DMRn_FSEL_MASK 0xC0000000L +#define DMRn_FSEL_SHIFT 30L +#define DMRn_FSEL0 0x00000000L +#define DMRn_FSEL1 0x40000000L +#define DMRn_FSEL2 0x80000000L +#define DMRn_FSEL3 0xC0000000L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Command Register n +// (DCRn) +// +//**************************************************************************** +#define DCRn_HTCIE 0x00020000L +#define DCRn_TCIE 0x00010000L +#define DCRn_MSK 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the FIFO Control +// register n.(FCRn) +// +//**************************************************************************** +#define FCRn_OF_MASK 0x0000007FL +#define FCRn_OF_SHIFT 0L +#define FCRn_SZ_MASK 0x00007F00L +#define FCRn_SZ_SHIFT 8L +#define FCRn_LS_MASK 0x001F0000L +#define FCRn_LS_SHIFT 16L +#define FCRn_RS_MASK 0x1F000000L +#define FCRn_RS_SHIFT 24L +#define FCRn_FEN 0x80000000L +#define FCRn_PSH 0x20000000L +#define FCRn_DACZ 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port Power Management +// control register.(SPMC) +// +//**************************************************************************** +#define SPMC_RSTN 0x00000001L +#define SPMC_ASYN 0x00000002L +#define SPMC_WUP1 0x00000004L +#define SPMC_WUP2 0x00000008L +#define SPMC_ASDI2E 0x00000100L +#define SPMC_ESSPD 0x00000200L +#define SPMC_GISPEN 0x00004000L +#define SPMC_GIPPEN 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the Configuration Load register. +// (CFLR) +// +//**************************************************************************** +#define CFLR_CLOCK_SOURCE_MASK 0x00000003L +#define CFLR_CLOCK_SOURCE_AC97 0x00000001L + +#define CFLR_CB0_MASK 0x000000FFL +#define CFLR_CB1_MASK 0x0000FF00L +#define CFLR_CB2_MASK 0x00FF0000L +#define CFLR_CB3_MASK 0xFF000000L +#define CFLR_CB0_SHIFT 0L +#define CFLR_CB1_SHIFT 8L +#define CFLR_CB2_SHIFT 16L +#define CFLR_CB3_SHIFT 24L + +#define IOTCR_DMA0 0x00000000L +#define IOTCR_DMA1 0x00000400L +#define IOTCR_DMA2 0x00000800L +#define IOTCR_DMA3 0x00000C00L +#define IOTCR_CCLS 0x00000100L +#define IOTCR_PCPCI 0x00000200L +#define IOTCR_DDMA 0x00000300L + +#define SBWBS_WBB 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the SRC Slot Assignment Register +// (SRCSA) +// +//**************************************************************************** +#define SRCSA_PLSS_MASK 0x0000001FL +#define SRCSA_PLSS_SHIFT 0L +#define SRCSA_PRSS_MASK 0x00001F00L +#define SRCSA_PRSS_SHIFT 8L +#define SRCSA_CLSS_MASK 0x001F0000L +#define SRCSA_CLSS_SHIFT 16L +#define SRCSA_CRSS_MASK 0x1F000000L +#define SRCSA_CRSS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Power Management +// register.(SSPM) +// +//**************************************************************************** +#define SSPM_FPDN 0x00000080L +#define SSPM_MIXEN 0x00000040L +#define SSPM_CSRCEN 0x00000020L +#define SSPM_PSRCEN 0x00000010L +#define SSPM_JSEN 0x00000008L +#define SSPM_ACLEN 0x00000004L +#define SSPM_FMEN 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Control +// Register. (SSCR) +// +//**************************************************************************** +#define SSCR_SB 0x00000004L +#define SSCR_HVC 0x00000008L +#define SSCR_LPFIFO 0x00000040L +#define SSCR_LPSRC 0x00000080L +#define SSCR_XLPSRC 0x00000100L +#define SSCR_MVMD 0x00010000L +#define SSCR_MVAD 0x00020000L +#define SSCR_MVLD 0x00040000L +#define SSCR_MVCS 0x00080000L + +//**************************************************************************** +// +// The following defines are for the flags in the Clock Control Register 1. +// (CLKCR1) +// +//**************************************************************************** +#define CLKCR1_DLLSS_MASK 0x0000000CL +#define CLKCR1_DLLSS_SHIFT 2L +#define CLKCR1_DLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_DLLOS 0x00000040L +#define CLKCR1_CKRA 0x00010000L +#define CLKCR1_CKRN 0x00020000L +#define CLKCR1_DLLRDY 0x01000000L +#define CLKCR1_CLKON 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound Blaster Read Buffer +// Status.(SBRBS) +// +//**************************************************************************** +#define SBRBS_RD_MASK 0x0000007FL +#define SBRBS_RD_SHIFT 0L +#define SBRBS_RBF 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port master control +// register.(SERMC) +// +//**************************************************************************** +#define SERMC_MSPE 0x00000001L +#define SERMC_PTC_MASK 0x0000000EL +#define SERMC_PTC_SHIFT 1L +#define SERMC_PTC_AC97 0x00000002L +#define SERMC_PLB 0x00000010L +#define SERMC_PXLB 0x00000020L +#define SERMC_LOFV 0x00080000L +#define SERMC_SLB 0x00100000L +#define SERMC_SXLB 0x00200000L +#define SERMC_ODSEN1 0x01000000L +#define SERMC_ODSEN2 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the General Purpose I/O Register. +// (GPIOR) +// +//**************************************************************************** +#define GPIOR_VDNS 0x00000001L +#define GPIOR_VUPS 0x00000002L +#define GPIOR_GP1S 0x00000004L +#define GPIOR_GP3S 0x00000008L +#define GPIOR_GPSS 0x00000010L +#define GPIOR_GPPS 0x00000020L +#define GPIOR_GP1D 0x00000400L +#define GPIOR_GP3D 0x00000800L +#define GPIOR_VDNLT 0x00010000L +#define GPIOR_VDNPO 0x00020000L +#define GPIOR_VDNST 0x00040000L +#define GPIOR_VDNW 0x00080000L +#define GPIOR_VUPLT 0x00100000L +#define GPIOR_VUPPO 0x00200000L +#define GPIOR_VUPST 0x00400000L +#define GPIOR_VUPW 0x00800000L +#define GPIOR_GP1OE 0x01000000L +#define GPIOR_GP1PT 0x02000000L +#define GPIOR_GP1ST 0x04000000L +#define GPIOR_GP1W 0x08000000L +#define GPIOR_GP3OE 0x10000000L +#define GPIOR_GP3PT 0x20000000L +#define GPIOR_GP3ST 0x40000000L +#define GPIOR_GP3W 0x80000000L + +//**************************************************************************** +// +// The following defines are for the flags in the clock control register 1. +// +//**************************************************************************** +#define CLKCR1_PLLSS_MASK 0x0000000CL +#define CLKCR1_PLLSS_SERIAL 0x00000000L +#define CLKCR1_PLLSS_CRYSTAL 0x00000004L +#define CLKCR1_PLLSS_PCI 0x00000008L +#define CLKCR1_PLLSS_RESERVED 0x0000000CL +#define CLKCR1_PLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_PLLOS 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the feature reporting register. +// +//**************************************************************************** +#define FRR_FAB_MASK 0x00000003L +#define FRR_MASK_MASK 0x0000001CL +#define FRR_ID_MASK 0x00003000L +#define FRR_FAB_SHIFT 0L +#define FRR_MASK_SHIFT 2L +#define FRR_ID_SHIFT 12L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 1 configuration +// register. +// +//**************************************************************************** +#define SERC1_VALUE 0x00000003L +#define SERC1_SO1EN 0x00000001L +#define SERC1_SO1F_MASK 0x0000000EL +#define SERC1_SO1F_CS423X 0x00000000L +#define SERC1_SO1F_AC97 0x00000002L +#define SERC1_SO1F_DAC 0x00000004L +#define SERC1_SO1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 2 configuration +// register. +// +//**************************************************************************** +#define SERC2_VALUE 0x00000003L +#define SERC2_SI1EN 0x00000001L +#define SERC2_SI1F_MASK 0x0000000EL +#define SERC2_SI1F_CS423X 0x00000000L +#define SERC2_SI1F_AC97 0x00000002L +#define SERC2_SI1F_ADC 0x00000004L +#define SERC2_SI1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 control register. +// +//**************************************************************************** +#define ACCTL_ESYN 0x00000002L +#define ACCTL_VFRM 0x00000004L +#define ACCTL_DCV 0x00000008L +#define ACCTL_CRW 0x00000010L +#define ACCTL_TC 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register. +// +//**************************************************************************** +#define ACSTS_CRDY 0x00000001L +#define ACSTS_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 output slot valid +// register. +// +//**************************************************************************** +#define ACOSV_SLV3 0x00000001L +#define ACOSV_SLV4 0x00000002L +#define ACOSV_SLV5 0x00000004L +#define ACOSV_SLV6 0x00000008L +#define ACOSV_SLV7 0x00000010L +#define ACOSV_SLV8 0x00000020L +#define ACOSV_SLV9 0x00000040L +#define ACOSV_SLV10 0x00000080L +#define ACOSV_SLV11 0x00000100L +#define ACOSV_SLV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command address +// register. +// +//**************************************************************************** +#define ACCAD_CI_MASK 0x0000007FL +#define ACCAD_CI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command data register. +// +//**************************************************************************** +#define ACCDA_CD_MASK 0x0000FFFFL +#define ACCDA_CD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register. +// +//**************************************************************************** +#define ACISV_ISV3 0x00000001L +#define ACISV_ISV4 0x00000002L +#define ACISV_ISV5 0x00000004L +#define ACISV_ISV6 0x00000008L +#define ACISV_ISV7 0x00000010L +#define ACISV_ISV8 0x00000020L +#define ACISV_ISV9 0x00000040L +#define ACISV_ISV10 0x00000080L +#define ACISV_ISV11 0x00000100L +#define ACISV_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register. +// +//**************************************************************************** +#define ACSAD_SI_MASK 0x0000007FL +#define ACSAD_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register. +// +//**************************************************************************** +#define ACSDA_SD_MASK 0x0000FFFFL +#define ACSDA_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers (all 12). +// +//**************************************************************************** +#define IOTAC_SA_MASK 0x0000FFFFL +#define IOTAC_MSK_MASK 0x000F0000L +#define IOTAC_IODC_MASK 0x06000000L +#define IOTAC_IODC_16_BIT 0x00000000L +#define IOTAC_IODC_10_BIT 0x02000000L +#define IOTAC_IODC_12_BIT 0x04000000L +#define IOTAC_WSPI 0x08000000L +#define IOTAC_RSPI 0x10000000L +#define IOTAC_WSE 0x20000000L +#define IOTAC_WE 0x40000000L +#define IOTAC_RE 0x80000000L +#define IOTAC_SA_SHIFT 0L +#define IOTAC_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI master enable +// register. +// +//**************************************************************************** +#define PCPCIEN_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick poll/trigger +// register. +// +//**************************************************************************** +#define JSPT_CAX 0x00000001L +#define JSPT_CAY 0x00000002L +#define JSPT_CBX 0x00000004L +#define JSPT_CBY 0x00000008L +#define JSPT_BA1 0x00000010L +#define JSPT_BA2 0x00000020L +#define JSPT_BB1 0x00000040L +#define JSPT_BB2 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick control register. +// The TBF bit has been moved from MIDSR register to JSCTL register bit 8. +// +//**************************************************************************** +#define JSCTL_SP_MASK 0x00000003L +#define JSCTL_SP_SLOW 0x00000000L +#define JSCTL_SP_MEDIUM_SLOW 0x00000001L +#define JSCTL_SP_MEDIUM_FAST 0x00000002L +#define JSCTL_SP_FAST 0x00000003L +#define JSCTL_ARE 0x00000004L +#define JSCTL_TBF 0x00000100L + + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI control register. +// +//**************************************************************************** +#define MIDCR_TXE 0x00000001L +#define MIDCR_RXE 0x00000002L +#define MIDCR_RIE 0x00000004L +#define MIDCR_TIE 0x00000008L +#define MIDCR_MLB 0x00000010L +#define MIDCR_MRST 0x00000020L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI status register. +// +//**************************************************************************** +#define MIDSR_RBE 0x00000080L +#define MIDSR_RDA 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI write port register. +// +//**************************************************************************** +#define MIDWP_MWD_MASK 0x000000FFL +#define MIDWP_MWD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI read port register. +// +//**************************************************************************** +#define MIDRP_MRD_MASK 0x000000FFL +#define MIDRP_MRD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the configuration interface +// register. +// +//**************************************************************************** +#define CFGI_CLK 0x00000001L +#define CFGI_DOUT 0x00000002L +#define CFGI_DIN_EEN 0x00000004L +#define CFGI_EELD 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the subsystem ID and vendor ID +// register. +// +//**************************************************************************** +#define SSVID_VID_MASK 0x0000FFFFL +#define SSVID_SID_MASK 0xFFFF0000L +#define SSVID_VID_SHIFT 0L +#define SSVID_SID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the GPIO pin interface register. +// +//**************************************************************************** +#define GPIOR_VOLDN 0x00000001L +#define GPIOR_VOLUP 0x00000002L +#define GPIOR_SI2D 0x00000004L +#define GPIOR_SI2OE 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register 2. +// +//**************************************************************************** +#define ACSTS2_CRDY 0x00000001L +#define ACSTS2_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register 2. +// +//**************************************************************************** +#define ACISV2_ISV3 0x00000001L +#define ACISV2_ISV4 0x00000002L +#define ACISV2_ISV5 0x00000004L +#define ACISV2_ISV6 0x00000008L +#define ACISV2_ISV7 0x00000010L +#define ACISV2_ISV8 0x00000020L +#define ACISV2_ISV9 0x00000040L +#define ACISV2_ISV10 0x00000080L +#define ACISV2_ISV11 0x00000100L +#define ACISV2_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register 2. +// +//**************************************************************************** +#define ACSAD2_SI_MASK 0x0000007FL +#define ACSAD2_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register 2. +// +//**************************************************************************** +#define ACSDA2_SD_MASK 0x0000FFFFL +#define ACSDA2_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap control register. +// +//**************************************************************************** +#define IOTCR_ITD 0x00000001L +#define IOTCR_HRV 0x00000002L +#define IOTCR_SRV 0x00000004L +#define IOTCR_DTI 0x00000008L +#define IOTCR_DFI 0x00000010L +#define IOTCR_DDP 0x00000020L +#define IOTCR_JTE 0x00000040L +#define IOTCR_PPE 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Hardware Master Volume. +// +//**************************************************************************** +#define IOTGP_SA_MASK 0x0000FFFFL +#define IOTGP_MSK_MASK 0x000F0000L +#define IOTGP_IODC_MASK 0x06000000L +#define IOTGP_IODC_16_BIT 0x00000000L +#define IOTGP_IODC_10_BIT 0x02000000L +#define IOTGP_IODC_12_BIT 0x04000000L +#define IOTGP_WSPI 0x08000000L +#define IOTGP_RSPI 0x10000000L +#define IOTGP_WSE 0x20000000L +#define IOTGP_WE 0x40000000L +#define IOTGP_RE 0x80000000L +#define IOTGP_SA_SHIFT 0L +#define IOTGP_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Sound Blaster +// +//**************************************************************************** +#define IOTSB_SA_MASK 0x0000FFFFL +#define IOTSB_MSK_MASK 0x000F0000L +#define IOTSB_IODC_MASK 0x06000000L +#define IOTSB_IODC_16_BIT 0x00000000L +#define IOTSB_IODC_10_BIT 0x02000000L +#define IOTSB_IODC_12_BIT 0x04000000L +#define IOTSB_WSPI 0x08000000L +#define IOTSB_RSPI 0x10000000L +#define IOTSB_WSE 0x20000000L +#define IOTSB_WE 0x40000000L +#define IOTSB_RE 0x80000000L +#define IOTSB_SA_SHIFT 0L +#define IOTSB_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for FM. +// +//**************************************************************************** +#define IOTFM_SA_MASK 0x0000FFFFL +#define IOTFM_MSK_MASK 0x000F0000L +#define IOTFM_IODC_MASK 0x06000000L +#define IOTFM_IODC_16_BIT 0x00000000L +#define IOTFM_IODC_10_BIT 0x02000000L +#define IOTFM_IODC_12_BIT 0x04000000L +#define IOTFM_WSPI 0x08000000L +#define IOTFM_RSPI 0x10000000L +#define IOTFM_WSE 0x20000000L +#define IOTFM_WE 0x40000000L +#define IOTFM_RE 0x80000000L +#define IOTFM_SA_SHIFT 0L +#define IOTFM_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI request register. +// +//**************************************************************************** +#define PCPRR_RDC_MASK 0x00000007L +#define PCPRR_REQ 0x00008000L +#define PCPRR_RDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI grant register. +// +//**************************************************************************** +#define PCPGR_GDC_MASK 0x00000007L +#define PCPGR_VL 0x00008000L +#define PCPGR_GDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI Control Register. +// +//**************************************************************************** +#define PCPCR_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the debug index register. +// +//**************************************************************************** +#define DREG_REGID_MASK 0x0000007FL +#define DREG_DEBUG 0x00000080L +#define DREG_RGBK_MASK 0x00000700L +#define DREG_TRAP 0x00000800L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000L +#endif +#endif +#define DREG_REGID_SHIFT 0L +#define DREG_RGBK_SHIFT 8L +#define DREG_RGBK_REGID_MASK 0x0000077FL +#define DREG_REGID_R0 0x00000010L +#define DREG_REGID_R1 0x00000011L +#define DREG_REGID_R2 0x00000012L +#define DREG_REGID_R3 0x00000013L +#define DREG_REGID_R4 0x00000014L +#define DREG_REGID_R5 0x00000015L +#define DREG_REGID_R6 0x00000016L +#define DREG_REGID_R7 0x00000017L +#define DREG_REGID_R8 0x00000018L +#define DREG_REGID_R9 0x00000019L +#define DREG_REGID_RA 0x0000001AL +#define DREG_REGID_RB 0x0000001BL +#define DREG_REGID_RC 0x0000001CL +#define DREG_REGID_RD 0x0000001DL +#define DREG_REGID_RE 0x0000001EL +#define DREG_REGID_RF 0x0000001FL +#define DREG_REGID_RA_BUS_LOW 0x00000020L +#define DREG_REGID_RA_BUS_HIGH 0x00000038L +#define DREG_REGID_YBUS_LOW 0x00000050L +#define DREG_REGID_YBUS_HIGH 0x00000058L +#define DREG_REGID_TRAP_0 0x00000100L +#define DREG_REGID_TRAP_1 0x00000101L +#define DREG_REGID_TRAP_2 0x00000102L +#define DREG_REGID_TRAP_3 0x00000103L +#define DREG_REGID_TRAP_4 0x00000104L +#define DREG_REGID_TRAP_5 0x00000105L +#define DREG_REGID_TRAP_6 0x00000106L +#define DREG_REGID_TRAP_7 0x00000107L +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010EL +#define DREG_REGID_TOP_OF_STACK 0x0000010FL +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110L +#define DREG_REGID_TRAP_9 0x00000111L +#define DREG_REGID_TRAP_10 0x00000112L +#define DREG_REGID_TRAP_11 0x00000113L +#define DREG_REGID_TRAP_12 0x00000114L +#define DREG_REGID_TRAP_13 0x00000115L +#define DREG_REGID_TRAP_14 0x00000116L +#define DREG_REGID_TRAP_15 0x00000117L +#define DREG_REGID_TRAP_16 0x00000118L +#define DREG_REGID_TRAP_17 0x00000119L +#define DREG_REGID_TRAP_18 0x0000011AL +#define DREG_REGID_TRAP_19 0x0000011BL +#define DREG_REGID_TRAP_20 0x0000011CL +#define DREG_REGID_TRAP_21 0x0000011DL +#define DREG_REGID_TRAP_22 0x0000011EL +#define DREG_REGID_TRAP_23 0x0000011FL +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200L +#define DREG_REGID_RSA0_HIGH 0x00000201L +#define DREG_REGID_RSA1_LOW 0x00000202L +#define DREG_REGID_RSA1_HIGH 0x00000203L +#define DREG_REGID_RSA2 0x00000204L +#define DREG_REGID_RSA3 0x00000205L +#define DREG_REGID_RSI0_LOW 0x00000206L +#define DREG_REGID_RSI0_HIGH 0x00000207L +#define DREG_REGID_RSI1 0x00000208L +#define DREG_REGID_RSI2 0x00000209L +#define DREG_REGID_SAGUSTATUS 0x0000020AL +#define DREG_REGID_RSCONFIG01_LOW 0x0000020BL +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020CL +#define DREG_REGID_RSCONFIG23_LOW 0x0000020DL +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020EL +#define DREG_REGID_RSDMA01E 0x0000020FL +#define DREG_REGID_RSDMA23E 0x00000210L +#define DREG_REGID_RSD0_LOW 0x00000211L +#define DREG_REGID_RSD0_HIGH 0x00000212L +#define DREG_REGID_RSD1_LOW 0x00000213L +#define DREG_REGID_RSD1_HIGH 0x00000214L +#define DREG_REGID_RSD2_LOW 0x00000215L +#define DREG_REGID_RSD2_HIGH 0x00000216L +#define DREG_REGID_RSD3_LOW 0x00000217L +#define DREG_REGID_RSD3_HIGH 0x00000218L +#define DREG_REGID_SRAR_HIGH 0x0000021AL +#define DREG_REGID_SRAR_LOW 0x0000021BL +#define DREG_REGID_DMA_STATE 0x0000021CL +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021DL +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021EL +#define DREG_REGID_CPU_STATUS 0x00000300L +#define DREG_REGID_MAC_MODE 0x00000301L +#define DREG_REGID_STACK_AND_REPEAT 0x00000302L +#define DREG_REGID_INDEX0 0x00000304L +#define DREG_REGID_INDEX1 0x00000305L +#define DREG_REGID_DMA_STATE_0_3 0x00000400L +#define DREG_REGID_DMA_STATE_4_7 0x00000404L +#define DREG_REGID_DMA_STATE_8_11 0x00000408L +#define DREG_REGID_DMA_STATE_12_15 0x0000040CL +#define DREG_REGID_DMA_STATE_16_19 0x00000410L +#define DREG_REGID_DMA_STATE_20_23 0x00000414L +#define DREG_REGID_DMA_STATE_24_27 0x00000418L +#define DREG_REGID_DMA_STATE_28_31 0x0000041CL +#define DREG_REGID_DMA_STATE_32_35 0x00000420L +#define DREG_REGID_DMA_STATE_36_39 0x00000424L +#define DREG_REGID_DMA_STATE_40_43 0x00000428L +#define DREG_REGID_DMA_STATE_44_47 0x0000042CL +#define DREG_REGID_DMA_STATE_48_51 0x00000430L +#define DREG_REGID_DMA_STATE_52_55 0x00000434L +#define DREG_REGID_DMA_STATE_56_59 0x00000438L +#define DREG_REGID_DMA_STATE_60_63 0x0000043CL +#define DREG_REGID_DMA_STATE_64_67 0x00000440L +#define DREG_REGID_DMA_STATE_68_71 0x00000444L +#define DREG_REGID_DMA_STATE_72_75 0x00000448L +#define DREG_REGID_DMA_STATE_76_79 0x0000044CL +#define DREG_REGID_DMA_STATE_80_83 0x00000450L +#define DREG_REGID_DMA_STATE_84_87 0x00000454L +#define DREG_REGID_DMA_STATE_88_91 0x00000458L +#define DREG_REGID_DMA_STATE_92_95 0x0000045CL +#define DREG_REGID_TRAP_SELECT 0x00000500L +#define DREG_REGID_TRAP_WRITE_0 0x00000500L +#define DREG_REGID_TRAP_WRITE_1 0x00000501L +#define DREG_REGID_TRAP_WRITE_2 0x00000502L +#define DREG_REGID_TRAP_WRITE_3 0x00000503L +#define DREG_REGID_TRAP_WRITE_4 0x00000504L +#define DREG_REGID_TRAP_WRITE_5 0x00000505L +#define DREG_REGID_TRAP_WRITE_6 0x00000506L +#define DREG_REGID_TRAP_WRITE_7 0x00000507L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510L +#define DREG_REGID_TRAP_WRITE_9 0x00000511L +#define DREG_REGID_TRAP_WRITE_10 0x00000512L +#define DREG_REGID_TRAP_WRITE_11 0x00000513L +#define DREG_REGID_TRAP_WRITE_12 0x00000514L +#define DREG_REGID_TRAP_WRITE_13 0x00000515L +#define DREG_REGID_TRAP_WRITE_14 0x00000516L +#define DREG_REGID_TRAP_WRITE_15 0x00000517L +#define DREG_REGID_TRAP_WRITE_16 0x00000518L +#define DREG_REGID_TRAP_WRITE_17 0x00000519L +#define DREG_REGID_TRAP_WRITE_18 0x0000051AL +#define DREG_REGID_TRAP_WRITE_19 0x0000051BL +#define DREG_REGID_TRAP_WRITE_20 0x0000051CL +#define DREG_REGID_TRAP_WRITE_21 0x0000051DL +#define DREG_REGID_TRAP_WRITE_22 0x0000051EL +#define DREG_REGID_TRAP_WRITE_23 0x0000051FL +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600L +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601L +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602L +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603L +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604L +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605L +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606L +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607L +#define DREG_REGID_MAC0_ACC0_MID 0x00000608L +#define DREG_REGID_MAC0_ACC1_MID 0x00000609L +#define DREG_REGID_MAC0_ACC2_MID 0x0000060AL +#define DREG_REGID_MAC0_ACC3_MID 0x0000060BL +#define DREG_REGID_MAC1_ACC0_MID 0x0000060CL +#define DREG_REGID_MAC1_ACC1_MID 0x0000060DL +#define DREG_REGID_MAC1_ACC2_MID 0x0000060EL +#define DREG_REGID_MAC1_ACC3_MID 0x0000060FL +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610L +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611L +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612L +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613L +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614L +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615L +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616L +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617L +#define DREG_REGID_RSHOUT_LOW 0x00000620L +#define DREG_REGID_RSHOUT_MID 0x00000628L +#define DREG_REGID_RSHOUT_HIGH 0x00000630L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 S/PDIF Control register. +// +//**************************************************************************** +#define SPDIF_CONTROL_SPDIF_EN 0x00008000L +#define SPDIF_CONTROL_VAL 0x00004000L +#define SPDIF_CONTROL_COPY 0x00000004L +#define SPDIF_CONTROL_CC0 0x00000010L +#define SPDIF_CONTROL_CC1 0x00000020L +#define SPDIF_CONTROL_CC2 0x00000040L +#define SPDIF_CONTROL_CC3 0x00000080L +#define SPDIF_CONTROL_CC4 0x00000100L +#define SPDIF_CONTROL_CC5 0x00000200L +#define SPDIF_CONTROL_CC6 0x00000400L +#define SPDIF_CONTROL_L 0x00000800L + +#endif // _H_HWDEFS diff --git a/trunk/sound/oss/cs4281/cs4281_wrapper-24.c b/trunk/sound/oss/cs4281/cs4281_wrapper-24.c new file mode 100644 index 000000000000..4559f02c9969 --- /dev/null +++ b/trunk/sound/oss/cs4281/cs4281_wrapper-24.c @@ -0,0 +1,41 @@ +/******************************************************************************* +* +* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/20/00 trw - new file. +* +*******************************************************************************/ + +#include + +static int cs4281_resume_null(struct pci_dev *pcidev) { return 0; } +static int cs4281_suspend_null(struct pci_dev *pcidev, pm_message_t state) { return 0; } + +#define free_dmabuf(state, dmabuf) \ + pci_free_consistent(state->pcidev, \ + PAGE_SIZE << (dmabuf)->buforder, \ + (dmabuf)->rawbuf, (dmabuf)->dmaaddr); +#define free_dmabuf2(state, dmabuf) \ + pci_free_consistent((state)->pcidev, \ + PAGE_SIZE << (state)->buforder_tmpbuff, \ + (state)->tmpbuff, (state)->dmaaddr_tmpbuff); +#define cs4x_pgoff(vma) ((vma)->vm_pgoff) + diff --git a/trunk/sound/oss/cs4281/cs4281m.c b/trunk/sound/oss/cs4281/cs4281m.c new file mode 100644 index 000000000000..0400a416dc93 --- /dev/null +++ b/trunk/sound/oss/cs4281/cs4281m.c @@ -0,0 +1,4487 @@ +/******************************************************************************* +* +* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- adapted from drivers by Thomas Sailer, +* -- but don't bug him; Problems should go to: +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Module command line parameters: +* none +* +* Supported devices: +* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible +* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible +* /dev/midi simple MIDI UART interface, no ioctl +* +* Modification History +* 08/20/00 trw - silence and no stopping DAC until release +* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. +* 09/18/00 trw - added 16bit only record with conversion +* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous +* capture/playback rates) +* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin +* libOSSm.so) +* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) +* 11/03/00 trw - fixed interrupt loss/stutter, added debug. +* 11/10/00 bkz - added __devinit to cs4281_hw_init() +* 11/10/00 trw - fixed SMP and capture spinlock hang. +* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. +* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. +* 12/08/00 trw - added PM support. +* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 +* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. +* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. +* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +* +*******************************************************************************/ + +/* uncomment the following line to disable building PM support into the driver */ +//#define NOT_CS4281_PM 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//#include "cs_dm.h" +#include "cs4281_hwdefs.h" +#include "cs4281pm.h" + +struct cs4281_state; + +static void stop_dac(struct cs4281_state *s); +static void stop_adc(struct cs4281_state *s); +static void start_dac(struct cs4281_state *s); +static void start_adc(struct cs4281_state *s); +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +// --------------------------------------------------------------------- + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281 +#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 +#endif + +#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS) +#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ + +// buffer order determines the size of the dma buffer for the driver. +// under Linux, a smaller buffer allows more responsiveness from many of the +// applications (e.g. games). A larger buffer allows some of the apps (esound) +// to not underrun the dma buffer as easily. As default, use 32k (order=3) +// rather than 64k as some of the games work more responsively. +// log base 2( buff sz = 32k). +static unsigned long defaultorder = 3; +module_param(defaultorder, ulong, 0); + +// +// Turn on/off debugging compilation by commenting out "#define CSDEBUG" +// +#define CSDEBUG 1 +#if CSDEBUG +#define CSDEBUG_INTERFACE 1 +#else +#undef CSDEBUG_INTERFACE +#endif +// +// cs_debugmask areas +// +#define CS_INIT 0x00000001 // initialization and probe functions +#define CS_ERROR 0x00000002 // tmp debugging bit placeholder +#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) +#define CS_FUNCTION 0x00000008 // enter/leave functions +#define CS_WAVE_WRITE 0x00000010 // write information for wave +#define CS_WAVE_READ 0x00000020 // read information for wave +#define CS_MIDI_WRITE 0x00000040 // write information for midi +#define CS_MIDI_READ 0x00000080 // read information for midi +#define CS_MPU401_WRITE 0x00000100 // write information for mpu401 +#define CS_MPU401_READ 0x00000200 // read information for mpu401 +#define CS_OPEN 0x00000400 // all open functions in the driver +#define CS_RELEASE 0x00000800 // all release functions in the driver +#define CS_PARMS 0x00001000 // functional and operational parameters +#define CS_IOCTL 0x00002000 // ioctl (non-mixer) +#define CS_PM 0x00004000 // power management +#define CS_TMP 0x10000000 // tmp debug mask bit + +#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend +#define CS_IOCTL_CMD_RESUME 0x2 // resume +// +// CSDEBUG is usual mode is set to 1, then use the +// cs_debuglevel and cs_debugmask to turn on or off debugging. +// Debug level of 1 has been defined to be kernel errors and info +// that should be printed on any released driver. +// +#if CSDEBUG +#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} +#else +#define CS_DBGOUT(mask,level,x) +#endif + +#if CSDEBUG +static unsigned long cs_debuglevel = 1; // levels range from 1-9 +static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values +module_param(cs_debuglevel, ulong, 0); +module_param(cs_debugmask, ulong, 0); +#endif +#define CS_TRUE 1 +#define CS_FALSE 0 + +// MIDI buffer sizes +#define MIDIINBUF 500 +#define MIDIOUTBUF 500 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define CS4281_MAJOR_VERSION 1 +#define CS4281_MINOR_VERSION 13 +#ifdef __ia64__ +#define CS4281_ARCH 64 //architecture key +#else +#define CS4281_ARCH 32 //architecture key +#endif + +#define CS_TYPE_ADC 0 +#define CS_TYPE_DAC 1 + + +static const char invalid_magic[] = + KERN_CRIT "cs4281: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CS4281_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +//LIST_HEAD(cs4281_devs); +static struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs }; + +struct cs4281_state; + +#include "cs4281_wrapper-24.c" + +struct cs4281_state { + // magic + unsigned int magic; + + // we keep the cards in a linked list + struct cs4281_state *next; + + // pcidev is needed to turn off the DDMA controller at driver shutdown + struct pci_dev *pcidev; + struct list_head list; + + // soundcore stuff + int dev_audio; + int dev_mixer; + int dev_midi; + + // hardware resources + unsigned int pBA0phys, pBA1phys; + char __iomem *pBA0; + char __iomem *pBA1; + unsigned int irq; + + // mixer registers + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + // wave stuff + struct properties { + unsigned fmt; + unsigned fmt_original; // original requested format + unsigned channels; + unsigned rate; + unsigned char clkdiv; + } prop_dac, prop_adc; + unsigned conversion:1; // conversion from 16 to 8 bit in progress + void *tmpbuff; // tmp buffer for sample conversions + unsigned ena; + spinlock_t lock; + struct mutex open_sem; + struct mutex open_sem_adc; + struct mutex open_sem_dac; + mode_t open_mode; + wait_queue_head_t open_wait; + wait_queue_head_t open_wait_adc; + wait_queue_head_t open_wait_dac; + + dma_addr_t dmaaddr_tmpbuff; + unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. + struct dmabuf { + void *rawbuf; // Physical address of + dma_addr_t dmaaddr; + unsigned buforder; // Log base 2 of 'rawbuf' size in bytes.. + unsigned numfrag; // # of 'fragments' in the buffer. + unsigned fragshift; // Log base 2 of fragment size. + unsigned hwptr, swptr; + unsigned total_bytes; // # bytes process since open. + unsigned blocks; // last returned blocks value GETOPTR + unsigned wakeup; // interrupt occurred on block + int count; + unsigned underrun; // underrun flag + unsigned error; // over/underrun + wait_queue_head_t wait; + // redundant, but makes calculations easier + unsigned fragsize; // 2**fragshift.. + unsigned dmasize; // 2**buforder. + unsigned fragsamples; + // OSS stuff + unsigned mapped:1; // Buffer mapped in cs4281_mmap()? + unsigned ready:1; // prog_dmabuf_dac()/adc() successful? + unsigned endcleared:1; + unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + // midi stuff + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct cs4281_pm pm; + struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; +}; + +#include "cs4281pm-24.c" + +#if CSDEBUG + +// DEBUG ROUTINES + +#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) +#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) +#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) +#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) + +#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) + + +static void cs_printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + switch (x) { + case SOUND_MIXER_CS_GETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, + printk("SNDCTL_DSP_SETFRAGMENT:\n")); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_READ_CHANNELS:\n")); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_WRITE_FILTER:\n")); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); + break; + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); + break; + + default: + switch (_IOC_NR(x)) { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_VOLUME:\n")); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SPEAKER:\n")); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECLEV:\n")); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_MIC:\n")); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SYNTH:\n")); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECSRC:\n")); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_DEVMASK:\n")); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECMASK:\n")); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_STEREODEVS:\n")); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); + break; + default: + i = _IOC_NR(x); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) { + CS_DBGOUT(CS_IOCTL, 4, printk + ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", + x, i)); + } else { + CS_DBGOUT(CS_IOCTL, 4, printk + ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", + x, i)); + } + break; + } + } +} +#endif +static int prog_dmabuf_adc(struct cs4281_state *s); +static void prog_codec(struct cs4281_state *s, unsigned type); + +// --------------------------------------------------------------------- +// +// Hardware Interfaces For the CS4281 +// + + +//****************************************************************************** +// "delayus()-- Delay for the specified # of microseconds. +//****************************************************************************** +static void delayus(struct cs4281_state *s, u32 delay) +{ + u32 j; + if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { + j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ + if (j < 1) + j = 1; /* minimum one jiffy. */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(j); + } else + udelay(delay); + return; +} + + +//****************************************************************************** +// "cs4281_read_ac97" -- Reads a word from the specified location in the +// CS4281's address space(based on the BA0 register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register, +// 0h for reads. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h +// 5. if DCV not cleared, break and return error +// 6. Read ACSTS = Status Register = 464h, check VSTS bit +//**************************************************************************** +static int cs4281_read_ac97(struct cs4281_state *card, u32 offset, + u32 * value) +{ + u32 count, status; + + // Make sure that there is not data sitting + // around from a previous uncompleted access. + // ACSDA = Status Data Register = 47Ch + status = readl(card->pBA0 + BA0_ACSDA); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // bit DCV - will clear when process completed + // bit CRW - Read command + // bit VFRM - valid frame enabled + // bit ESYN - ASYNC generation enabled + + // Get the actual AC97 register from the offset + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(0, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the read to occur. + for (count = 0; count < 10; count++) { + // First, we want to wait for a short time. + udelay(25); + + // Now, check to see if the read has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 17h + if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)) + break; + } + + // Make sure the read completed. + if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV) + return 1; + + // Wait for the valid status bit to go active. + for (count = 0; count < 10; count++) { + // Read the AC97 status register. + // ACSTS = Status Register = 464h + status = readl(card->pBA0 + BA0_ACSTS); + + // See if we have valid status. + // VSTS - Valid Status + if (status & ACSTS_VSTS) + break; + // Wait for a short while. + udelay(25); + } + + // Make sure we got valid status. + if (!(status & ACSTS_VSTS)) + return 1; + + // Read the data returned from the AC97 register. + // ACSDA = Status Data Register = 474h + *value = readl(card->pBA0 + BA0_ACSDA); + + // Success. + return (0); +} + + +//**************************************************************************** +// +// "cs4281_write_ac97()"-- writes a word to the specified location in the +// CS461x's address space (based on the part's base address zero register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h +// 5. if DCV not cleared, break and return error +// +//**************************************************************************** +static int cs4281_write_ac97(struct cs4281_state *card, u32 offset, + u32 value) +{ + u32 count, status=0; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n")); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // set DCV - will clear when process completed + // reset CRW - Write command + // set VFRM - valid frame enabled + // set ESYN - ASYNC generation enabled + // set RSTN - ARST# inactive, AC97 codec not reset + + // Get the actual AC97 register from the offset + + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(value, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the write to occur. + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25); + // Now, check to see if the write has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 07h + status = readl(card->pBA0 + BA0_ACCTL); + if (!(status & ACCTL_DCV)) + break; + } + + // Make sure the write completed. + if (status & ACCTL_DCV) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n")); + return 1; + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n")); + // Success. + return 0; +} + + +//****************************************************************************** +// "Init4281()" -- Bring up the part. +//****************************************************************************** +static __devinit int cs4281_hw_init(struct cs4281_state *card) +{ + u32 ac97_slotid; + u32 temp1, temp2; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n")); +#ifndef NOT_CS4281_PM + if(!card) + return 1; +#endif + temp2 = readl(card->pBA0 + BA0_CFLR); + CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2)); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n", + temp2,CS4281_CFLR_DEFAULT)); + writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR); + temp2 = readl(card->pBA0 + BA0_CFLR); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n")); + return 1; + } + } + + //***************************************7 + // Set up the Sound System Configuration + //*************************************** + + // Set the 'Configuration Write Protect' register + // to 4281h. Allows vendor-defined configuration + // space between 0e4h and 0ffh to be written. + + writel(0x4281, card->pBA0 + BA0_CWPR); // (3e0h) + + // (0), Blast the clock control register to zero so that the + // PLL starts out in a known state, and blast the master serial + // port control register to zero so that the serial ports also + // start out in a known state. + + writel(0, card->pBA0 + BA0_CLKCR1); // (400h) + writel(0, card->pBA0 + BA0_SERMC); // (420h) + + + // (1), Make ESYN go to zero to turn off + // the Sync pulse on the AC97 link. + + writel(0, card->pBA0 + BA0_ACCTL); + udelay(50); + + + // (2) Drive the ARST# pin low for a minimum of 1uS (as defined in + // the AC97 spec) and then drive it high. This is done for non + // AC97 modes since there might be logic external to the CS461x + // that uses the ARST# line for a reset. + + writel(0, card->pBA0 + BA0_SPMC); // (3ech) + udelay(100); + writel(SPMC_RSTN, card->pBA0 + BA0_SPMC); + delayus(card,50000); // Wait 50 ms for ABITCLK to become stable. + + // (3) Turn on the Sound System Clocks. + writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1); // (400h) + delayus(card,50000); // Wait for the PLL to stabilize. + // Turn on clocking of the core (CLKCR1(400h) = 0x00000030) + writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1); + + // (4) Power on everything for now.. + writel(0x7E, card->pBA0 + BA0_SSPM); // (740h) + + // (5) Wait for clock stabilization. + for (temp1 = 0; temp1 < 1000; temp1++) { + udelay(1000); + if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY) + break; + } + if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: DLLRDY failed!\n")); + return -EIO; + } + // (6) Enable ASYNC generation. + writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // Now wait 'for a short while' to allow the AC97 + // part to start generating bit clock. (so we don't + // Try to start the PLL without an input clock.) + delayus(card,50000); + + // Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + // (7) Wait for the codec ready signal from the AC97 codec. + + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + udelay(1000); + if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY) // If ready, (464h) + break; // exit the 'for' loop. + } + if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)) // If never came ready, + { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: ACSTS never came ready!\n")); + return -EIO; // exit initialization. + } + // (8) Assert the 'valid frame' signal so we can + // begin sending commands to the AC97 codec. + writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // (9), Wait until CODEC calibration is finished. + // Print an error message if it doesn't. + for (temp1 = 0; temp1 < 1000; temp1++) { + delayus(card,10000); + // Read the AC97 Powerdown Control/Status Register. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2); + if ((temp2 & 0x0000000F) == 0x0000000F) + break; + } + if ((temp2 & 0x0000000F) != 0x0000000F) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: Codec failed to calibrate. Status = %.8x.\n", + temp2)); + return -EIO; + } + // (10), Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + + // (11) Wait until we've sampled input slots 3 & 4 as valid, meaning + // that the codec is pumping ADC data across the AC link. + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + delayus(card,1000); //(test) + + // Read the input slot valid register; See + // if input slots 3 and 4 are valid yet. + if ( + (readl(card->pBA0 + BA0_ACISV) & + (ACISV_ISV3 | ACISV_ISV4)) == + (ACISV_ISV3 | ACISV_ISV4)) break; // Exit the 'for' if slots are valid. + } + // If we never got valid data, exit initialization. + if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) + != (ACISV_ISV3 | ACISV_ISV4)) { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_ERR + "cs4281: Never got valid data!\n")); + return -EIO; // If no valid data, exit initialization. + } + // (12), Start digital data transfer of audio data to the codec. + writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV); // (468h) + + + //************************************** + // Unmute the Master and Alternate + // (headphone) volumes. Set to max. + //************************************** + cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0); + cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0); + + //****************************************** + // Power on the DAC(AddDACUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff); + + // Wait until we sample a DAC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the DAC ready state bit is set, stop waiting. + if (temp1 & 0x2) + break; + } + + //****************************************** + // Power on the ADC(AddADCUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff); + + // Wait until we sample ADC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the ADC ready state bit is set, stop waiting. + if (temp1 & 0x1) + break; + } + // Set up 4281 Register contents that + // don't change for boot duration. + + // For playback, we map AC97 slot 3 and 4(Left + // & Right PCM playback) to DMA Channel 0. + // Set the fifo to be 15 bytes at offset zero. + + ac97_slotid = 0x01000f00; // FCR0.RS[4:0]=1(=>slot4, right PCM playback). + // FCR0.LS[4:0]=0(=>slot3, left PCM playback). + // FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0. + writel(ac97_slotid, card->pBA0 + BA0_FCR0); // (180h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0); // Turn on FIFO Enable. + + // For capture, we map AC97 slot 10 and 11(Left + // and Right PCM Record) to DMA Channel 1. + // Set the fifo to be 15 bytes at offset sixteen. + ac97_slotid = 0x0B0A0f10; // FCR1.RS[4:0]=11(=>slot11, right PCM record). + // FCR1.LS[4:0]=10(=>slot10, left PCM record). + // FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16. + writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1); // (184h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1); // Turn on FIFO Enable. + + // Map the Playback SRC to the same AC97 slots(3 & 4-- + // --Playback left & right)as DMA channel 0. + // Map the record SRC to the same AC97 slots(10 & 11-- + // -- Record left & right) as DMA channel 1. + + ac97_slotid = 0x0b0a0100; // SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback). + // SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback). + // SCRSA.CRSS[4:0]=11(=>slot11, right PCM record) + // SCRSA.CLSS[4:0]=10(=>slot10, left PCM record). + writel(ac97_slotid, card->pBA0 + BA0_SRCSA); // (75ch) + + // Set 'Half Terminal Count Interrupt Enable' and 'Terminal + // Count Interrupt Enable' in DMA Control Registers 0 & 1. + // Set 'MSK' flag to 1 to keep the DMA engines paused. + temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK); // (00030001h) + writel(temp1, card->pBA0 + BA0_DCR0); // (154h + writel(temp1, card->pBA0 + BA0_DCR1); // (15ch) + + // Set 'Auto-Initialize Control' to 'enabled'; For playback, + // set 'Transfer Type Control'(TR[1:0]) to 'read transfer', + // for record, set Transfer Type Control to 'write transfer'. + // All other bits set to zero; Some will be changed @ transfer start. + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); // (20000018h) + writel(temp1, card->pBA0 + BA0_DMR0); // (150h) + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); // (20000014h) + writel(temp1, card->pBA0 + BA0_DMR1); // (158h) + + // Enable DMA interrupts generally, and + // DMA0 & DMA1 interrupts specifically. + temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff; + writel(temp1, card->pBA0 + BA0_HIMR); + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n")); + return 0; +} + +#ifndef NOT_CS4281_PM +static void printpm(struct cs4281_state *s) +{ + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", + (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); + CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", + s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", + s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", + s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", + s->pm.u32SSCR,s->pm.u32SRCSA)); + CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", + s->pm.u32DacASR,s->pm.u32AdcASR)); + CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", + s->pm.u32DacSR,s->pm.u32AdcSR)); + CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", + s->pm.u32MIDCR_Save)); + +} +static void printpipe(struct cs4281_pipeline *pl) +{ + + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n", + (unsigned)pl->flags,pl->number)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n", + pl->u32DBAnValue,pl->u32DBCnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n", + pl->u32DMRnValue,pl->u32DCRnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n", + pl->u32DBAnAddress,pl->u32DBCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n", + pl->u32DCCnAddress,pl->u32DCCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n", + pl->u32DMRnAddress,pl->u32DCRnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n", + pl->u32HDSRnAddress,pl->u32DBAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n", + pl->u32DBCn_Save,pl->u32DMRn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n", + pl->u32DCRn_Save,pl->u32DCCn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n", + pl->u32DCAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n", + pl->u32FCRn_Save,pl->u32FSICn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n", + pl->u32FCRnValue,pl->u32FSICnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n", + pl->u32FCRnAddress,pl->u32FSICnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n", + pl->u32FPDRnValue,pl->u32FPDRnAddress)); +} +static void printpipelines(struct cs4281_state *s) +{ + int i; + for(i=0;ipl[i].flags & CS4281_PIPELINE_VALID) + { + printpipe(&s->pl[i]); + } + } +} +/**************************************************************************** +* +* Suspend - save the ac97 regs, mute the outputs and power down the part. +* +****************************************************************************/ +static void cs4281_ac97_suspend(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n")); +/* +* change the state, save the current hwptr, then stop the dac/adc +*/ + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); + s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); + stop_dac(s); + stop_adc(s); + + for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]); + } +/* +* Save the ac97 volume registers as well as the current powerdown state. +* Now, mute the all the outputs (master, headphone, and mono), as well +* as the PCM volume, in preparation for powering down the entire part. +*/ + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume); + cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume); + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono); + cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume); + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000); + + cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose); + +/* +* And power down everything on the AC97 codec. +*/ + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00); + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n")); +} + +/**************************************************************************** +* +* Resume - power up the part and restore its registers.. +* +****************************************************************************/ +static void cs4281_ac97_resume(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n")); + +/* do not save the power state registers at this time + // + // If we saved away the power control registers, write them into the + // shadows so those saved values get restored instead of the current + // shadowed value. + // + if( bPowerStateSaved ) + { + PokeShadow( 0x26, ulSaveReg0x26 ); + bPowerStateSaved = FALSE; + } +*/ + +// +// First, we restore the state of the general purpose register. This +// contains the mic select (mic1 or mic2) and if we restore this after +// we restore the mic volume/boost state and mic2 was selected at +// suspend time, we will end up with a brief period of time where mic1 +// is selected with the volume/boost settings for mic2, causing +// acoustic feedback. So we restore the general purpose register +// first, thereby getting the correct mic selected before we restore +// the mic volume/boost. +// + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose); + +// +// Now, while the outputs are still muted, restore the state of power +// on the AC97 part. +// + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown); + +/* +* Restore just the first set of registers, from register number +* 0x02 to the register number that ulHighestRegToRestore specifies. +*/ + for( Count = 0x2, i=0; + (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]); + } + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n")); +} + +/* do not save the power state registers at this time +**************************************************************************** +* +* SavePowerState - Save the power registers away. +* +**************************************************************************** +void +HWAC97codec::SavePowerState(void) +{ + ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n"); + + ulSaveReg0x26 = PeekShadow(0x26); + + // + // Note that we have saved registers that need to be restored during a + // resume instead of ulAC97Regs[]. + // + bPowerStateSaved = TRUE; + +} // SavePowerState +*/ + +static void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to save the contents of the BASIC FIFO Registers. + */ + pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress); + pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress); +} +static void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to restore the contents of the BASIC FIFO Registers. + */ + writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress); + writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress); +} +static void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress); + pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress); + pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress); + pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress); + pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress); + pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress); +} +static void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress); + writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress); + writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress); + writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress); + writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress); + writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress); +} + +static int cs4281_suspend(struct cs4281_state *s) +{ + int i; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()+ flags=%d\n", + (unsigned)s->pm.flags)); +/* +* check the current state, only suspend if IDLE +*/ + if(!(s->pm.flags & CS4281_PM_IDLE)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + + pm->u32CLKCR1_SAVE = u32CLKCR1; + if(!(u32CLKCR1 & 0x00010000 ) ) + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + +// +// First, turn on the clocks (yikes) to the devices, so that they will +// respond when we try to save their state. +// + if(!(u32CLKCR1 & CLKCR1_SWCE)) + { + writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1); + } + + // + // Save the power state + // + pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM); + + // + // Disable interrupts. + // + writel(HICR_CHGM, s->pBA0 + BA0_HICR); + + // + // Save the PCM Playback Left and Right Volume Control. + // + pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC); + pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC); + + // + // Save the FM Synthesis Left and Right Volume Control. + // + pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC); + pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC); + + // + // Save the GPIOR value. + // + pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save the JSCTL value. + // + pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save Sound System Control Register + // + pm->u32SSCR = readl(s->pBA0 + BA0_SSCR); + + // + // Save SRC Slot Assinment register + // + pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA); + + // + // Save sample rate + // + pm->u32DacASR = readl(s->pBA0 + BA0_PASR); + pm->u32AdcASR = readl(s->pBA0 + BA0_CASR); + pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); + pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Suspend. + // + cs4281_SuspendDMAengine(s,&s->pl[i]); + cs4281_SuspendFIFO(s,&s->pl[i]); + } + } + // + // We need to save the contents of the Midi Control Register. + // + pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR); +/* +* save off the AC97 part information +*/ + cs4281_ac97_suspend(s); + + // + // Turn off the serial ports. + // + writel(0, s->pBA0 + BA0_SERMC); + + // + // Power off FM, Joystick, AC link, + // + writel(0, s->pBA0 + BA0_SSPM); + + // + // DLL off. + // + writel(0, s->pBA0 + BA0_CLKCR1); + + // + // AC link off. + // + writel(0, s->pBA0 + BA0_SPMC); + + // + // Put the chip into D3(hot) state. + // + // PokeBA0(BA0_PMCS, 0x00000003); + + // + // Gershwin CLKRUN - Clear CKRA + // + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif + + s->pm.flags &= ~CS4281_PM_SUSPENDING; + s->pm.flags |= CS4281_PM_SUSPENDED; + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +static int cs4281_resume(struct cs4281_state *s) +{ + int i; + unsigned temp1; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs4281: cs4281_resume()+ flags=%d\n", + (unsigned)s->pm.flags)); + if(!(s->pm.flags & CS4281_PM_SUSPENDED)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_SUSPENDED; + s->pm.flags |= CS4281_PM_RESUMING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + + // + // set the power state. + // + //old PokeBA0(BA0_PMCS, 0); + + // + // Program the clock circuit and serial ports. + // + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, + printk(KERN_ERR + "cs4281: resume cs4281_hw_init() error.\n")); + return -1; + } + + // + // restore the Power state + // + writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM); + + // + // Set post SRC mix setting (FM or ALT48K) + // + writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Resume. + // + cs4281_ResumeDMAengine(s,&s->pl[i]); + cs4281_ResumeFIFO(s,&s->pl[i]); + } + } + // + // We need to restore the contents of the Midi Control Register. + // + writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR); + + cs4281_ac97_resume(s); + // + // Restore the PCM Playback Left and Right Volume Control. + // + writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC); + writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC); + + // + // Restore the FM Synthesis Left and Right Volume Control. + // + writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC); + writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC); + + // + // Restore the JSCTL value. + // + writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL); + + // + // Restore the GPIOR register value. + // + writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR); + + // + // Restore Sound System Control Register + // + writel(pm->u32SSCR, s->pBA0 + BA0_SSCR); + + // + // Restore SRC Slot Assignment register + // + writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA); + + // + // Restore sample rate + // + writel(pm->u32DacASR, s->pBA0 + BA0_PASR); + writel(pm->u32AdcASR, s->pBA0 + BA0_CASR); + writel(pm->u32DacSR, s->pBA0 + BA0_DACSR); + writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR); + + // + // Restore CFL1/2 registers we saved to compensate for OEM bugs. + // + // PokeBA0(BA0_CFLR, ulConfig); + + // + // Gershwin CLKRUN - Clear CKRA + // + writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1); + + // + // Enable interrupts on the part. + // + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif +/* +* change the state, restore the current hwptrs, then stop the dac/adc +*/ + s->pm.flags |= CS4281_PM_IDLE; + s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED + | CS4281_PM_RESUMING | CS4281_PM_RESUMED); + + writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0); + writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1); + start_dac(s); + start_adc(s); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +#endif + +//****************************************************************************** +// "cs4281_play_rate()" -- +//****************************************************************************** +static void cs4281_play_rate(struct cs4281_state *card, u32 playrate) +{ + u32 DACSRvalue = 1; + + // Based on the sample rate, program the DACSR register. + if (playrate == 8000) + DACSRvalue = 5; + if (playrate == 11025) + DACSRvalue = 4; + else if (playrate == 22050) + DACSRvalue = 2; + else if (playrate == 44100) + DACSRvalue = 1; + else if ((playrate <= 48000) && (playrate >= 6023)) + DACSRvalue = 24576000 / (playrate * 16); + else if (playrate < 6023) + // Not allowed by open. + return; + else if (playrate > 48000) + // Not allowed by open. + return; + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n", + DACSRvalue, playrate)); + // Write the 'sample rate select code' + // to the 'DAC Sample Rate' register. + writel(DACSRvalue, card->pBA0 + BA0_DACSR); // (744h) +} + +//****************************************************************************** +// "cs4281_record_rate()" -- Initialize the record sample rate converter. +//****************************************************************************** +static void cs4281_record_rate(struct cs4281_state *card, u32 outrate) +{ + u32 ADCSRvalue = 1; + + // + // Based on the sample rate, program the ADCSR register + // + if (outrate == 8000) + ADCSRvalue = 5; + if (outrate == 11025) + ADCSRvalue = 4; + else if (outrate == 22050) + ADCSRvalue = 2; + else if (outrate == 44100) + ADCSRvalue = 1; + else if ((outrate <= 48000) && (outrate >= 6023)) + ADCSRvalue = 24576000 / (outrate * 16); + else if (outrate < 6023) { + // Not allowed by open. + return; + } else if (outrate > 48000) { + // Not allowed by open. + return; + } + CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n", + ADCSRvalue, outrate)); + // Write the 'sample rate select code + // to the 'ADC Sample Rate' register. + writel(ADCSRvalue, card->pBA0 + BA0_ADCSR); // (748h) +} + + + +static void stop_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n")); + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR0); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void start_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: start_dac()+\n")); + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || + (s->dma_dac.count > 0 + && s->dma_dac.ready)) +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + s->ena |= FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK; // Clear DMA0 channel mask. + writel(temp1, s->pBA0 + BA0_DCR0); // Start DMA'ing. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + + writel(7, s->pBA0 + BA0_PPRVC); + writel(7, s->pBA0 + BA0_PPLVC); + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO + "cs4281: start_dac(): writel 0x%x start dma\n", temp1)); + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: start_dac()-\n")); +} + + +static void stop_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()+\n")); + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + + if (s->conversion == 1) { + s->conversion = 0; + s->prop_adc.fmt = s->prop_adc.fmt_original; + } + temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR1); + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()-\n")); +} + + +static void start_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()+\n")); + + if (!(s->ena & FMODE_READ) && + (s->dma_adc.mapped || s->dma_adc.count <= + (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize)) + && s->dma_adc.ready +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { + // + // now only use 16 bit capture, due to truncation issue + // in the chip, noticable distortion occurs. + // allocate buffer and then convert from 16 bit to + // 8 bit for the user buffer. + // + s->prop_adc.fmt_original = s->prop_adc.fmt; + if (s->prop_adc.fmt & AFMT_S8) { + s->prop_adc.fmt &= ~AFMT_S8; + s->prop_adc.fmt |= AFMT_S16_LE; + } + if (s->prop_adc.fmt & AFMT_U8) { + s->prop_adc.fmt &= ~AFMT_U8; + s->prop_adc.fmt |= AFMT_U16_LE; + } + // + // prog_dmabuf_adc performs a stop_adc() but that is + // ok since we really haven't started the DMA yet. + // + prog_codec(s, CS_TYPE_ADC); + + if (prog_dmabuf_adc(s) != 0) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: start_adc(): error in prog_dmabuf_adc\n")); + } + s->conversion = 1; + } + spin_lock_irqsave(&s->lock, flags); + s->ena |= FMODE_READ; + temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK; // Clear DMA1 channel mask bit. + writel(temp1, s->pBA0 + BA0_DCR1); // Start recording + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + spin_unlock_irqrestore(&s->lock, flags); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4281: start_adc(): writel 0x%x \n", temp1)); + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()-\n")); + +} + + +// --------------------------------------------------------------------- + +#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. + + +static void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + struct page *map, *mapend; + + if (db->rawbuf) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - + 1); + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + ClearPageReserved(map); + free_dmabuf(s, db); + } + if (s->tmpbuff && (db->type == CS_TYPE_ADC)) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + ClearPageReserved(map); + free_dmabuf2(s, db); + } + s->tmpbuff = NULL; + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec, temp1; + unsigned bufs, sample_shift = 0; + struct page *map, *mapend; + unsigned long df; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()+\n")); + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = + db->endcleared = db->blocks = db->wakeup = db->underrun = 0; +/* +* check for order within limits, but do not overwrite value, check +* later for a fractional defaultorder (i.e. 100+). +*/ + if((defaultorder > 0) && (defaultorder < 12)) + df = defaultorder; + else + df = 1; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = df; order >= DMABUF_MINORDER; order--) + if ( (db->rawbuf = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, &db-> dmaaddr))) + break; + if (!db->rawbuf) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate rawbuf\n")); + return -ENOMEM; + } + db->buforder = order; + // Now mark the pages as reserved; otherwise the + // remap_pfn_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + SetPageReserved(map); + } + if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) { + for (order = df; order >= DMABUF_MINORDER; + order--) + if ( (s->tmpbuff = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, + &s->dmaaddr_tmpbuff))) + break; + if (!s->tmpbuff) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate tmpbuff\n")); + return -ENOMEM; + } + s->buforder_tmpbuff = order; + // Now mark the pages as reserved; otherwise the + // remap_pfn_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + SetPageReserved(map); + } + if (db->type == CS_TYPE_DAC) { + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_dac.channels > 1) + sample_shift++; + bytespersec = s->prop_dac.rate << sample_shift; + } else // CS_TYPE_ADC + { + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_adc.channels > 1) + sample_shift++; + bytespersec = s->prop_adc.rate << sample_shift; + } + bufs = PAGE_SIZE << db->buforder; + +/* +* added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +*/ + if(defaultorder >= 100) + { + bufs = 1 << (defaultorder-100); + } + +#define INTERRUPT_RATE_MS 100 // Interrupt rate in milliseconds. + db->numfrag = 2; +/* +* Nominal frag size(bytes/interrupt) +*/ + temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS); + db->fragshift = 8; // Min 256 bytes. + while (1 << db->fragshift < temp1) // Calc power of 2 frag size. + db->fragshift += 1; + db->fragsize = 1 << db->fragshift; + db->dmasize = db->fragsize * 2; + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + +// If the calculated size is larger than the allocated +// buffer, divide the allocated buffer into 2 fragments. + if (db->dmasize > bufs) { + + db->numfrag = 2; // Two fragments. + db->fragsize = bufs >> 1; // Each 1/2 the alloc'ed buffer. + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + db->dmasize = bufs; // Use all the alloc'ed buffer. + + db->fragshift = 0; // Calculate 'fragshift'. + temp1 = db->fragsize; // update_ptr() uses it + while ((temp1 >>= 1) > 1) // to calc 'total-bytes' + db->fragshift += 1; // returned in DSP_GETI/OPTR. + } + CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO + "cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n", + db->numfrag, db->fragsize, db->fragsamples, + db->fragshift, bufs, + (db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : + s->prop_adc.fmt, + (db->type == CS_TYPE_DAC) ? s->prop_dac.channels : + s->prop_adc.channels)); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()-\n")); + return 0; +} + + +static int prog_dmabuf_adc(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_adc(s); + s->dma_adc.type = CS_TYPE_ADC; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + + if (s->dma_adc.rawbuf) { + memset(s->dma_adc.rawbuf, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_adc.dmasize); + } + if (s->tmpbuff) { + memset(s->tmpbuff, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + PAGE_SIZE << s->buforder_tmpbuff); + } + + va = virt_to_bus(s->dma_adc.rawbuf); + + count = s->dma_adc.dmasize; + + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_adc.channels > 1) + count /= 2; // Assume stereo. + + CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + writel(va, s->pBA0 + BA0_DBA1); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC1); // Set count. + s->dma_adc.ready = 1; + return 0; +} + + +static int prog_dmabuf_dac(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_dac(s); + s->dma_dac.type = CS_TYPE_DAC; + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, + (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_dac.dmasize); + + va = virt_to_bus(s->dma_dac.rawbuf); + + count = s->dma_dac.dmasize; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_dac.channels > 1) + count /= 2; // Assume stereo. + + writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + s->dma_dac.ready = 1; + return 0; +} + + +static void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *) buf) + bptr, c, x); + bptr = 0; + len -= x; + } + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4281: clear_advance(): memset %d at %p for %d size \n", + (unsigned)c, ((char *) buf) + bptr, len)); + memset(((char *) buf) + bptr, c, len); +} + + + +// call with spinlock held! +static void cs4281_update_ptr(struct cs4281_state *s, int intflag) +{ + int diff; + unsigned hwptr, va; + + // update ADC pointer + if (s->ena & FMODE_READ) { + hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. + va = virt_to_bus(s->dma_adc.rawbuf); + hwptr -= (unsigned) va; + diff = + (s->dma_adc.dmasize + hwptr - + s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count > s->dma_adc.dmasize) + s->dma_adc.count = s->dma_adc.dmasize; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= + (signed) s->dma_adc.fragsize) wake_up(&s-> + dma_adc. + wait); + } else { + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", + s, s->dma_adc.hwptr, s->dma_adc.total_bytes, s->dma_adc.count)); + } + // update DAC pointer + // + // check for end of buffer, means that we are going to wait for another interrupt + // to allow silence to fill the fifos on the part, to keep pops down to a minimum. + // + if (s->ena & FMODE_WRITE) { + hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. + va = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) va; + diff = (s->dma_dac.dmasize + hwptr - + s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= s->dma_dac.fragsize) { + s->dma_dac.wakeup = 1; + wake_up(&s->dma_dac.wait); + if (s->dma_dac.count > s->dma_dac.dmasize) + s->dma_dac.count &= + s->dma_dac.dmasize - 1; + } + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + // + // fill with silence, and do not shut down the DAC. + // Continue to play silence until the _release. + // + CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): memset %d at %p for %d size \n", + (unsigned)(s->prop_dac.fmt & + (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_dac.rawbuf, s->dma_dac.dmasize)); + memset(s->dma_dac.rawbuf, + (s->prop_dac. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? + 0x80 : 0, s->dma_dac.dmasize); + if (s->dma_dac.count < 0) { + s->dma_dac.underrun = 1; + s->dma_dac.count = 0; + CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): underrun\n")); + } + } else if (s->dma_dac.count <= + (signed) s->dma_dac.fragsize + && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, + s->dma_dac.dmasize, + s->dma_dac.swptr, + s->dma_dac.fragsize, + (s->prop_dac. + fmt & (AFMT_U8 | + AFMT_U16_LE)) ? 0x80 + : 0); + s->dma_dac.endcleared = 1; + } + if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) || + intflag) + { + wake_up(&s->dma_dac.wait); + } + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", + s, s->dma_dac.hwptr, s->dma_dac.total_bytes, s->dma_dac.count)); + } +} + + +// --------------------------------------------------------------------- + +static void prog_codec(struct cs4281_state *s, unsigned type) +{ + unsigned long flags; + unsigned temp1, format; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()+ \n")); + + spin_lock_irqsave(&s->lock, flags); + if (type == CS_TYPE_ADC) { + temp1 = readl(s->pBA0 + BA0_DCR1); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_record_rate(s, s->prop_adc.rate); + + // program ADC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? + format |= DMRn_BEND; + if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + if (s->prop_adc.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR1); + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_adc.rate, format)); + + s->ena &= ~FMODE_READ; // not capturing data yet + } + + + if (type == CS_TYPE_DAC) { + temp1 = readl(s->pBA0 + BA0_DCR0); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_play_rate(s, s->prop_dac.rate); + + // program DAC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) + format |= DMRn_BEND; // Big Endian. + if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + + if (s->prop_dac.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR0); + + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_dac.rate, format)); + + s->ena &= ~FMODE_WRITE; // not capturing data yet + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()- \n")); +} + + +static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd, + unsigned long arg) +{ + // Index to mixer_src[] is value of AC97 Input Mux Select Reg. + // Value of array member is recording source Device ID Mask. + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, + SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 + }; + void __user *argp = (void __user *)arg; + + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + + static const unsigned mixreg[] = { + BA0_AC97_PCM_OUT_VOLUME, + BA0_AC97_AUX_VOLUME, + BA0_AC97_CD_VOLUME, + BA0_AC97_LINE_IN_VOLUME + }; + unsigned char l, r, rl, rr, vidx; + unsigned char attentbl[11] = + { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; + unsigned temp1; + int i, val; + + VALIDATE_STATE(s); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: mixer_ioctl(): s=%p cmd=0x%.8x\n", s, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif +#if CSDEBUG_INTERFACE + + if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_APM)) + { + switch (cmd) { + + case SOUND_MIXER_CS_GETDBGMASK: + return put_user(cs_debugmask, + (unsigned long __user *) argp); + + case SOUND_MIXER_CS_GETDBGLEVEL: + return put_user(cs_debuglevel, + (unsigned long __user *) argp); + + case SOUND_MIXER_CS_SETDBGMASK: + if (get_user(val, (unsigned long __user *) argp)) + return -EFAULT; + cs_debugmask = val; + return 0; + + case SOUND_MIXER_CS_SETDBGLEVEL: + if (get_user(val, (unsigned long __user *) argp)) + return -EFAULT; + cs_debuglevel = val; + return 0; +#ifndef NOT_CS4281_PM + case SOUND_MIXER_CS_APM: + if (get_user(val, (unsigned long __user *) argp)) + return -EFAULT; + if(val == CS_IOCTL_CMD_SUSPEND) + cs4281_suspend(s); + else if(val == CS_IOCTL_CMD_RESUME) + cs4281_resume(s); + else + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n", + val)); + } + return 0; +#endif + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); + return 0; + } + } +#endif + + if (cmd == SOUND_MIXER_PRIVATE1) { + // enable/disable/query mixer preamp + if (get_user(val, (int __user *) argp)) + return -EFAULT; + if (val != -1) { + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + val = (temp1 & 0x40) ? 1 : 0; + return put_user(val, (int __user *) argp); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + // enable/disable/query spatializer + if (get_user(val, (int __user *)argp)) + return -EFAULT; + if (val != -1) { + temp1 = (val & 0x3f) >> 2; + cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, + &temp1); + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, + temp1 | 0x2000); + } + cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); + return put_user((temp1 << 2) | 3, (int __user *)argp); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strlcpy(info.id, "CS4281", sizeof(info.id)); + strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strlcpy(info.id, "CS4281", sizeof(info.id)); + strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int __user *) argp); + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + // If ioctl has only the SIOC_READ bit(bit 31) + // on, process the only-read commands. + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + cs4281_read_ac97(s, BA0_AC97_RECORD_SELECT, &temp1); + return put_user(mixer_src[temp1&7], (int __user *)argp); + + case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int __user *)argp); + + case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_CD | SOUND_MASK_VOLUME | + SOUND_MASK_LINE1, (int __user *) argp); + + case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV, (int __user *)argp); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int __user *)argp); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx - 1], (int __user *)argp); + } + } + // If ioctl doesn't have both the SIOC_READ and + // the SIOC_WRITE bit set, return invalid. + if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) + return -EINVAL; + + // Increment the count of volume writes. + s->mix.modcnt++; + + // Isolate the command; it must be a write. + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + if (get_user(val, (int __user *)argp)) + return -EFAULT; + i = hweight32(val); // i = # bits on in val. + if (i != 1) // One & only 1 bit must be on. + return 0; + for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { + if (val == mixer_src[i]) { + temp1 = (i << 8) | i; + cs4281_write_ac97(s, + BA0_AC97_RECORD_SELECT, + temp1); + return 0; + } + } + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; // Max soundcard.h vol is 100. + if (l < 6) { + rl = 63; + l = 0; + } else + rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; // Max right volume is 100, too + if (r < 6) { + rr = 63; + r = 0; + } else + rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. + + if ((rl > 60) && (rr > 60)) // If both l & r are 'low', + temp1 = 0x8000; // turn on the mute bit. + else + temp1 = 0; + + temp1 |= (rl << 8) | rr; + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int __user *)argp); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 3) { + rl = 0; + l = 0; + } else { + rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. + l = (rl * 13 + 5) / 2; + } + + if (rl < 3) { + temp1 = 0x8000; + rl = 0; + } else + temp1 = 0; + rl = 15 - rl; // Convert volume to attenuation. + temp1 |= rl << 1; + cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[6] = l << 8; +#else + s->mix.vol[6] = val; +#endif + return put_user(s->mix.vol[6], (int __user *)argp); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. + rr = (r * 2 - 5) / 13; + if (rl < 3 && rr < 3) + temp1 = 0x8000; + else + temp1 = 0; + + temp1 = temp1 | (rl << 8) | rr; + cs4281_write_ac97(s, BA0_AC97_RECORD_GAIN, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int __user *)argp); + + case SOUND_MIXER_MIC: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 0; + } else { + rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. + l = (rl * 16 + 4) / 5; + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 &= 0x40; // Isolate 20db gain bit. + if (rl < 3) { + temp1 |= 0x8000; + rl = 0; + } + rl = 31 - rl; // Convert volume to attenuation. + temp1 |= rl; + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[5] = val << 8; +#else + s->mix.vol[5] = val; +#endif + return put_user(s->mix.vol[5], (int __user *)argp); + + + case SOUND_MIXER_SYNTH: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (get_user(val, (int __user *)argp)) + return -EFAULT; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. + rr = (r * 2 - 11) / 3; + if (rl < 3) // If l is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + + rl = 63 - rl; // Convert vol to attenuation. + writel(temp1 | rl, s->pBA0 + BA0_FMLVC); + if (rr < 3) // If rr is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + rr = 63 - rr; // Convert vol to attenuation. + writel(temp1 | rr, s->pBA0 + BA0_FMRVC); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[4] = (r << 8) | l; +#else + s->mix.vol[4] = val; +#endif + return put_user(s->mix.vol[4], (int __user *)argp); + + + default: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: mixer_ioctl(): default\n")); + + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 31; + } else + rl = (attentbl[(l * 10) / 100]) >> 1; + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (r < 1) { + r = 0; + rr = 31; + } else + rr = (attentbl[(r * 10) / 100]) >> 1; + if ((rl > 30) && (rr > 30)) + temp1 = 0x8000; + else + temp1 = 0; + temp1 = temp1 | (rl << 8) | rr; + cs4281_write_ac97(s, mixreg[vidx - 1], temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[vidx - 1] = val; +#endif +#ifndef NOT_CS4281_PM + CS_DBGOUT(CS_PM, 9, printk(KERN_INFO + "write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", + vidx-1,temp1,s->mix.vol[vidx-1])); +#endif + return put_user(s->mix.vol[vidx - 1], (int __user *)argp); + } +} + + +// --------------------------------------------------------------------- + +static int cs4281_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n")); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + if(s->dev_mixer == minor) + break; + } + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()- 0\n")); + + return nonseekable_open(inode, file); +} + + +static int cs4281_release_mixdev(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + VALIDATE_STATE(s); + return 0; +} + + +static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cs4281_state *) file->private_data, cmd, + arg); +} + + +// ****************************************************************************************** +// Mixer file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = cs4281_ioctl_mixdev, + .open = cs4281_open_mixdev, + .release = cs4281_release_mixdev, +}; + +// --------------------------------------------------------------------- + + +static int drain_adc(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_adc.mapped) + return 0; + add_wait_queue(&s->dma_adc.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_adc() %d\n", count)); + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: drain_adc() count<0\n")); + break; + } + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_adc.fragsize) / 2 / s->prop_adc.rate; + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_adc.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_dac.fragsize) / 2 / s->prop_dac.rate; + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_dac.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +//**************************************************************************** +// +// CopySamples copies 16-bit stereo samples from the source to the +// destination, possibly converting down to either 8-bit or mono or both. +// count specifies the number of output bytes to write. +// +// Arguments: +// +// dst - Pointer to a destination buffer. +// src - Pointer to a source buffer +// count - The number of bytes to copy into the destination buffer. +// iChannels - Stereo - 2 +// Mono - 1 +// fmt - AFMT_xxx (soundcard.h formats) +// +// NOTES: only call this routine for conversion to 8bit from 16bit +// +//**************************************************************************** +static void CopySamples(char *dst, char *src, int count, int iChannels, + unsigned fmt) +{ + + unsigned short *psSrc; + long lAudioSample; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: CopySamples()+ ")); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " dst=%p src=%p count=%d iChannels=%d fmt=0x%x\n", + dst, src, (unsigned) count, (unsigned) iChannels, (unsigned) fmt)); + + // Gershwin does format conversion in hardware so normally + // we don't do any host based coversion. The data formatter + // truncates 16 bit data to 8 bit and that causes some hiss. + // We have already forced the HW to do 16 bit sampling and + // 2 channel so that we can use software to round instead + // of truncate + + // + // See if the data should be output as 8-bit unsigned stereo. + // or if the data should be output at 8-bit unsigned mono. + // + if ( ((iChannels == 2) && (fmt & AFMT_U8)) || + ((iChannels == 1) && (fmt & AFMT_U8)) ) { + // + // Convert each 16-bit unsigned stereo sample to 8-bit unsigned + // stereo using rounding. + // + psSrc = (unsigned short *) src; + count = count / 2; + while (count--) { + lAudioSample = (long) psSrc[count] + (long) 0x80; + if (lAudioSample > 0xffff) { + lAudioSample = 0xffff; + } + dst[count] = (char) (lAudioSample >> 8); + } + } + // + // check for 8-bit signed stereo. + // + else if ((iChannels == 2) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit stereo sample to 8-bit stereo using rounding. + // + psSrc = (short *) src; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } + // + // Otherwise, the data should be output as 8-bit signed mono. + // + else if ((iChannels == 1) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit signed mono sample to 8-bit signed mono + // using rounding. + // + psSrc = (short *) src; + count = count / 2; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + if (lAudioSample > 0x7fff) { + lAudioSample = 0x7fff; + } + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } +} + +// +// cs_copy_to_user() +// replacement for the standard copy_to_user, to allow for a conversion from +// 16 bit to 8 bit if the record conversion is active. the cs4281 has some +// issues with 8 bit capture, so the driver always captures data in 16 bit +// and then if the user requested 8 bit, converts from 16 to 8 bit. +// +static unsigned cs_copy_to_user(struct cs4281_state *s, void __user *dest, + unsigned *hwsrc, unsigned cnt, + unsigned *copied) +{ + void *src = hwsrc; //default to the standard destination buffer addr + + CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO + "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=%p\n", + s->prop_adc.fmt, s->prop_adc.fmt_original, + (unsigned) cnt, dest)); + + if (cnt > s->dma_adc.dmasize) { + cnt = s->dma_adc.dmasize; + } + if (!cnt) { + *copied = 0; + return 0; + } + if (s->conversion) { + if (!s->tmpbuff) { + *copied = cnt / 2; + return 0; + } + CopySamples(s->tmpbuff, (void *) hwsrc, cnt, + (unsigned) s->prop_adc.channels, + s->prop_adc.fmt_original); + src = s->tmpbuff; + cnt = cnt / 2; + } + + if (copy_to_user(dest, src, cnt)) { + *copied = 0; + return -EFAULT; + } + *copied = cnt; + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt)); + return 0; +} + +// --------------------------------------------------------------------- + +static ssize_t cs4281_read(struct file *file, char __user *buffer, size_t count, + loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned copied = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_read()+ %Zu \n", count)); + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +// +// "count" is the amount of bytes to read (from app), is decremented each loop +// by the amount of bytes that have been returned to the user buffer. +// "cnt" is the running total of each read from the buffer (changes each loop) +// "buffer" points to the app's buffer +// "ret" keeps a running total of the amount of bytes that have been copied +// to the user buffer. +// "copied" is the total bytes copied into the user buffer for each loop. +// + while (count > 0) { + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + "_read() count>0 count=%Zu .count=%d .swptr=%d .hwptr=%d \n", + count, s->dma_adc.count, + s->dma_adc.swptr, s->dma_adc.hwptr)); + spin_lock_irqsave(&s->lock, flags); + + // get the current copy point of the sw buffer + swptr = s->dma_adc.swptr; + + // cnt is the amount of unread bytes from the end of the + // hw buffer to the current sw pointer + cnt = s->dma_adc.dmasize - swptr; + + // dma_adc.count is the current total bytes that have not been read. + // if the amount of unread bytes from the current sw pointer to the + // end of the buffer is greater than the current total bytes that + // have not been read, then set the "cnt" (unread bytes) to the + // amount of unread bytes. + + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + // + // if we are converting from 8/16 then we need to copy + // twice the number of 16 bit bytes then 8 bit bytes. + // + if (s->conversion) { + if (cnt > (count * 2)) + cnt = (count * 2); + } else { + if (cnt > count) + cnt = count; + } + // + // "cnt" NOW is the smaller of the amount that will be read, + // and the amount that is requested in this read (or partial). + // if there are no bytes in the buffer to read, then start the + // ADC and wait for the interrupt handler to wake us up. + // + if (cnt <= 0) { + + // start up the dma engine and then continue back to the top of + // the loop when wake up occurs. + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + // there are bytes in the buffer to read. + // copy from the hw buffer over to the user buffer. + // user buffer is designated by "buffer" + // virtual address to copy from is rawbuf+swptr + // the "cnt" is the number of bytes to read. + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%Zu ", cnt, count)); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .dmasize=%d .count=%d buffer=%p ret=%Zd\n", + s->dma_adc.dmasize, s->dma_adc.count, buffer, ret)); + + if (cs_copy_to_user + (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_read()- %Zd\n", ret)); + return ret; +} + + +static ssize_t cs4281_write(struct file *file, const char __user *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr, hwptr, busaddr; + int cnt; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_write()+ count=%Zu\n", + count)); + VALIDATE_STATE(s); + + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->dma_dac.underrun) { + s->dma_dac.underrun = 0; + hwptr = readl(s->pBA0 + BA0_DCA0); + busaddr = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) busaddr; + s->dma_dac.swptr = s->dma_dac.hwptr = hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize - swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_write()- %Zd\n", ret)); + return ret; +} + + +static unsigned int cs4281_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4281: cs4281_poll()+\n")); + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4281: cs4281_poll() wait on FMODE_WRITE\n")); + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4281: cs4281_poll() wait on FMODE_READ\n")); + if(!s->dma_dac.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed) s->dma_dac.fragsize) { + if (s->dma_dac.wakeup) + mask |= POLLOUT | POLLWRNORM; + else + mask = 0; + s->dma_dac.wakeup = 0; + } + } else { + if ((signed) (s->dma_dac.dmasize/2) >= s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } else if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4281: cs4281_poll()- 0x%.8x\n", + mask)); + return mask; +} + + +static int cs4281_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()+\n")); + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; +// +// only support PLAYBACK for now +// + db = &s->dma_dac; + + if (cs4x_pgoff(vma) != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n", + (unsigned) size)); + + return 0; +} + + +static int cs4281_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + int __user *p = (int __user *)arg; + + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): file=%p cmd=0x%.8x\n", file, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): SOUND_VERSION=0x%.8x\n", + SOUND_VERSION)); + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SYNC\n")); + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, + 0 /*file->f_flags & O_NONBLOCK */ + ); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, + p); + + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_RESET\n")); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = + s->dma_dac.count = s->dma_dac.total_bytes = + s->dma_dac.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_DAC); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = + s->dma_adc.count = s->dma_adc.total_bytes = + s->dma_adc.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_ADC); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val)); + // + // support independent capture and playback channels + // assume that the file mode bit determines the + // direction of the data flow. + // + if (file->f_mode & FMODE_READ) { + if (val >= 0) { + stop_adc(s); + s->dma_adc.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_adc.rate = val; + prog_codec(s, CS_TYPE_ADC); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val >= 0) { + stop_dac(s); + s->dma_dac.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_dac.rate = val; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.rate; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.rate; + + return put_user(val, p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_STEREO val=%d\n", val)); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + s->prop_adc.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + s->prop_dac.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_DAC); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_CHANNELS val=%d\n", + val)); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + s->prop_adc.channels = 2; + else + s->prop_adc.channels = 1; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + s->prop_dac.channels = 2; + else + s->prop_dac.channels = 1; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.channels; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.channels; + + return put_user(val, p); + + case SNDCTL_DSP_GETFMTS: // Returns a mask + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_GETFMT val=0x%.8x\n", + AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8)); + return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SETFMT val=0x%.8x\n", + val)); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_adc.fmt = val; + s->prop_adc.fmt_original = s->prop_adc.fmt; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_dac.fmt = val; + s->prop_dac.fmt_original = s->prop_dac.fmt; + prog_codec(s, CS_TYPE_DAC); + } + } else { + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.fmt_original; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.fmt_original; + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SETFMT return val=0x%.8x\n", + val)); + return put_user(val, p); + + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_POST\n")); + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready + && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready + && (ret = prog_dmabuf_dac(s))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + abinfo.fragsize = s->dma_dac.fragsize; + if (s->dma_dac.mapped) + abinfo.bytes = s->dma_dac.dmasize; + else + abinfo.bytes = + s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", + abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, + abinfo.fragments)); + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(p, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + if (s->conversion) { + abinfo.fragsize = s->dma_adc.fragsize / 2; + abinfo.bytes = s->dma_adc.count / 2; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> (s->dma_adc.fragshift - 1); + } else { + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> s->dma_adc.fragshift; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(p, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if(!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_adc.total_bytes; + if (s->dma_adc.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_adc.fragshift) - + s->dma_adc.blocks; + s->dma_adc.blocks = + cinfo.bytes >> s->dma_adc.fragshift; + } else { + if (s->conversion) { + cinfo.blocks = + s->dma_adc.count / + 2 >> (s->dma_adc.fragshift - 1); + } else + cinfo.blocks = + s->dma_adc.count >> s->dma_adc. + fragshift; + } + if (s->conversion) + cinfo.ptr = s->dma_adc.hwptr / 2; + else + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(p, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_dac.total_bytes; + if (s->dma_dac.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_dac.fragshift) - + s->dma_dac.blocks; + s->dma_dac.blocks = + cinfo.bytes >> s->dma_dac.fragshift; + } else { + cinfo.blocks = + s->dma_dac.count >> s->dma_dac.fragshift; + } + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(p, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf_adc(s))) + return val; + if (s->conversion) + return put_user(s->dma_adc.fragsize / 2, p); + else + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + return 0; // Say OK, but do nothing. + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) + || (file->f_mode & FMODE_WRITE + && s->dma_dac.subdivision)) return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + else if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.rate, p); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.rate, p); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.channels, p); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.channels, p); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return + put_user( + (s->prop_adc. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + p); + else if (file->f_mode & FMODE_WRITE) + return + put_user( + (s->prop_dac. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return mixer_ioctl(s, cmd, arg); +} + + +static int cs4281_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO + "cs4281: cs4281_release(): inode=%p file=%p f_mode=%d\n", + inode, file, file->f_mode)); + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + drain_dac(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_sem_dac); + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + s->open_mode &= ~FMODE_WRITE; + mutex_unlock(&s->open_sem_dac); + wake_up(&s->open_wait_dac); + } + if (file->f_mode & FMODE_READ) { + drain_adc(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_sem_adc); + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + s->open_mode &= ~FMODE_READ; + mutex_unlock(&s->open_sem_adc); + wake_up(&s->open_wait_adc); + } + return 0; +} + +static int cs4281_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): inode=%p file=%p f_mode=0x%x\n", + inode, file, file->f_mode)); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + // wait for device to become free + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n")); + return -ENODEV; + } + if (file->f_mode & FMODE_WRITE) { + mutex_lock(&s->open_sem_dac); + while (s->open_mode & FMODE_WRITE) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_sem_dac); + return -EBUSY; + } + mutex_unlock(&s->open_sem_dac); + interruptible_sleep_on(&s->open_wait_dac); + + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_sem_dac); + } + } + if (file->f_mode & FMODE_READ) { + mutex_lock(&s->open_sem_adc); + while (s->open_mode & FMODE_READ) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_sem_adc); + return -EBUSY; + } + mutex_unlock(&s->open_sem_adc); + interruptible_sleep_on(&s->open_wait_adc); + + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_sem_adc); + } + } + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + if (file->f_mode & FMODE_READ) { + s->prop_adc.fmt = AFMT_U8; + s->prop_adc.fmt_original = s->prop_adc.fmt; + s->prop_adc.channels = 1; + s->prop_adc.rate = 8000; + s->prop_adc.clkdiv = 96 | 0x80; + s->conversion = 0; + s->ena &= ~FMODE_READ; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = 0; + mutex_unlock(&s->open_sem_adc); + + if (prog_dmabuf_adc(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4281: adc Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + s->prop_dac.fmt = AFMT_U8; + s->prop_dac.fmt_original = s->prop_dac.fmt; + s->prop_dac.channels = 1; + s->prop_dac.rate = 8000; + s->prop_dac.clkdiv = 96 | 0x80; + s->conversion = 0; + s->ena &= ~FMODE_WRITE; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = 0; + mutex_unlock(&s->open_sem_dac); + + if (prog_dmabuf_dac(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4281: dac Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_DAC); + } + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, + printk(KERN_INFO "cs4281: cs4281_open()- 0\n")); + return nonseekable_open(inode, file); +} + + +// ****************************************************************************************** +// Wave (audio) file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = cs4281_read, + .write = cs4281_write, + .poll = cs4281_poll, + .ioctl = cs4281_ioctl, + .mmap = cs4281_mmap, + .open = cs4281_open, + .release = cs4281_release, +}; + +// --------------------------------------------------------------------- + +// hold spinlock for the following! +static void cs4281_handle_midi(struct cs4281_state *s) +{ + unsigned char ch; + int wake; + unsigned temp1; + + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) { + ch = readl(s->pBA0 + BA0_MIDRP); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) { + temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff; + writel(temp1, s->pBA0 + BA0_MIDWP); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF - 16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + + + +static irqreturn_t cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs4281_state *s = (struct cs4281_state *) dev_id; + unsigned int temp1; + + // fastpath out, to ease interrupt sharing + temp1 = readl(s->pBA0 + BA0_HISR); // Get Int Status reg. + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1)); +/* +* If not DMA or MIDI interrupt, then just return. +*/ + if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) { + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO + "cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n")); + return IRQ_NONE; + } + + if (temp1 & HISR_DMA0) // If play interrupt, + readl(s->pBA0 + BA0_HDSR0); // clear the source. + + if (temp1 & HISR_DMA1) // Same for play. + readl(s->pBA0 + BA0_HDSR1); + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Local EOI + + spin_lock(&s->lock); + cs4281_update_ptr(s,CS_TRUE); + cs4281_handle_midi(s); + spin_unlock(&s->lock); + return IRQ_HANDLED; +} + +// ************************************************************************** + +static void cs4281_midi_timer(unsigned long data) +{ + struct cs4281_state *s = (struct cs4281_state *) data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies + 1; + add_timer(&s->midi.timer); +} + + +// --------------------------------------------------------------------- + +static ssize_t cs4281_midi_read(struct file *file, char __user *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + + +static ssize_t cs4281_midi_write(struct file *file, const char __user *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + + +static unsigned int cs4281_midi_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int cs4281_midi_open(struct inode *inode, struct file *file) +{ + unsigned long flags, temp1; + unsigned int minor = iminor(inode); + struct cs4281_state *s=NULL; + struct list_head *entry; + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (s->dev_midi == minor) + break; + } + + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + // wait for device to become free + mutex_lock(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_sem); + return -EBUSY; + } + mutex_unlock(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + writel(1, s->pBA0 + BA0_MIDCR); // Reset the interface. + writel(0, s->pBA0 + BA0_MIDCR); // Return to normal mode. + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + writel(0x0000000f, s->pBA0 + BA0_MIDCR); // Enable transmit, record, ints. + temp1 = readl(s->pBA0 + BA0_HIMR); + writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR); // Enable midi int. recognition. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies + 1; + s->midi.timer.data = (unsigned long) s; + s->midi.timer.function = cs4281_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= + (file-> + f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + mutex_unlock(&s->open_sem); + return nonseekable_open(inode, file); +} + + +static int cs4281_midi_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG + "cs4281: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + mutex_lock(&s->open_sem); + s->open_mode &= + (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + writel(0, s->pBA0 + BA0_MIDCR); // Disable Midi interrupts. + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->open_sem); + wake_up(&s->open_wait); + return 0; +} + +// ****************************************************************************************** +// Midi file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_midi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = cs4281_midi_read, + .write = cs4281_midi_write, + .poll = cs4281_midi_poll, + .open = cs4281_midi_open, + .release = cs4281_midi_release, +}; + + +// --------------------------------------------------------------------- + +// maximum number of devices +#define NR_DEVICE 8 // Only eight devices supported currently. + +// --------------------------------------------------------------------- + +static struct initvol { + int mixch; + int vol; +} initvol[] __devinitdata = { + + { + SOUND_MIXER_WRITE_VOLUME, 0x4040}, { + SOUND_MIXER_WRITE_PCM, 0x4040}, { + SOUND_MIXER_WRITE_SYNTH, 0x4040}, { + SOUND_MIXER_WRITE_CD, 0x4040}, { + SOUND_MIXER_WRITE_LINE, 0x4040}, { + SOUND_MIXER_WRITE_LINE1, 0x4040}, { + SOUND_MIXER_WRITE_RECLEV, 0x0000}, { + SOUND_MIXER_WRITE_SPEAKER, 0x4040}, { + SOUND_MIXER_WRITE_MIC, 0x0000} +}; + + +#ifndef NOT_CS4281_PM +static void __devinit cs4281_BuildFIFO( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ + switch(p->number) + { + case 0: /* playback */ + { + p->u32FCRnAddress = BA0_FCR0; + p->u32FSICnAddress = BA0_FSIC0; + p->u32FPDRnAddress = BA0_FPDR0; + break; + } + case 1: /* capture */ + { + p->u32FCRnAddress = BA0_FCR1; + p->u32FSICnAddress = BA0_FSIC1; + p->u32FPDRnAddress = BA0_FPDR1; + break; + } + + case 2: + { + p->u32FCRnAddress = BA0_FCR2; + p->u32FSICnAddress = BA0_FSIC2; + p->u32FPDRnAddress = BA0_FPDR2; + break; + } + case 3: + { + p->u32FCRnAddress = BA0_FCR3; + p->u32FSICnAddress = BA0_FSIC3; + p->u32FPDRnAddress = BA0_FPDR3; + break; + } + default: + break; + } + // + // first read the hardware to initialize the member variables + // + p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress); + p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress); + p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress); + +} + +static void __devinit cs4281_BuildDMAengine( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ +/* +* initialize all the addresses of this pipeline dma info. +*/ + switch(p->number) + { + case 0: /* playback */ + { + p->u32DBAnAddress = BA0_DBA0; + p->u32DCAnAddress = BA0_DCA0; + p->u32DBCnAddress = BA0_DBC0; + p->u32DCCnAddress = BA0_DCC0; + p->u32DMRnAddress = BA0_DMR0; + p->u32DCRnAddress = BA0_DCR0; + p->u32HDSRnAddress = BA0_HDSR0; + break; + } + + case 1: /* capture */ + { + p->u32DBAnAddress = BA0_DBA1; + p->u32DCAnAddress = BA0_DCA1; + p->u32DBCnAddress = BA0_DBC1; + p->u32DCCnAddress = BA0_DCC1; + p->u32DMRnAddress = BA0_DMR1; + p->u32DCRnAddress = BA0_DCR1; + p->u32HDSRnAddress = BA0_HDSR1; + break; + } + + case 2: + { + p->u32DBAnAddress = BA0_DBA2; + p->u32DCAnAddress = BA0_DCA2; + p->u32DBCnAddress = BA0_DBC2; + p->u32DCCnAddress = BA0_DCC2; + p->u32DMRnAddress = BA0_DMR2; + p->u32DCRnAddress = BA0_DCR2; + p->u32HDSRnAddress = BA0_HDSR2; + break; + } + + case 3: + { + p->u32DBAnAddress = BA0_DBA3; + p->u32DCAnAddress = BA0_DCA3; + p->u32DBCnAddress = BA0_DBC3; + p->u32DCCnAddress = BA0_DCC3; + p->u32DMRnAddress = BA0_DMR3; + p->u32DCRnAddress = BA0_DCR3; + p->u32HDSRnAddress = BA0_HDSR3; + break; + } + default: + break; + } + +// +// Initialize the dma values for this pipeline +// + p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress); + p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress); + p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress); + p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress); + +} + +static void __devinit cs4281_InitPM(struct cs4281_state *s) +{ + int i; + struct cs4281_pipeline *p; + + for(i=0;ipl[i]; + p->number = i; + cs4281_BuildDMAengine(p,s); + cs4281_BuildFIFO(p,s); + /* + * currently only 2 pipelines are used + * so, only set the valid bit on the playback and capture. + */ + if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || + (i == CS4281_CAPTURE_PIPELINE_NUMBER)) + p->flags |= CS4281_PIPELINE_VALID; + } + s->pm.u32SSPM_BITS = 0x7e; /* rev c, use 0x7c for rev a or b */ +} +#endif + +static int __devinit cs4281_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + struct cs4281_state *s; + dma_addr_t dma_mask; + mm_segment_t fs; + int i, val; + unsigned int temp1, temp2; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, + printk(KERN_INFO "cs4281: probe()+\n")); + + if (pci_enable_device(pcidev)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: pci_enable_device() failed\n")); + return -1; + } + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe()- Memory region not assigned\n")); + return -ENODEV; + } + if (pcidev->irq == 0) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() IRQ not assigned\n")); + return -ENODEV; + } + dma_mask = 0xffffffff; /* this enables playback and recording */ + i = pci_set_dma_mask(pcidev, dma_mask); + if (i) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n")); + return i; + } + if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() no memory for state struct.\n")); + return -1; + } + memset(s, 0, sizeof(struct cs4281_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->open_wait_adc); + init_waitqueue_head(&s->open_wait_dac); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + mutex_init(&s->open_sem); + mutex_init(&s->open_sem_adc); + mutex_init(&s->open_sem_dac); + spin_lock_init(&s->lock); + s->pBA0phys = pci_resource_start(pcidev, 0); + s->pBA1phys = pci_resource_start(pcidev, 1); + + /* Convert phys to linear. */ + s->pBA0 = ioremap_nocache(s->pBA0phys, 4096); + if (!s->pBA0) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA0 I/O mapping failed. Skipping part.\n")); + goto err_free; + } + s->pBA1 = ioremap_nocache(s->pBA1phys, 65536); + if (!s->pBA1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA1 I/O mapping failed. Skipping part.\n")); + goto err_unmap; + } + + temp1 = readl(s->pBA0 + BA0_PCICFG00); + temp2 = readl(s->pBA0 + BA0_PCICFG04); + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=%p pBA1=%p \n", + (unsigned) temp1, (unsigned) temp2, s->pBA0, s->pBA1)); + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", + (unsigned) s->pBA0phys, (unsigned) s->pBA1phys)); + +#ifndef NOT_CS4281_PM + s->pm.flags = CS4281_PM_IDLE; +#endif + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: cs4281_hw_init() failed. Skipping part.\n")); + goto err_irq; + } + s->magic = CS4281_MAGIC; + s->pcidev = pcidev; + s->irq = pcidev->irq; + if (request_irq + (s->irq, cs4281_interrupt, IRQF_SHARED, "Crystal CS4281", s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, + printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); + goto err_irq; + } + if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_dsp() failed.\n")); + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_mixer() failed.\n")); + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_midi() failed.\n")); + goto err_dev3; + } +#ifndef NOT_CS4281_PM + cs4281_InitPM(s); + s->pm.flags |= CS4281_PM_NOT_REGISTERED; +#endif + + pci_set_master(pcidev); // enable bus mastering + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); + for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); + } + val = 1; // enable mic preamp + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val); + set_fs(fs); + + pci_set_drvdata(pcidev, s); + list_add(&s->list, &cs4281_devs); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: probe()- device allocated successfully\n")); + return 0; + + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_irq(s->irq, s); + err_irq: + iounmap(s->pBA1); + err_unmap: + iounmap(s->pBA0); + err_free: + kfree(s); + + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: probe()- no device allocated\n")); + return -ENODEV; +} // probe_cs4281 + + +// --------------------------------------------------------------------- + +static void __devexit cs4281_remove(struct pci_dev *pci_dev) +{ + struct cs4281_state *s = pci_get_drvdata(pci_dev); + // stop DMA controller + synchronize_irq(s->irq); + free_irq(s->irq, s); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + iounmap(s->pBA1); + iounmap(s->pBA0); + pci_set_drvdata(pci_dev,NULL); + list_del(&s->list); + kfree(s); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_remove()-: remove successful\n")); +} + +static struct pci_device_id cs4281_pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_CIRRUS, + .device = PCI_DEVICE_ID_CRYSTAL_CS4281, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl); + +static struct pci_driver cs4281_pci_driver = { + .name = "cs4281", + .id_table = cs4281_pci_tbl, + .probe = cs4281_probe, + .remove = __devexit_p(cs4281_remove), + .suspend = CS4281_SUSPEND_TBL, + .resume = CS4281_RESUME_TBL, +}; + +static int __init cs4281_init_module(void) +{ + int rtn = 0; + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_init_module()+ \n")); + printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " " + __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION, + CS4281_ARCH); + rtn = pci_register_driver(&cs4281_pci_driver); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn)); + return rtn; +} + +static void __exit cs4281_cleanup_module(void) +{ + pci_unregister_driver(&cs4281_pci_driver); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n")); +} +// --------------------------------------------------------------------- + +MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); +MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); +MODULE_LICENSE("GPL"); + +// --------------------------------------------------------------------- + +module_init(cs4281_init_module); +module_exit(cs4281_cleanup_module); + diff --git a/trunk/sound/oss/cs4281/cs4281pm-24.c b/trunk/sound/oss/cs4281/cs4281pm-24.c new file mode 100644 index 000000000000..90cbd7679534 --- /dev/null +++ b/trunk/sound/oss/cs4281/cs4281pm-24.c @@ -0,0 +1,45 @@ +/******************************************************************************* +* +* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ + +#ifndef NOT_CS4281_PM +#include + +static int cs4281_suspend(struct cs4281_state *s); +static int cs4281_resume(struct cs4281_state *s); +/* +* for now (12/22/00) only enable the pm_register PM support. +* allow these table entries to be null. +#define CS4281_SUSPEND_TBL cs4281_suspend_tbl +#define CS4281_RESUME_TBL cs4281_resume_tbl +*/ +#define CS4281_SUSPEND_TBL cs4281_suspend_null +#define CS4281_RESUME_TBL cs4281_resume_null + +#else /* CS4281_PM */ +#define CS4281_SUSPEND_TBL cs4281_suspend_null +#define CS4281_RESUME_TBL cs4281_resume_null +#endif /* CS4281_PM */ + diff --git a/trunk/sound/oss/cs4281/cs4281pm.h b/trunk/sound/oss/cs4281/cs4281pm.h new file mode 100644 index 000000000000..b44fdc9ce002 --- /dev/null +++ b/trunk/sound/oss/cs4281/cs4281pm.h @@ -0,0 +1,74 @@ +#ifndef NOT_CS4281_PM +/******************************************************************************* +* +* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +/* general pm definitions */ +#define CS4281_AC97_HIGHESTREGTORESTORE 0x26 +#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1) + +/* pipeline definitions */ +#define CS4281_NUMBER_OF_PIPELINES 4 +#define CS4281_PIPELINE_VALID 0x0001 +#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000 +#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001 + +/* PM state defintions */ +#define CS4281_PM_NOT_REGISTERED 0x1000 +#define CS4281_PM_IDLE 0x0001 +#define CS4281_PM_SUSPENDING 0x0002 +#define CS4281_PM_SUSPENDED 0x0004 +#define CS4281_PM_RESUMING 0x0008 +#define CS4281_PM_RESUMED 0x0010 + +struct cs4281_pm { + unsigned long flags; + u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; + u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; + u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; + u32 u32SSPM_BITS; + u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS]; + u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; + u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; + u32 u32hwptr_playback,u32hwptr_capture; +}; + +struct cs4281_pipeline { + unsigned flags; + unsigned number; + u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue; + u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress; + u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress; + u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save; + u32 u32DCCn_Save,u32DCAn_Save; +/* +* technically, these are fifo variables, but just map the +* first fifo with the first pipeline and then use the fifo +* variables inside of the pipeline struct. +*/ + u32 u32FCRn_Save,u32FSICn_Save; + u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress; + u32 u32FPDRnValue,u32FPDRnAddress; +}; +#endif diff --git a/trunk/sound/oss/dev_table.c b/trunk/sound/oss/dev_table.c index 08274c995d06..fb64279f3935 100644 --- a/trunk/sound/oss/dev_table.c +++ b/trunk/sound/oss/dev_table.c @@ -13,39 +13,9 @@ #include +#define _DEV_TABLE_C_ #include "sound_config.h" -struct audio_operations *audio_devs[MAX_AUDIO_DEV]; -EXPORT_SYMBOL(audio_devs); - -int num_audiodevs; -EXPORT_SYMBOL(num_audiodevs); - -struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; -EXPORT_SYMBOL(mixer_devs); - -int num_mixers; -EXPORT_SYMBOL(num_mixers); - -struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; -EXPORT_SYMBOL(synth_devs); - -int num_synths; - -struct midi_operations *midi_devs[MAX_MIDI_DEV]; -EXPORT_SYMBOL(midi_devs); - -int num_midis; -EXPORT_SYMBOL(num_midis); - -struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { - &default_sound_timer, NULL -}; -EXPORT_SYMBOL(sound_timer_devs); - -int num_sound_timers = 1; - - static int sound_alloc_audiodev(void); int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, @@ -105,7 +75,6 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, audio_init_devices(); return num; } -EXPORT_SYMBOL(sound_install_audiodrv); int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, int driver_size, void *devc) @@ -144,7 +113,6 @@ int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, mixer_devs[n] = op; return n; } -EXPORT_SYMBOL(sound_install_mixer); void sound_unload_audiodev(int dev) { @@ -154,7 +122,6 @@ void sound_unload_audiodev(int dev) unregister_sound_dsp((dev<<4)+3); } } -EXPORT_SYMBOL(sound_unload_audiodev); static int sound_alloc_audiodev(void) { @@ -177,7 +144,6 @@ int sound_alloc_mididev(void) num_midis = i + 1; return i; } -EXPORT_SYMBOL(sound_alloc_mididev); int sound_alloc_synthdev(void) { @@ -192,7 +158,6 @@ int sound_alloc_synthdev(void) } return -1; } -EXPORT_SYMBOL(sound_alloc_synthdev); int sound_alloc_mixerdev(void) { @@ -204,7 +169,6 @@ int sound_alloc_mixerdev(void) num_mixers = i + 1; return i; } -EXPORT_SYMBOL(sound_alloc_mixerdev); int sound_alloc_timerdev(void) { @@ -219,7 +183,6 @@ int sound_alloc_timerdev(void) } return -1; } -EXPORT_SYMBOL(sound_alloc_timerdev); void sound_unload_mixerdev(int dev) { @@ -229,7 +192,6 @@ void sound_unload_mixerdev(int dev) num_mixers--; } } -EXPORT_SYMBOL(sound_unload_mixerdev); void sound_unload_mididev(int dev) { @@ -238,19 +200,15 @@ void sound_unload_mididev(int dev) unregister_sound_midi((dev<<4)+2); } } -EXPORT_SYMBOL(sound_unload_mididev); void sound_unload_synthdev(int dev) { if (dev != -1) synth_devs[dev] = NULL; } -EXPORT_SYMBOL(sound_unload_synthdev); void sound_unload_timerdev(int dev) { if (dev != -1) sound_timer_devs[dev] = NULL; } -EXPORT_SYMBOL(sound_unload_timerdev); - diff --git a/trunk/sound/oss/dev_table.h b/trunk/sound/oss/dev_table.h index b7617bee6388..adf1d625b576 100644 --- a/trunk/sound/oss/dev_table.h +++ b/trunk/sound/oss/dev_table.h @@ -352,8 +352,22 @@ struct sound_timer_operations void (*arm_timer)(int dev, long time); }; -extern struct sound_timer_operations default_sound_timer; +#ifdef _DEV_TABLE_C_ +struct audio_operations *audio_devs[MAX_AUDIO_DEV]; +int num_audiodevs; +struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; +int num_mixers; +struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; +int num_synths; +struct midi_operations *midi_devs[MAX_MIDI_DEV]; +int num_midis; +extern struct sound_timer_operations default_sound_timer; +struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { + &default_sound_timer, NULL +}; +int num_sound_timers = 1; +#else extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs; extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; @@ -364,6 +378,7 @@ extern struct midi_operations *midi_devs[MAX_MIDI_DEV]; extern int num_midis; extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; extern int num_sound_timers; +#endif /* _DEV_TABLE_C_ */ extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); void sound_timer_init (struct sound_lowlev_timer *t, char *name); diff --git a/trunk/sound/oss/dm.h b/trunk/sound/oss/dm.h new file mode 100644 index 000000000000..14a90593c44f --- /dev/null +++ b/trunk/sound/oss/dm.h @@ -0,0 +1,79 @@ +#ifndef _DRIVERS_SOUND_DM_H +#define _DRIVERS_SOUND_DM_H + +/* + * Definitions of the 'direct midi sound' interface used + * by the newer commercial OSS package. We should export + * this to userland somewhere in glibc later. + */ + +/* + * Data structure composing an FM "note" or sound event. + */ + +struct dm_fm_voice +{ + u8 op; + u8 voice; + u8 am; + u8 vibrato; + u8 do_sustain; + u8 kbd_scale; + u8 harmonic; + u8 scale_level; + u8 volume; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + u8 feedback; + u8 connection; + u8 left; + u8 right; + u8 waveform; +}; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +struct dm_fm_note +{ + u8 voice; + u8 octave; + u32 fnum; + u8 key_on; +}; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +struct dm_fm_params +{ + u8 am_depth; + u8 vib_depth; + u8 kbd_split; + u8 rhythm; + + /* This block is the percussion instrument data */ + u8 bass; + u8 snare; + u8 tomtom; + u8 cymbal; + u8 hihat; +}; + +/* + * FM mode ioctl settings + */ + +#define FM_IOCTL_RESET 0x20 +#define FM_IOCTL_PLAY_NOTE 0x21 +#define FM_IOCTL_SET_VOICE 0x22 +#define FM_IOCTL_SET_PARAMS 0x23 +#define FM_IOCTL_SET_MODE 0x24 +#define FM_IOCTL_SET_OPL 0x25 + +#endif diff --git a/trunk/sound/oss/dmabuf.c b/trunk/sound/oss/dmabuf.c index b256c0401161..6c1cf74b78c5 100644 --- a/trunk/sound/oss/dmabuf.c +++ b/trunk/sound/oss/dmabuf.c @@ -926,7 +926,6 @@ int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) sound_start_dma(dmap, physaddr, count, dma_mode); return count; } -EXPORT_SYMBOL(DMAbuf_start_dma); static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) { @@ -1056,8 +1055,6 @@ void DMAbuf_outputintr(int dev, int notify_only) do_outputintr(dev, notify_only); spin_unlock_irqrestore(&dmap->lock,flags); } -EXPORT_SYMBOL(DMAbuf_outputintr); - /* called with dmap->lock held in irq context */ static void do_inputintr(int dev) { @@ -1157,7 +1154,36 @@ void DMAbuf_inputintr(int dev) do_inputintr(dev); spin_unlock_irqrestore(&dmap->lock,flags); } -EXPORT_SYMBOL(DMAbuf_inputintr); + +int DMAbuf_open_dma(int dev) +{ + /* + * NOTE! This routine opens only the primary DMA channel (output). + */ + struct audio_operations *adev = audio_devs[dev]; + int err; + + if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0) + return -EBUSY; + dma_init_buffers(adev->dmap_out); + adev->dmap_out->flags |= DMA_ALLOC_DONE; + adev->dmap_out->fragment_size = adev->dmap_out->buffsize; + + if (adev->dmap_out->dma >= 0) { + unsigned long flags; + + flags=claim_dma_lock(); + clear_dma_ff(adev->dmap_out->dma); + disable_dma(adev->dmap_out->dma); + release_dma_lock(flags); + } + return 0; +} + +void DMAbuf_close_dma(int dev) +{ + close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out); +} void DMAbuf_init(int dev, int dma1, int dma2) { @@ -1166,6 +1192,12 @@ void DMAbuf_init(int dev, int dma1, int dma2) * NOTE! This routine could be called several times. */ + /* drag in audio_syms.o */ + { + extern char audio_syms_symbol; + audio_syms_symbol = 0; + } + if (adev && adev->dmap_out == NULL) { if (adev->d == NULL) panic("OSS: audio_devs[%d]->d == NULL\n", dev); diff --git a/trunk/sound/oss/es1370.c b/trunk/sound/oss/es1370.c new file mode 100644 index 000000000000..13f483149737 --- /dev/null +++ b/trunk/sound/oss/es1370.c @@ -0,0 +1,2819 @@ +/*****************************************************************************/ + +/* + * es1370.c -- Ensoniq ES1370/Asahi Kasei AK4531 audio driver. + * + * Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * lineout if 1 the LINE jack is used as an output instead of an input. + * LINE then contains the unmixed dsp output. This can be used + * to make the card a four channel one: use dsp to output two + * channels to LINE and dac to output the other two channels to + * SPKR. Set the mixer to only output synth to SPKR. + * micbias sets the +5V bias to the mic if using an electretmic. + * + * + * Note: sync mode is not yet supported (i.e. running dsp and dac from the same + * clock source) + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but output only, + * only 5512, 11025, 22050 and 44100 samples/s, + * outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 26.03.1998 0.1 Initial release + * 31.03.1998 0.2 Fix bug in GETOSPACE + * 04.04.1998 0.3 Make it work (again) under 2.0.33 + * Fix mixer write operation not returning the actual + * settings + * 05.04.1998 0.4 First attempt at using the new PCI stuff + * 29.04.1998 0.5 Fix hang when ^C is pressed on amp + * 07.05.1998 0.6 Don't double lock around stop_*() in *_release() + * 10.05.1998 0.7 First stab at a simple midi interface (no bells&whistles) + * 14.05.1998 0.8 Don't allow excessive interrupt rates + * 08.06.1998 0.9 First release using Alan Cox' soundcore instead of + * miscdevice + * 05.07.1998 0.10 Fixed the driver to correctly maintin OSS style volume + * settings (not sure if this should be standard) + * Fixed many references: f_flags should be f_mode + * -- Gerald Britton + * 03.08.1998 0.11 Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * Fixed mixer table thanks to Hakan.Lennestal@lu.erisoft.se + * On module startup, set DAC2 to 11kSPS instead of 5.5kSPS, + * as it produces an annoying ssssh in the lower sampling rate + * Do not include modversions.h + * 22.08.1998 0.12 Mixer registers actually have 5 instead of 4 bits + * pointed out by Itai Nahshon + * 31.08.1998 0.13 Fix realplayer problems - dac.count issues + * 08.10.1998 0.14 Joystick support fixed + * -- Oliver Neukum + * 10.12.1998 0.15 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.16 Don't wake up app until there are fragsize bytes to read/write + * 06.01.1999 0.17 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.18 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.20 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: joystick address handling might still be wrong on archs + * other than i386 + * 10.05.1999 0.21 Added support for an electret mic for SB PCI64 + * to the Linux kernel sound driver. This mod also straighten + * out the question marks around the mic impedance setting + * (micz). From Kim.Berts@fisub.mail.abb.com + * 11.05.1999 0.22 Implemented the IMIX call to mute recording monitor. + * Guenter Geiger + * 15.06.1999 0.23 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.24 Add pci_set_master + * 02.08.1999 0.25 Added workaround for the "phantom write" bug first + * documented by Dave Sharpless from Anchor Games + * 03.08.1999 0.26 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1370=joystick[,lineout[,micbias]]" + * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge + * 12.08.1999 0.27 module_init/__setup fixes + * 19.08.1999 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca + * 31.08.1999 0.29 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.30 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.31 More waitqueue races fixed + * 08.01.2000 0.32 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * 07.02.2000 0.33 Use pci_alloc_consistent and pci_register_driver + * 21.11.2000 0.34 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.35 More dma buffer initializations, patch from + * Tjeerd Mulder + * 07.01.2001 0.36 Timeout change in wrcodec as requested by Frank Klemm + * 31.01.2001 0.37 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * 03.01.2003 0.38 open_mode fixes from Georg Acher + * + * some important things missing in Ensoniq documentation: + * + * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 + * and vary PCLKDIV to obtain zero beat. + * 5512sps: 254 + * 44100sps: 30 + * seems to be fs = 1411200/(PCLKDIV+2) + * + * should find out when curr_sample_ct is cleared and + * where exactly the CCB fetches data + * + * The card uses a 22.5792 MHz crystal. + * The LINEIN jack may be converted to an AOUT jack by + * setting pin 47 (XCTL0) of the ES1370 to high. + * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK +#endif + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif + +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#endif + +#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) + +#define ES1370_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 +#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c + +#define ES1370_FMT_U8_MONO 0 +#define ES1370_FMT_U8_STEREO 1 +#define ES1370_FMT_S16_MONO 2 +#define ES1370_FMT_S16_STEREO 3 +#define ES1370_FMT_STEREO 1 +#define ES1370_FMT_S16 2 +#define ES1370_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; + +#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) +#define DAC2_DIVTOSR(x) (1411200/((x)+2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* electret mic bias */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +/* --------------------------------------------------------------------- */ + +struct es1370_state { + /* magic */ + unsigned int magic; + + /* list of es1370 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned long io; /* long for SPARC */ + unsigned int irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + unsigned int imix; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + + spinlock_t lock; + struct mutex open_mutex; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* The following buffer is used to point the phantom write channel to. */ + unsigned char *bugbuf_cpu; + dma_addr_t bugbuf_dma; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + +#ifdef SUPPORT_JOYSTICK + struct gameport *gameport; +#endif + + struct mutex mutex; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) +{ + unsigned long tmo = jiffies + HZ/10, j; + + do { + j = jiffies; + if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { + outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); + return; + } + schedule(); + } while ((signed)(tmo-j) > 0); + printk(KERN_ERR "es1370: write to codec register timeout\n"); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac1(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac2(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct es1370_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + ClearPageReserved(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + SetPageReserved(page); + } + fmt &= ES1370_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(db->dmaaddr, s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct es1370_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); +} + +static inline int prog_dmabuf_dac2(struct es1370_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); +} + +static inline int prog_dmabuf_dac1(struct es1370_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); +} + +static inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1370_update_ptr(struct es1370_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_adc.error++; + } + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1370_handle_midi(struct es1370_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1370_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); +} + +static irqreturn_t es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1370_state *s = (struct es1370_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1370_REG_STATUS); + if (!(intsrc & 0x80000000)) + return IRQ_NONE; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + es1370_update_ptr(s); + es1370_handle_midi(s); + spin_unlock(&s->lock); + return IRQ_HANDLED; +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1370_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static const struct { + unsigned volidx:4; + unsigned left:4; + unsigned right:4; + unsigned stereo:1; + unsigned recmask:13; + unsigned avail:1; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ + [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ + [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ + [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ + [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ + [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ + [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 }, /* Mono1 */ + [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 }, /* Mono2 */ + [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 }, /* Mic */ + [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ +}; + +static void set_recsrc(struct es1370_state *s, unsigned int val) +{ + unsigned int i, j; + + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; + if (!s->mix.imix) { + i &= 0xff60; /* mute record and line monitor */ + } + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); +} + +static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + int __user *p = (int __user *)arg; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + if (get_user(val, p)) + return -EFAULT; + if (val != -1) { + s->mix.micpreamp = !!val; + wrcodec(s, 0x19, s->mix.micpreamp); + } + return put_user(s->mix.micpreamp, p); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query use of linein as second lineout */ + if (get_user(val, p)) + return -EFAULT; + if (val != -1) { + spin_lock_irqsave(&s->lock, flags); + if (val) + s->ctrl |= CTRL_XCTL0; + else + s->ctrl &= ~CTRL_XCTL0; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->ctrl & CTRL_XCTL0) ? 1 : 0, p); + } + if (cmd == SOUND_MIXER_PRIVATE3) { + /* enable/disable/query microphone impedance setting */ + if (get_user(val, p)) + return -EFAULT; + if (val != -1) { + spin_lock_irqsave(&s->lock, flags); + if (val) + s->ctrl |= CTRL_XCTL1; + else + s->ctrl &= ~CTRL_XCTL1; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->ctrl & CTRL_XCTL1) ? 1 : 0, p); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, p); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(s->mix.recsrc, p); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + val = SOUND_MASK_IMIX; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].avail) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].recmask) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].stereo) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_CAPS: + return put_user(0, p); + + case SOUND_MIXER_IMIX: + return put_user(s->mix.imix, p); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + return put_user(s->mix.vol[mixtable[i].volidx], p); + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_IMIX: + if (get_user(s->mix.imix, p)) + return -EFAULT; + set_recsrc(s, s->mix.recsrc); + return 0; + + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, p)) + return -EFAULT; + set_recsrc(s, val); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (mixtable[i].stereo) { + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 7) { + rl = 0x80; + l = 0; + } else { + rl = 31 - ((l - 7) / 3); + l = (31 - rl) * 3 + 7; + } + if (r < 7) { + rr = 0x80; + r = 0; + } else { + rr = 31 - ((r - 7) / 3); + r = (31 - rr) * 3 + 7; + } + wrcodec(s, mixtable[i].right, rr); + } else { + if (mixtable[i].left == 15) { + if (l < 2) { + rr = rl = 0x80; + r = l = 0; + } else { + rl = 7 - ((l - 2) / 14); + r = l = (7 - rl) * 14 + 2; + } + } else { + if (l < 7) { + rl = 0x80; + r = l = 0; + } else { + rl = 31 - ((l - 7) / 3); + r = l = (31 - rl) * 3 + 7; + } + } + } + wrcodec(s, mixtable[i].left, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[mixtable[i].volidx] = val; +#endif + return put_user(s->mix.vol[mixtable[i].volidx], p); + } +} + +/* --------------------------------------------------------------------- */ + +static int es1370_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return nonseekable_open(inode, file); +} + +static int es1370_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1370_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = es1370_ioctl_mixdev, + .open = es1370_open_mixdev, + .release = es1370_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1370_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped || !s->dma_dac1.ready) + return 0; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1370_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped || !s->dma_dac2.ready) + return 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + mutex_lock(&s->mutex); + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + mutex_unlock(&s->mutex); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + mutex_lock(&s->mutex); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } +out: + mutex_unlock(&s->mutex); + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t es1370_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac2.mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + mutex_lock(&s->mutex); + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + goto out; + ret = 0; + add_wait_queue(&s->dma_dac2.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac2.enabled) + start_dac2(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + mutex_unlock(&s->mutex); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + mutex_lock(&s->mutex); + if (s->dma_dac2.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac2.enabled) + start_dac2(s); + } +out: + mutex_unlock(&s->mutex); + remove_wait_queue(&s->dma_dac2.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac2.ready && prog_dmabuf_dac2(s)) + return 0; + poll_wait(file, &s->dma_dac2.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct dmabuf *db; + int ret = 0; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + mutex_lock(&s->mutex); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) { + goto out; + } + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) { + goto out; + } + db = &s->dma_adc; + } else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + db->mapped = 1; +out: + mutex_unlock(&s->mutex); + unlock_kernel(); + return ret; +} + +static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(s->irq); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; + if (val < 4000) + val = 4000; + if (val > 50000) + val = 50000; + stop_adc(s); + stop_dac2(s); + s->dma_adc.ready = s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + s->dma_dac2.enabled = 1; + start_dac2(s); + } else { + s->dma_dac2.enabled = 0; + stop_dac2(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac2.dmasize - count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + count = s->dma_dac2.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, p); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), p); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? + 2 : 1, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + 16 : 8, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->dma_dac2.enabled = 1; + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + mutex_unlock(&s->open_mutex); + mutex_init(&s->mutex); + return nonseekable_open(inode, file); +} + +static int es1370_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_mutex); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(s->irq); + dealloc_dmabuf(s, &s->dma_dac2); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = es1370_read, + .write = es1370_write, + .poll = es1370_poll, + .ioctl = es1370_ioctl, + .mmap = es1370_mmap, + .open = es1370_open, + .release = es1370_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_write_dac(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + add_wait_queue(&s->dma_dac1.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac1.enabled) + start_dac1(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac1.enabled) + start_dac1(s); + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (!s->dma_dac1.ready && prog_dmabuf_dac1(s)) + return 0; + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + lock_kernel(); + if ((ret = prog_dmabuf_dac1(s)) != 0) + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + goto out; + ret = -EAGAIN; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(s->dma_dac1.rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + s->dma_dac1.mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + unsigned ctrl; + int val, ret; + int __user *p = (int __user *)arg; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(s->irq); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + for (ctrl = 0; ctrl <= 2; ctrl++) + if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) + break; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + if (val != 0) { + if (s->dma_dac1.mapped) + return -EINVAL; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + s->dma_dac1.enabled = 1; + start_dac1(s); + } else { + s->dma_dac1.enabled = 0; + stop_dac1(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac1.dmasize - count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, p); + + case SNDCTL_DSP_GETOPTR: + if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + count = s->dma_dac1.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], p); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open_dac(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (!((s->dev_dac ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + s->dma_dac1.enabled = 1; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int es1370_release_dac(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + drain_dac1(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_mutex); + stop_dac1(s); + dealloc_dmabuf(s, &s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_dac_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = es1370_write_dac, + .poll = es1370_poll_dac, + .ioctl = es1370_ioctl_dac, + .mmap = es1370_mmap_dac, + .open = es1370_open_dac, + .release = es1370_release_dac, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t es1370_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t es1370_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + es1370_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int es1370_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct es1370_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct es1370_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int es1370_midi_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) + break; + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");) + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + mutex_lock(&s->open_mutex); + s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE)); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations es1370_midi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = es1370_midi_read, + .write = es1370_midi_write, + .poll = es1370_midi_poll, + .open = es1370_midi_open, + .release = es1370_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int lineout[NR_DEVICE]; +static int micbias[NR_DEVICE]; + +static unsigned int devindex; + +module_param_array(lineout, bool, NULL, 0); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +module_param_array(micbias, bool, NULL, 0); +MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __devinitdata = { + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_LINE3, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 } +}; + +#ifdef SUPPORT_JOYSTICK + +static int __devinit es1370_register_gameport(struct es1370_state *s) +{ + struct gameport *gp; + + if (!request_region(0x200, JOY_EXTENT, "es1370")) { + printk(KERN_ERR "es1370: joystick io port 0x200 in use\n"); + return -EBUSY; + } + + s->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR "es1370: can not allocate memory for gameport\n"); + release_region(0x200, JOY_EXTENT); + return -ENOMEM; + } + + gameport_set_name(gp, "ESS1370"); + gameport_set_phys(gp, "pci%s/gameport0", pci_name(s->dev)); + gp->dev.parent = &s->dev->dev; + gp->io = 0x200; + + s->ctrl |= CTRL_JYSTK_EN; + outl(s->ctrl, s->io + ES1370_REG_CONTROL); + + gameport_register_port(gp); + + return 0; +} + +static inline void es1370_unregister_gameport(struct es1370_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, JOY_EXTENT); + + } +} + +#else +static inline int es1370_register_gameport(struct es1370_state *s) { return -ENOSYS; } +static inline void es1370_unregister_gameport(struct es1370_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + +static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct es1370_state *s; + mm_segment_t fs; + int i, val, ret; + + if ((ret=pci_enable_device(pcidev))) + return ret; + + if ( !(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || + !pci_resource_start(pcidev, 0) + ) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + i = pci_set_dma_mask(pcidev, DMA_32BIT_MASK); + if (i) { + printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n"); + return i; + } + if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1370: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct es1370_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac1.wait); + init_waitqueue_head(&s->dma_dac2.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + mutex_init(&s->open_mutex); + spin_lock_init(&s->lock); + s->magic = ES1370_MAGIC; + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + if (!request_region(s->io, ES1370_EXTENT, "es1370")) { + printk(KERN_ERR "es1370: io ports %#lx-%#lx in use\n", s->io, s->io+ES1370_EXTENT-1); + ret = -EBUSY; + goto err_region; + } + if ((ret=request_irq(s->irq, es1370_interrupt, IRQF_SHARED, "es1370",s))) { + printk(KERN_ERR "es1370: irq %u in use\n", s->irq); + goto err_irq; + } + + /* initialize codec registers */ + /* note: setting CTRL_SERR_DIS is reported to break + * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */ + s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL); + if (lineout[devindex]) + s->ctrl |= CTRL_XCTL0; + if (micbias[devindex]) + s->ctrl |= CTRL_XCTL1; + s->sctrl = 0; + printk(KERN_INFO "es1370: adapter at io %#lx irq %u, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) { + ret = s->dev_dac; + goto err_dev3; + } + if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev4; + } + /* initialize the chips */ + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + /* point phantom write channel to "bugbuf" */ + s->bugbuf_cpu = pci_alloc_consistent(pcidev,16,&s->bugbuf_dma); + if (!s->bugbuf_cpu) { + ret = -ENOMEM; + goto err_dev5; + } + outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(s->bugbuf_dma, s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); + outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); + pci_set_master(pcidev); /* enable bus mastering */ + wrcodec(s, 0x16, 3); /* no RST, PD */ + wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ + wrcodec(s, 0x18, 0); /* recording source is mixer */ + wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */ + s->mix.imix = 1; + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + + es1370_register_gameport(s); + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev5: + unregister_sound_midi(s->dev_midi); + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1370: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1370_EXTENT); + err_region: + kfree(s); + return ret; +} + +static void __devexit es1370_remove(struct pci_dev *dev) +{ + struct es1370_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); + outl(CTRL_SERR_DIS | (1 << CTRL_SH_WTSRSEL), s->io+ES1370_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(s->irq); + free_irq(s->irq, s); + es1370_unregister_gameport(s); + release_region(s->io, ES1370_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + pci_free_consistent(dev, 16, s->bugbuf_cpu, s->bugbuf_dma); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] = { + { PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver es1370_driver = { + .name = "es1370", + .id_table = id_table, + .probe = es1370_probe, + .remove = __devexit_p(es1370_remove), +}; + +static int __init init_es1370(void) +{ + printk(KERN_INFO "es1370: version v0.38 time " __TIME__ " " __DATE__ "\n"); + return pci_register_driver(&es1370_driver); +} + +static void __exit cleanup_es1370(void) +{ + printk(KERN_INFO "es1370: unloading\n"); + pci_unregister_driver(&es1370_driver); +} + +module_init(init_es1370); +module_exit(cleanup_es1370); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: es1370=lineout[,micbias]] */ + +static int __init es1370_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + + (void) + ((get_option(&str,&lineout [nr_dev]) == 2) + && get_option(&str,&micbias [nr_dev]) + ); + + nr_dev++; + return 1; +} + +__setup("es1370=", es1370_setup); + +#endif /* MODULE */ diff --git a/trunk/sound/oss/esssolo1.c b/trunk/sound/oss/esssolo1.c new file mode 100644 index 000000000000..82f40a0a5c9c --- /dev/null +++ b/trunk/sound/oss/esssolo1.c @@ -0,0 +1,2516 @@ +/****************************************************************************/ + +/* + * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. + * + * Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Module command line parameters: + * none so far + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * Revision history + * 10.11.1998 0.1 Initial release (without any hardware) + * 22.03.1999 0.2 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.1999 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * 15.06.1999 0.4 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.5 Add pci_set_master + * 12.08.1999 0.6 Fix MIDI UART crashing the driver + * Changed mixer semantics from OSS documented + * behaviour to OSS "code behaviour". + * Recording might actually work now. + * The real DDMA controller address register is at PCI config + * 0x60, while the register at 0x18 is used as a placeholder + * register for BIOS address allocation. This register + * is supposed to be copied into 0x60, according + * to the Solo1 datasheet. When I do that, I can access + * the DDMA registers except the mask bit, which + * is stuck at 1. When I copy the contents of 0x18 +0x10 + * to the DDMA base register, everything seems to work. + * The fun part is that the Windows Solo1 driver doesn't + * seem to do these tricks. + * Bugs remaining: plops and clicks when starting/stopping playback + * 31.08.1999 0.7 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.8 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 07.10.1999 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer + * 28.10.1999 0.10 More waitqueue races fixed + * 09.12.1999 0.11 Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) + * Disabling recording on Alpha + * 12.01.2000 0.12 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * Integrated (aka redid 8-)) APM support patch by Zach Brown + * 07.02.2000 0.13 Use pci_alloc_consistent and pci_register_driver + * 19.02.2000 0.14 Use pci_dma_supported to determine if recording should be disabled + * 13.03.2000 0.15 Reintroduce initialization of a couple of PCI config space registers + * 21.11.2000 0.16 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.17 More dma buffer initializations, patch from + * Tjeerd Mulder + * 31.01.2001 0.18 Register/Unregister gameport, original patch from + * Nathaniel Daw + * Fix SETTRIGGER non OSS API conformity + * 10.03.2001 provide abs function, prevent picking up a bogus kernel macro + * for abs. Bug report by Andrew Morton + * 15.05.2001 pci_enable_device moved, return values in probe cleaned + * up. Marcus Meissner + * 22.05.2001 0.19 more cleanups, changed PM to PCI 2.4 style, got rid + * of global list of devices, using pci device data. + * Marcus Meissner + * 03.01.2003 0.20 open_mode fixes from Georg Acher + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_SOLO1 +#define PCI_DEVICE_ID_ESS_SOLO1 0x1969 +#endif + +#define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) + +#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ +#define DDMABASE_EXTENT 16 + +#define IOBASE_EXTENT 16 +#define SBBASE_EXTENT 16 +#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) +#define MPUBASE_EXTENT 4 +#define GPBASE_EXTENT 4 +#define GAMEPORT_EXTENT 4 + +#define FMSYNTH_EXTENT 4 + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + +static struct pci_driver solo1_driver; + +/* --------------------------------------------------------------------- */ + +struct solo1_state { + /* magic */ + unsigned int magic; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */ + unsigned int irq; + + /* mixer registers */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + /* wave stuff */ + unsigned fmt; + unsigned channels; + unsigned rate; + unsigned char clkdiv; + unsigned ena; + + spinlock_t lock; + struct mutex open_mutex; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + +#if SUPPORT_JOYSTICK + struct gameport *gameport; +#endif +}; + +/* --------------------------------------------------------------------- */ + +static inline void write_seq(struct solo1_state *s, unsigned char data) +{ + int i; + unsigned long flags; + + /* the local_irq_save stunt is to send the data within the command window */ + for (i = 0; i < 0xffff; i++) { + local_irq_save(flags); + if (!(inb(s->sbbase+0xc) & 0x80)) { + outb(data, s->sbbase+0xc); + local_irq_restore(flags); + return; + } + local_irq_restore(flags); + } + printk(KERN_ERR "esssolo1: write_seq timeout\n"); + outb(data, s->sbbase+0xc); +} + +static inline int read_seq(struct solo1_state *s, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) { + *data = inb(s->sbbase+0xa); + return 1; + } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); + return 0; +} + +static inline int reset_ctrl(struct solo1_state *s) +{ + int i; + + outb(3, s->sbbase+6); /* clear sequencer and FIFO */ + udelay(10); + outb(0, s->sbbase+6); + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) + if (inb(s->sbbase+0xa) == 0xaa) { + write_seq(s, 0xc6); /* enter enhanced mode */ + return 1; + } + return 0; +} + +static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + write_seq(s, reg); + write_seq(s, data); +} + +#if 0 /* unused */ +static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) +{ + unsigned char r; + + write_seq(s, 0xc0); + write_seq(s, reg); + read_seq(s, &r); + return r; +} +#endif /* unused */ + +static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + outb(reg, s->sbbase+4); + outb(data, s->sbbase+5); +} + +static unsigned char read_mixer(struct solo1_state *s, unsigned char reg) +{ + outb(reg, s->sbbase+4); + return inb(s->sbbase+5); +} + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x10); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->ena |= FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + udelay(10); + write_mixer(s, 0x78, 0x13); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ena |= FMODE_READ; + write_ctrl(s, 0xb8, 0xf); +#if 0 + printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); + printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", + inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); +#endif + outb(0, s->ddmabase+0xd); /* master reset */ + outb(1, s->ddmabase+0xf); /* mask */ + outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + outb(0, s->ddmabase+0xf); + } + spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" + KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->sbbase+0xc), + inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); + printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), + read_ctrl(s, 0xb9)); +#endif +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static inline void dealloc_dmabuf(struct solo1_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + ClearPageReserved(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec; + unsigned bufs, sample_shift = 0; + struct page *page, *pend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + SetPageReserved(page); + } + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->channels > 1) + sample_shift++; + bytespersec = s->rate << sample_shift; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytespersec) + db->fragshift = ld2(bytespersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift; + db->dmasize = db->numfrag << db->fragshift; + db->enabled = 1; + return 0; +} + +static inline int prog_dmabuf_adc(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_adc(s); + /* check if PCI implementation supports 24bit busmaster DMA */ + if (s->dev->dma_mask > 0xffffff) + return -EIO; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + va = s->dma_adc.dmaaddr; + if ((va & ~((1<<24)-1))) + panic("solo1: buffer above 16M boundary"); + outb(0, s->ddmabase+0xd); /* clear */ + outb(1, s->ddmabase+0xf); /* mask */ + /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ + outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(va, s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + c = - s->dma_adc.fragsamples; + write_ctrl(s, 0xa4, c); + write_ctrl(s, 0xa5, c >> 8); + outb(0, s->ddmabase+0xf); + s->dma_adc.ready = 1; + return 0; +} + +static int prog_dmabuf_dac(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_dac(s); + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ + va = s->dma_dac.dmaaddr; + if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) + panic("solo1: buffer crosses 1M boundary"); + outl(va, s->iobase); + /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ + outw(s->dma_dac.dmasize, s->iobase+4); + c = - s->dma_dac.fragsamples; + write_mixer(s, 0x74, c); + write_mixer(s, 0x76, c >> 8); + outb(0xa, s->iobase+6); + s->dma_dac.ready = 1; + return 0; +} + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ + +static void solo1_update_ptr(struct solo1_state *s) +{ + int diff; + unsigned hwptr; + + /* update ADC pointer */ + if (s->ena & FMODE_READ) { + hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; +#if 0 + printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); +#endif + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC pointer */ + if (s->ena & FMODE_WRITE) { + hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; +#if 0 + printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count); +#endif + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, + s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* --------------------------------------------------------------------- */ + +static void prog_codec(struct solo1_state *s) +{ + unsigned long flags; + int fdiv, filter; + unsigned char c; + + reset_ctrl(s); + write_seq(s, 0xd3); + /* program sampling rates */ + filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ + fdiv = 256 - 7160000 / (filter * 82); + spin_lock_irqsave(&s->lock, flags); + write_ctrl(s, 0xa1, s->clkdiv); + write_ctrl(s, 0xa2, fdiv); + write_mixer(s, 0x70, s->clkdiv); + write_mixer(s, 0x72, fdiv); + /* program ADC parameters */ + write_ctrl(s, 0xb8, 0xe); + write_ctrl(s, 0xb9, /*0x1*/0); + write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); + c = 0xd0; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 0x04; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 0x20; + if (s->channels > 1) + c ^= 0x48; + write_ctrl(s, 0xb7, (c & 0x70) | 1); + write_ctrl(s, 0xb7, c); + write_ctrl(s, 0xb1, 0x50); + write_ctrl(s, 0xb2, 0x50); + /* program DAC parameters */ + c = 0x40; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 1; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 4; + if (s->channels > 1) + c |= 2; + write_mixer(s, 0x7a, c); + write_mixer(s, 0x78, 0x10); + s->ena = 0; + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SOLO1_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg) +{ + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, + SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 + }; + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, /* voice */ + [SOUND_MIXER_SYNTH] = 2, /* FM */ + [SOUND_MIXER_CD] = 3, /* CD */ + [SOUND_MIXER_LINE] = 4, /* Line */ + [SOUND_MIXER_LINE1] = 5, /* AUX */ + [SOUND_MIXER_MIC] = 6, /* Mic */ + [SOUND_MIXER_LINE2] = 7, /* Mono in */ + [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ + [SOUND_MIXER_RECLEV] = 9, /* Recording level */ + [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ + }; + static const unsigned char mixreg[] = { + 0x7c, /* voice */ + 0x36, /* FM */ + 0x38, /* CD */ + 0x3e, /* Line */ + 0x3a, /* AUX */ + 0x1a, /* Mic */ + 0x6d /* Mono in */ + }; + unsigned char l, r, rl, rr, vidx; + int i, val; + int __user *p = (int __user *)arg; + + VALIDATE_STATE(s); + + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + if (get_user(val, p)) + return -EFAULT; + if (val != -1) { + val = val ? 0xff : 0xf7; + write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); + } + val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; + return put_user(val, p); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query spatializer */ + if (get_user(val, p)) + return -EFAULT; + if (val != -1) { + val &= 0x3f; + write_mixer(s, 0x52, val); + write_mixer(s, 0x50, val ? 0x08 : 0); + } + return put_user(read_mixer(s, 0x52), p); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, p); + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_src[read_mixer(s, 0x1c) & 7], p); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, p); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, p); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV, p); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, p); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx-1], p); + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ +#if 0 + { + static const unsigned char regs[] = { + 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c + }; + int i; + + for (i = 0; i < sizeof(regs); i++) + printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", + regs[i], read_mixer(s, regs[i])); + printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", + 0xb4, read_ctrl(s, 0xb4)); + } +#endif + if (get_user(val, p)) + return -EFAULT; + i = hweight32(val); + if (i == 0) + return 0; + else if (i > 1) + val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; + for (i = 0; i < 8; i++) { + if (mixer_src[i] & val) + break; + } + if (i > 7) + return 0; + write_mixer(s, 0x1c, i); + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, p)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 6) { + rl = 0x40; + l = 0; + } else { + rl = (l * 2 - 11) / 3; + l = (rl * 3 + 11) / 2; + } + if (r < 6) { + rr = 0x40; + r = 0; + } else { + rr = (r * 2 - 11) / 3; + r = (rr * 3 + 11) / 2; + } + write_mixer(s, 0x60, rl); + write_mixer(s, 0x62, rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[9] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[9] = val; +#endif + return put_user(s->mix.vol[9], p); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, p)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + else if (l < 2) + l = 2; + rl = (l - 2) / 14; + l = rl * 14 + 2; + write_mixer(s, 0x3c, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = l * 0x101; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], p); + + case SOUND_MIXER_RECLEV: + if (get_user(val, p)) + return -EFAULT; + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_ctrl(s, 0xb4, (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], p); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx-1] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[vidx-1] = val; +#endif + return put_user(s->mix.vol[vidx-1], p); + } +} + +/* --------------------------------------------------------------------- */ + +static int solo1_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev = NULL; + + while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { + struct pci_driver *drvr; + drvr = pci_dev_driver (pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_mixer == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + return nonseekable_open(inode, file); +} + +static int solo1_release_mixdev(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations solo1_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = solo1_ioctl_mixdev, + .open = solo1_open_mixdev, + .release = solo1_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct solo1_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "solo1: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); +#endif + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); +#endif + } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t solo1_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; +#if 0 + printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x 71: 0x%02x 72: 0x%02x 74: 0x%02x 76: 0x%02x 78: 0x%02x 7A: 0x%02x\n" + KERN_DEBUG "solo1_write: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76), + read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); + printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x reg 7A: 0x%02x DMAcnt: 0x%04x DMAstat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); +#endif + ret = 0; + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac.enabled) + start_dac(s); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize > s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int solo1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret, count; + int div1, div2; + unsigned rate1, rate2; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + prog_codec(s); + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program sampling rates */ + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + div1 = (768000 + val / 2) / val; + rate1 = (768000 + div1 / 2) / div1; + div1 = -div1; + div2 = (793800 + val / 2) / val; + rate2 = (793800 + div2 / 2) / div2; + div2 = (-div2) & 0x7f; + if (abs(val - rate2) < abs(val - rate1)) { + rate1 = rate2; + div1 = div2; + } + s->rate = rate1; + s->clkdiv = div1; + prog_codec(s); + } + return put_user(s->rate, p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = val ? 2 : 1; + prog_codec(s); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + if (val != 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = (val >= 2) ? 2 : 1; + prog_codec(s); + } + return put_user(s->channels, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program format */ + if (val != AFMT_S16_LE && val != AFMT_U16_LE && + val != AFMT_S8 && val != AFMT_U8) + val = AFMT_U8; + s->fmt = val; + prog_codec(s); + } + return put_user(s->fmt, p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_dac.enabled = 1; + start_adc(s); + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + } else { + s->dma_dac.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "esssolo1: GETOPTR: bytes %u blocks %u ptr %u, buforder %u numfrag %u fragshift %u\n" + KERN_DEBUG "esssolo1: swptr %u count %u fragsize %u dmasize %u fragsamples %u\n", + cinfo.bytes, cinfo.blocks, cinfo.ptr, s->dma_dac.buforder, s->dma_dac.numfrag, s->dma_dac.fragshift, + s->dma_dac.swptr, s->dma_dac.count, s->dma_dac.fragsize, s->dma_dac.dmasize, s->dma_dac.fragsamples); +#endif + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->rate, p); + + case SOUND_PCM_READ_CHANNELS: + return put_user(s->channels, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & (AFMT_S8|AFMT_U8)) ? 8 : 16, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int solo1_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_mutex); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + outb(0, s->iobase+6); /* disable DMA */ + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + outb(1, s->ddmabase+0xf); /* mask DMA channel */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ~(FMODE_READ | FMODE_WRITE); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static int solo1_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev = NULL; + + while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + s->fmt = AFMT_U8; + s->channels = 1; + s->rate = 8000; + s->clkdiv = 96 | 0x80; + s->ena = 0; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + mutex_unlock(&s->open_mutex); + prog_codec(s); + return nonseekable_open(inode, file); +} + +static /*const*/ struct file_operations solo1_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = solo1_read, + .write = solo1_write, + .poll = solo1_poll, + .ioctl = solo1_ioctl, + .mmap = solo1_mmap, + .open = solo1_open, + .release = solo1_release, +}; + +/* --------------------------------------------------------------------- */ + +/* hold spinlock for the following! */ +static void solo1_handle_midi(struct solo1_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->mpubase)) + return; + wake = 0; + while (!(inb(s->mpubase+1) & 0x80)) { + ch = inb(s->mpubase); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->mpubase); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static irqreturn_t solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct solo1_state *s = (struct solo1_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->iobase+7); /* get interrupt source(s) */ + if (!intsrc) + return IRQ_NONE; + (void)inb(s->sbbase+0xe); /* clear interrupt */ + spin_lock(&s->lock); + /* clear audio interrupts first */ + if (intsrc & 0x20) + write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); + solo1_update_ptr(s); + solo1_handle_midi(s); + spin_unlock(&s->lock); + return IRQ_HANDLED; +} + +static void solo1_midi_timer(unsigned long data) +{ + struct solo1_state *s = (struct solo1_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t solo1_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + solo1_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int solo1_midi_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct solo1_state *s = NULL; + struct pci_dev *pci_dev = NULL; + + while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_midi == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(0xff, s->mpubase+1); /* reset command */ + outb(0x3f, s->mpubase+1); /* uart command */ + if (!(inb(s->mpubase+1) & 0x80)) + inb(s->mpubase); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = solo1_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int solo1_midi_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) + break; + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "solo1: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + mutex_lock(&s->open_mutex); + s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE)); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations solo1_midi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = solo1_midi_read, + .write = solo1_midi_write, + .poll = solo1_midi_poll, + .open = solo1_midi_open, + .release = solo1_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void __user *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void __user *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->sbbase + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void __user *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->sbbase); + outb((p.kbd_split & 1) << 6, s->sbbase+1); + outb(0xbd, s->sbbase); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->sbbase+2); + outb(arg, s->sbbase+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->sbbase+2); + outb(arg & 1, s->sbbase+3); + return 0; + + default: + return -EINVAL; + } +} + +static int solo1_dmfm_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + struct solo1_state *s = NULL; + struct pci_dev *pci_dev = NULL; + + while ((pci_dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { + struct pci_driver *drvr; + + drvr = pci_dev_driver(pci_dev); + if (drvr != &solo1_driver) + continue; + s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + continue; + if (s->dev_dmfm == minor) + break; + } + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + if (!request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1")) { + mutex_unlock(&s->open_mutex); + printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); + return -EBUSY; + } + /* init the stuff */ + outb(1, s->sbbase); + outb(0x20, s->sbbase+1); /* enable waveforms */ + outb(4, s->sbbase+2); + outb(0, s->sbbase+3); /* no 4op enabled */ + outb(5, s->sbbase+2); + outb(1, s->sbbase+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int solo1_dmfm_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + mutex_lock(&s->open_mutex); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + release_region(s->sbbase, FMSYNTH_EXTENT); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations solo1_dmfm_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = solo1_dmfm_ioctl, + .open = solo1_dmfm_open, + .release = solo1_dmfm_release, +}; + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __devinitdata = { + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 } +}; + +static int setup_solo1(struct solo1_state *s) +{ + struct pci_dev *pcidev = s->dev; + mm_segment_t fs; + int i, val; + + /* initialize DDMA base address */ + printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); + pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); + /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ + pci_write_config_dword(pcidev, 0x50, 0); + /* disable legacy audio address decode */ + pci_write_config_word(pcidev, 0x40, 0x907f); + + /* initialize the chips */ + if (!reset_ctrl(s)) { + printk(KERN_ERR "esssolo1: cannot reset controller\n"); + return -1; + } + outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ + + /* initialize mixer regs */ + write_mixer(s, 0x7f, 0); /* disable music digital recording */ + write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */ + write_mixer(s, 0x64, 0x45); /* volume control */ + write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */ + write_mixer(s, 0x50, 0); /* disable spatializer */ + write_mixer(s, 0x52, 0); + write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ + write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(1, s->ddmabase+0xf); /* mask channel */ + /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ + + pci_set_master(pcidev); /* enable bus mastering */ + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + val = 1; /* enable mic preamp */ + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); + set_fs(fs); + return 0; +} + +static int +solo1_suspend(struct pci_dev *pci_dev, pm_message_t state) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return 1; + outb(0, s->iobase+6); + /* DMA master clear */ + outb(0, s->ddmabase+0xd); + /* reset sequencer and FIFO */ + outb(3, s->sbbase+6); + /* turn off DDMA controller address space */ + pci_write_config_word(s->dev, 0x60, 0); + return 0; +} + +static int +solo1_resume(struct pci_dev *pci_dev) { + struct solo1_state *s = (struct solo1_state*)pci_get_drvdata(pci_dev); + if (!s) + return 1; + setup_solo1(s); + return 0; +} + +#ifdef SUPPORT_JOYSTICK +static int __devinit solo1_register_gameport(struct solo1_state *s, int io_port) +{ + struct gameport *gp; + + if (!request_region(io_port, GAMEPORT_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: gameport io ports are in use\n"); + return -EBUSY; + } + + s->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR "solo1: can not allocate memory for gameport\n"); + release_region(io_port, GAMEPORT_EXTENT); + return -ENOMEM; + } + + gameport_set_name(gp, "ESS Solo1 Gameport"); + gameport_set_phys(gp, "isa%04x/gameport0", io_port); + gp->dev.parent = &s->dev->dev; + gp->io = io_port; + + gameport_register_port(gp); + + return 0; +} + +static inline void solo1_unregister_gameport(struct solo1_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, GAMEPORT_EXTENT); + } +} +#else +static inline int solo1_register_gameport(struct solo1_state *s, int io_port) { return -ENOSYS; } +static inline void solo1_unregister_gameport(struct solo1_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + +static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + struct solo1_state *s; + int gpio; + int ret; + + if ((ret=pci_enable_device(pcidev))) + return ret; + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 2) & IORESOURCE_IO) || + !(pci_resource_flags(pcidev, 3) & IORESOURCE_IO)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + + /* Recording requires 24-bit DMA, so attempt to set dma mask + * to 24 bits first, then 32 bits (playback only) if that fails. + */ + if (pci_set_dma_mask(pcidev, DMA_24BIT_MASK) && + pci_set_dma_mask(pcidev, DMA_32BIT_MASK)) { + printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) { + printk(KERN_WARNING "solo1: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct solo1_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + mutex_init(&s->open_mutex); + spin_lock_init(&s->lock); + s->magic = SOLO1_MAGIC; + s->dev = pcidev; + s->iobase = pci_resource_start(pcidev, 0); + s->sbbase = pci_resource_start(pcidev, 1); + s->vcbase = pci_resource_start(pcidev, 2); + s->ddmabase = s->vcbase + DDMABASE_OFFSET; + s->mpubase = pci_resource_start(pcidev, 3); + gpio = pci_resource_start(pcidev, 4); + s->irq = pcidev->irq; + ret = -EBUSY; + if (!request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region1; + } + if (!request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region2; + } + if (!request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region3; + } + if (!request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1")) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region4; + } + if ((ret=request_irq(s->irq,solo1_interrupt,IRQF_SHARED,"ESS Solo1",s))) { + printk(KERN_ERR "solo1: irq %u in use\n", s->irq); + goto err_irq; + } + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev3; + } + if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; + goto err_dev4; + } + if (setup_solo1(s)) { + ret = -EIO; + goto err; + } + /* register gameport */ + solo1_register_gameport(s, gpio); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + return 0; + + err: + unregister_sound_special(s->dev_dmfm); + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "solo1: initialisation error\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->mpubase, MPUBASE_EXTENT); + err_region4: + release_region(s->ddmabase, DDMABASE_EXTENT); + err_region3: + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); + err_region2: + release_region(s->iobase, IOBASE_EXTENT); + err_region1: + kfree(s); + return ret; +} + +static void __devexit solo1_remove(struct pci_dev *dev) +{ + struct solo1_state *s = pci_get_drvdata(dev); + + if (!s) + return; + /* stop DMA controller */ + outb(0, s->iobase+6); + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(3, s->sbbase+6); /* reset sequencer and FIFO */ + synchronize_irq(s->irq); + pci_write_config_word(s->dev, 0x60, 0); /* turn off DDMA controller address space */ + free_irq(s->irq, s); + solo1_unregister_gameport(s); + release_region(s->iobase, IOBASE_EXTENT); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); + release_region(s->mpubase, MPUBASE_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] = { + { PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver solo1_driver = { + .name = "ESS Solo1", + .id_table = id_table, + .probe = solo1_probe, + .remove = __devexit_p(solo1_remove), + .suspend = solo1_suspend, + .resume = solo1_resume, +}; + + +static int __init init_solo1(void) +{ + printk(KERN_INFO "solo1: version v0.20 time " __TIME__ " " __DATE__ "\n"); + return pci_register_driver(&solo1_driver); +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ESS Solo1 Driver"); +MODULE_LICENSE("GPL"); + + +static void __exit cleanup_solo1(void) +{ + printk(KERN_INFO "solo1: unloading\n"); + pci_unregister_driver(&solo1_driver); +} + +/* --------------------------------------------------------------------- */ + +module_init(init_solo1); +module_exit(cleanup_solo1); + diff --git a/trunk/sound/oss/forte.c b/trunk/sound/oss/forte.c new file mode 100644 index 000000000000..6c910498924a --- /dev/null +++ b/trunk/sound/oss/forte.c @@ -0,0 +1,2138 @@ +/* + * forte.c - ForteMedia FM801 OSS Driver + * + * Written by Martin K. Petersen + * Copyright (C) 2002 Hewlett-Packard Company + * Portions Copyright (C) 2003 Martin K. Petersen + * + * Latest version: http://mkp.net/forte/ + * + * Based upon the ALSA FM801 driver by Jaroslav Kysela and OSS drivers + * by Thomas Sailer, Alan Cox, Zach Brown, and Jeff Garzik. Thanks + * guys! + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#define DRIVER_NAME "forte" +#define DRIVER_VERSION "$Id: forte.c,v 1.63 2003/03/01 05:32:42 mkp Exp $" +#define PFX DRIVER_NAME ": " + +#undef M_DEBUG + +#ifdef M_DEBUG +#define DPRINTK(args...) printk(KERN_WARNING args) +#else +#define DPRINTK(args...) +#endif + +/* Card capabilities */ +#define FORTE_CAPS (DSP_CAP_MMAP | DSP_CAP_TRIGGER) + +/* Supported audio formats */ +#define FORTE_FMTS (AFMT_U8 | AFMT_S16_LE) + +/* Buffers */ +#define FORTE_MIN_FRAG_SIZE 256 +#define FORTE_MAX_FRAG_SIZE PAGE_SIZE +#define FORTE_DEF_FRAG_SIZE 256 +#define FORTE_MIN_FRAGMENTS 2 +#define FORTE_MAX_FRAGMENTS 256 +#define FORTE_DEF_FRAGMENTS 2 +#define FORTE_MIN_BUF_MSECS 500 +#define FORTE_MAX_BUF_MSECS 1000 + +/* PCI BARs */ +#define FORTE_PCM_VOL 0x00 /* PCM Output Volume */ +#define FORTE_FM_VOL 0x02 /* FM Output Volume */ +#define FORTE_I2S_VOL 0x04 /* I2S Volume */ +#define FORTE_REC_SRC 0x06 /* Record Source */ +#define FORTE_PLY_CTRL 0x08 /* Playback Control */ +#define FORTE_PLY_COUNT 0x0a /* Playback Count */ +#define FORTE_PLY_BUF1 0x0c /* Playback Buffer I */ +#define FORTE_PLY_BUF2 0x10 /* Playback Buffer II */ +#define FORTE_CAP_CTRL 0x14 /* Capture Control */ +#define FORTE_CAP_COUNT 0x16 /* Capture Count */ +#define FORTE_CAP_BUF1 0x18 /* Capture Buffer I */ +#define FORTE_CAP_BUF2 0x1c /* Capture Buffer II */ +#define FORTE_CODEC_CTRL 0x22 /* Codec Control */ +#define FORTE_I2S_MODE 0x24 /* I2S Mode Control */ +#define FORTE_VOLUME 0x26 /* Volume Up/Down/Mute Status */ +#define FORTE_I2C_CTRL 0x29 /* I2C Control */ +#define FORTE_AC97_CMD 0x2a /* AC'97 Command */ +#define FORTE_AC97_DATA 0x2c /* AC'97 Data */ +#define FORTE_MPU401_DATA 0x30 /* MPU401 Data */ +#define FORTE_MPU401_CMD 0x31 /* MPU401 Command */ +#define FORTE_GPIO_CTRL 0x52 /* General Purpose I/O Control */ +#define FORTE_GEN_CTRL 0x54 /* General Control */ +#define FORTE_IRQ_MASK 0x56 /* Interrupt Mask */ +#define FORTE_IRQ_STATUS 0x5a /* Interrupt Status */ +#define FORTE_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ +#define FORTE_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ +#define FORTE_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ +#define FORTE_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ +#define FORTE_POWERDOWN 0x70 /* Blocks Power Down Control */ + +#define FORTE_CAP_OFFSET FORTE_CAP_CTRL - FORTE_PLY_CTRL + +#define FORTE_AC97_ADDR_SHIFT 10 + +/* Playback and record control register bits */ +#define FORTE_BUF1_LAST (1<<1) +#define FORTE_BUF2_LAST (1<<2) +#define FORTE_START (1<<5) +#define FORTE_PAUSE (1<<6) +#define FORTE_IMMED_STOP (1<<7) +#define FORTE_RATE_SHIFT 8 +#define FORTE_RATE_MASK (15 << FORTE_RATE_SHIFT) +#define FORTE_CHANNELS_4 (1<<12) /* Playback only */ +#define FORTE_CHANNELS_6 (2<<12) /* Playback only */ +#define FORTE_CHANNELS_6MS (3<<12) /* Playback only */ +#define FORTE_CHANNELS_MASK (3<<12) +#define FORTE_16BIT (1<<14) +#define FORTE_STEREO (1<<15) + +/* IRQ status bits */ +#define FORTE_IRQ_PLAYBACK (1<<8) +#define FORTE_IRQ_CAPTURE (1<<9) +#define FORTE_IRQ_VOLUME (1<<14) +#define FORTE_IRQ_MPU (1<<15) + +/* CODEC control */ +#define FORTE_CC_CODEC_RESET (1<<5) +#define FORTE_CC_AC97_RESET (1<<6) + +/* AC97 cmd */ +#define FORTE_AC97_WRITE (0<<7) +#define FORTE_AC97_READ (1<<7) +#define FORTE_AC97_DP_INVALID (0<<8) +#define FORTE_AC97_DP_VALID (1<<8) +#define FORTE_AC97_PORT_RDY (0<<9) +#define FORTE_AC97_PORT_BSY (1<<9) + + +struct forte_channel { + const char *name; + + unsigned short ctrl; /* Ctrl BAR contents */ + unsigned long iobase; /* Ctrl BAR address */ + + wait_queue_head_t wait; + + void *buf; /* Buffer */ + dma_addr_t buf_handle; /* Buffer handle */ + + unsigned int record; + unsigned int format; + unsigned int rate; + unsigned int stereo; + + unsigned int frag_sz; /* Current fragment size */ + unsigned int frag_num; /* Current # of fragments */ + unsigned int frag_msecs; /* Milliseconds per frag */ + unsigned int buf_sz; /* Current buffer size */ + + unsigned int hwptr; /* Tail */ + unsigned int swptr; /* Head */ + unsigned int filled_frags; /* Fragments currently full */ + unsigned int next_buf; /* Index of next buffer */ + + unsigned int active; /* Channel currently in use */ + unsigned int mapped; /* mmap */ + + unsigned int buf_pages; /* Real size of buffer */ + unsigned int nr_irqs; /* Number of interrupts */ + unsigned int bytes; /* Total bytes */ + unsigned int residue; /* Partial fragment */ +}; + + +struct forte_chip { + struct pci_dev *pci_dev; + unsigned long iobase; + int irq; + + struct mutex open_mutex; /* Device access */ + spinlock_t lock; /* State */ + + spinlock_t ac97_lock; + struct ac97_codec *ac97; + + int multichannel; + int dsp; /* OSS handle */ + int trigger; /* mmap I/O trigger */ + + struct forte_channel play; + struct forte_channel rec; +}; + + +static int channels[] = { 2, 4, 6, }; +static int rates[] = { 5500, 8000, 9600, 11025, 16000, 19200, + 22050, 32000, 38400, 44100, 48000, }; + +static struct forte_chip *forte; +static int found; + + +/* AC97 Codec -------------------------------------------------------------- */ + + +/** + * forte_ac97_wait: + * @chip: fm801 instance whose AC97 codec to wait on + * + * FIXME: + * Stop busy-waiting + */ + +static inline int +forte_ac97_wait (struct forte_chip *chip) +{ + int i = 10000; + + while ( (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_PORT_BSY) + && i-- ) + cpu_relax(); + + return i == 0; +} + + +/** + * forte_ac97_read: + * @codec: AC97 codec to read from + * @reg: register to read + */ + +static u16 +forte_ac97_read (struct ac97_codec *codec, u8 reg) +{ + u16 ret = 0; + struct forte_chip *chip = codec->private_data; + + spin_lock (&chip->ac97_lock); + + /* Knock, knock */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_read: Serial bus busy\n"); + goto out; + } + + /* Send read command */ + outw (reg | (1<<7), chip->iobase + FORTE_AC97_CMD); + + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_read: Bus busy reading reg 0x%x\n", + reg); + goto out; + } + + /* Sanity checking */ + if (inw (chip->iobase + FORTE_AC97_CMD) & FORTE_AC97_DP_INVALID) { + printk (KERN_ERR PFX "ac97_read: Invalid data port"); + goto out; + } + + /* Fetch result */ + ret = inw (chip->iobase + FORTE_AC97_DATA); + + out: + spin_unlock (&chip->ac97_lock); + return ret; +} + + +/** + * forte_ac97_write: + * @codec: AC97 codec to send command to + * @reg: register to write + * @val: value to write + */ + +static void +forte_ac97_write (struct ac97_codec *codec, u8 reg, u16 val) +{ + struct forte_chip *chip = codec->private_data; + + spin_lock (&chip->ac97_lock); + + /* Knock, knock */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_write: Serial bus busy\n"); + goto out; + } + + outw (val, chip->iobase + FORTE_AC97_DATA); + outb (reg | FORTE_AC97_WRITE, chip->iobase + FORTE_AC97_CMD); + + /* Wait for completion */ + if (forte_ac97_wait (chip)) { + printk (KERN_ERR PFX "ac97_write: Bus busy after write\n"); + goto out; + } + + out: + spin_unlock (&chip->ac97_lock); +} + + +/* Mixer ------------------------------------------------------------------- */ + + +/** + * forte_mixer_open: + * @inode: + * @file: + */ + +static int +forte_mixer_open (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = forte; + file->private_data = chip->ac97; + return 0; +} + + +/** + * forte_mixer_release: + * @inode: + * @file: + */ + +static int +forte_mixer_release (struct inode *inode, struct file *file) +{ + /* We will welease Wodewick */ + return 0; +} + + +/** + * forte_mixer_ioctl: + * @inode: + * @file: + */ + +static int +forte_mixer_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *) file->private_data; + + return codec->mixer_ioctl (codec, cmd, arg); +} + + +static struct file_operations forte_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = forte_mixer_ioctl, + .open = forte_mixer_open, + .release = forte_mixer_release, +}; + + +/* Channel ----------------------------------------------------------------- */ + +/** + * forte_channel_reset: + * @channel: Channel to reset + * + * Locking: Must be called with lock held. + */ + +static void +forte_channel_reset (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + DPRINTK ("%s: channel = %s\n", __FUNCTION__, channel->name); + + channel->ctrl &= ~FORTE_START; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); + + /* We always play at least two fragments, hence these defaults */ + channel->hwptr = channel->frag_sz; + channel->next_buf = 1; + channel->swptr = 0; + channel->filled_frags = 0; + channel->active = 0; + channel->bytes = 0; + channel->nr_irqs = 0; + channel->mapped = 0; + channel->residue = 0; +} + + +/** + * forte_channel_start: + * @channel: Channel to start (record/playback) + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_start (struct forte_channel *channel) +{ + if (!channel || !channel->iobase || channel->active) + return; + + channel->ctrl &= ~(FORTE_PAUSE | FORTE_BUF1_LAST | FORTE_BUF2_LAST + | FORTE_IMMED_STOP); + channel->ctrl |= FORTE_START; + channel->active = 1; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_stop: + * @channel: Channel to stop + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_stop (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + channel->ctrl &= ~(FORTE_START | FORTE_PAUSE); + channel->ctrl |= FORTE_IMMED_STOP; + + channel->active = 0; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_pause: + * @channel: Channel to pause + * + * Locking: Must be called with lock held. + */ + +static void inline +forte_channel_pause (struct forte_channel *channel) +{ + if (!channel || !channel->iobase) + return; + + channel->ctrl |= FORTE_PAUSE; + + channel->active = 0; + outw (channel->ctrl, channel->iobase + FORTE_PLY_CTRL); +} + + +/** + * forte_channel_rate: + * @channel: Channel whose rate to set. Playback and record are + * independent. + * @rate: Channel rate in Hz + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_rate (struct forte_channel *channel, unsigned int rate) +{ + int new_rate; + + if (!channel || !channel->iobase) + return -EINVAL; + + /* The FM801 only supports a handful of fixed frequencies. + * We find the value closest to what userland requested. + */ + if (rate <= 6250) { rate = 5500; new_rate = 0; } + else if (rate <= 8800) { rate = 8000; new_rate = 1; } + else if (rate <= 10312) { rate = 9600; new_rate = 2; } + else if (rate <= 13512) { rate = 11025; new_rate = 3; } + else if (rate <= 17600) { rate = 16000; new_rate = 4; } + else if (rate <= 20625) { rate = 19200; new_rate = 5; } + else if (rate <= 27025) { rate = 22050; new_rate = 6; } + else if (rate <= 35200) { rate = 32000; new_rate = 7; } + else if (rate <= 41250) { rate = 38400; new_rate = 8; } + else if (rate <= 46050) { rate = 44100; new_rate = 9; } + else { rate = 48000; new_rate = 10; } + + channel->ctrl &= ~FORTE_RATE_MASK; + channel->ctrl |= new_rate << FORTE_RATE_SHIFT; + channel->rate = rate; + + DPRINTK ("%s: %s rate = %d\n", __FUNCTION__, channel->name, rate); + + return rate; +} + + +/** + * forte_channel_format: + * @channel: Channel whose audio format to set + * @format: OSS format ID + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_format (struct forte_channel *channel, int format) +{ + if (!channel || !channel->iobase) + return -EINVAL; + + switch (format) { + + case AFMT_QUERY: + break; + + case AFMT_U8: + channel->ctrl &= ~FORTE_16BIT; + channel->format = AFMT_U8; + break; + + case AFMT_S16_LE: + default: + channel->ctrl |= FORTE_16BIT; + channel->format = AFMT_S16_LE; + break; + } + + DPRINTK ("%s: %s want %d format, got %d\n", __FUNCTION__, channel->name, + format, channel->format); + + return channel->format; +} + + +/** + * forte_channel_stereo: + * @channel: Channel to toggle + * @stereo: 0 for Mono, 1 for Stereo + * + * Locking: Must be called with lock held. + */ + +static int +forte_channel_stereo (struct forte_channel *channel, unsigned int stereo) +{ + int ret; + + if (!channel || !channel->iobase) + return -EINVAL; + + DPRINTK ("%s: %s stereo = %d\n", __FUNCTION__, channel->name, stereo); + + switch (stereo) { + + case 0: + channel->ctrl &= ~(FORTE_STEREO | FORTE_CHANNELS_MASK); + channel-> stereo = stereo; + ret = stereo; + break; + + case 1: + channel->ctrl &= ~FORTE_CHANNELS_MASK; + channel->ctrl |= FORTE_STEREO; + channel-> stereo = stereo; + ret = stereo; + break; + + default: + DPRINTK ("Unsupported channel format"); + ret = -EINVAL; + break; + } + + return ret; +} + + +/** + * forte_channel_buffer: + * @channel: Channel whose buffer to set up + * + * Locking: Must be called with lock held. + */ + +static void +forte_channel_buffer (struct forte_channel *channel, int sz, int num) +{ + unsigned int msecs, shift; + + /* Go away, I'm busy */ + if (channel->filled_frags || channel->bytes) + return; + + /* Fragment size must be a power of 2 */ + shift = 0; sz++; + while (sz >>= 1) + shift++; + channel->frag_sz = 1 << shift; + + /* Round fragment size to something reasonable */ + if (channel->frag_sz < FORTE_MIN_FRAG_SIZE) + channel->frag_sz = FORTE_MIN_FRAG_SIZE; + + if (channel->frag_sz > FORTE_MAX_FRAG_SIZE) + channel->frag_sz = FORTE_MAX_FRAG_SIZE; + + /* Find fragment length in milliseconds */ + msecs = channel->frag_sz / + (channel->format == AFMT_S16_LE ? 2 : 1) / + (channel->stereo ? 2 : 1) / + (channel->rate / 1000); + + channel->frag_msecs = msecs; + + /* Pick a suitable number of fragments */ + if (msecs * num < FORTE_MIN_BUF_MSECS) + num = FORTE_MIN_BUF_MSECS / msecs; + + if (msecs * num > FORTE_MAX_BUF_MSECS) + num = FORTE_MAX_BUF_MSECS / msecs; + + /* Fragment number must be a power of 2 */ + shift = 0; + while (num >>= 1) + shift++; + channel->frag_num = 1 << (shift + 1); + + /* Round fragment number to something reasonable */ + if (channel->frag_num < FORTE_MIN_FRAGMENTS) + channel->frag_num = FORTE_MIN_FRAGMENTS; + + if (channel->frag_num > FORTE_MAX_FRAGMENTS) + channel->frag_num = FORTE_MAX_FRAGMENTS; + + channel->buf_sz = channel->frag_sz * channel->frag_num; + + DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d\n", + __FUNCTION__, channel->name, channel->frag_sz, + channel->frag_num, channel->buf_sz); +} + + +/** + * forte_channel_prep: + * @channel: Channel whose buffer to prepare + * + * Locking: Lock held. + */ + +static void +forte_channel_prep (struct forte_channel *channel) +{ + struct page *page; + int i; + + if (channel->buf) + return; + + forte_channel_buffer (channel, channel->frag_sz, channel->frag_num); + channel->buf_pages = channel->buf_sz >> PAGE_SHIFT; + + if (channel->buf_sz % PAGE_SIZE) + channel->buf_pages++; + + DPRINTK ("%s: %s frag_sz = %d, frag_num = %d, buf_sz = %d, pg = %d\n", + __FUNCTION__, channel->name, channel->frag_sz, + channel->frag_num, channel->buf_sz, channel->buf_pages); + + /* DMA buffer */ + channel->buf = pci_alloc_consistent (forte->pci_dev, + channel->buf_pages * PAGE_SIZE, + &channel->buf_handle); + + if (!channel->buf || !channel->buf_handle) + BUG(); + + page = virt_to_page (channel->buf); + + /* FIXME: can this go away ? */ + for (i = 0 ; i < channel->buf_pages ; i++) + SetPageReserved(page++); + + /* Prep buffer registers */ + outw (channel->frag_sz - 1, channel->iobase + FORTE_PLY_COUNT); + outl (channel->buf_handle, channel->iobase + FORTE_PLY_BUF1); + outl (channel->buf_handle + channel->frag_sz, + channel->iobase + FORTE_PLY_BUF2); + + /* Reset hwptr */ + channel->hwptr = channel->frag_sz; + channel->next_buf = 1; + + DPRINTK ("%s: %s buffer @ %p (%p)\n", __FUNCTION__, channel->name, + channel->buf, channel->buf_handle); +} + + +/** + * forte_channel_drain: + * @chip: + * @channel: + * + * Locking: Don't hold the lock. + */ + +static inline int +forte_channel_drain (struct forte_channel *channel) +{ + DECLARE_WAITQUEUE (wait, current); + unsigned long flags; + + DPRINTK ("%s\n", __FUNCTION__); + + if (channel->mapped) { + spin_lock_irqsave (&forte->lock, flags); + forte_channel_stop (channel); + spin_unlock_irqrestore (&forte->lock, flags); + return 0; + } + + spin_lock_irqsave (&forte->lock, flags); + add_wait_queue (&channel->wait, &wait); + + for (;;) { + if (channel->active == 0 || channel->filled_frags == 1) + break; + + spin_unlock_irqrestore (&forte->lock, flags); + + __set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&forte->lock, flags); + } + + forte_channel_stop (channel); + forte_channel_reset (channel); + set_current_state (TASK_RUNNING); + remove_wait_queue (&channel->wait, &wait); + spin_unlock_irqrestore (&forte->lock, flags); + + return 0; +} + + +/** + * forte_channel_init: + * @chip: Forte chip instance the channel hangs off + * @channel: Channel to initialize + * + * Description: + * Initializes a channel, sets defaults, and allocates + * buffers. + * + * Locking: No lock held. + */ + +static int +forte_channel_init (struct forte_chip *chip, struct forte_channel *channel) +{ + DPRINTK ("%s: chip iobase @ %p\n", __FUNCTION__, (void *)chip->iobase); + + spin_lock_irq (&chip->lock); + memset (channel, 0x0, sizeof (*channel)); + + if (channel == &chip->play) { + channel->name = "PCM_OUT"; + channel->iobase = chip->iobase; + DPRINTK ("%s: PCM-OUT iobase @ %p\n", __FUNCTION__, + (void *) channel->iobase); + } + else if (channel == &chip->rec) { + channel->name = "PCM_IN"; + channel->iobase = chip->iobase + FORTE_CAP_OFFSET; + channel->record = 1; + DPRINTK ("%s: PCM-IN iobase @ %p\n", __FUNCTION__, + (void *) channel->iobase); + } + else + BUG(); + + init_waitqueue_head (&channel->wait); + + /* Defaults: 48kHz, 16-bit, stereo */ + channel->ctrl = inw (channel->iobase + FORTE_PLY_CTRL); + forte_channel_reset (channel); + forte_channel_stereo (channel, 1); + forte_channel_format (channel, AFMT_S16_LE); + forte_channel_rate (channel, 48000); + channel->frag_sz = FORTE_DEF_FRAG_SIZE; + channel->frag_num = FORTE_DEF_FRAGMENTS; + + chip->trigger = 0; + spin_unlock_irq (&chip->lock); + + return 0; +} + + +/** + * forte_channel_free: + * @chip: Chip this channel hangs off + * @channel: Channel to nuke + * + * Description: + * Resets channel and frees buffers. + * + * Locking: Hold your horses. + */ + +static void +forte_channel_free (struct forte_chip *chip, struct forte_channel *channel) +{ + DPRINTK ("%s: %s\n", __FUNCTION__, channel->name); + + if (!channel->buf_handle) + return; + + pci_free_consistent (chip->pci_dev, channel->buf_pages * PAGE_SIZE, + channel->buf, channel->buf_handle); + + memset (channel, 0x0, sizeof (*channel)); +} + + +/* DSP --------------------------------------------------------------------- */ + + +/** + * forte_dsp_ioctl: + */ + +static int +forte_dsp_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ival=0, ret, rval=0, rd, wr, count; + struct forte_chip *chip; + struct audio_buf_info abi; + struct count_info cinfo; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + chip = file->private_data; + + if (file->f_mode & FMODE_WRITE) + wr = 1; + else + wr = 0; + + if (file->f_mode & FMODE_READ) + rd = 1; + else + rd = 0; + + switch (cmd) { + + case OSS_GETVERSION: + return put_user (SOUND_VERSION, p); + + case SNDCTL_DSP_GETCAPS: + DPRINTK ("%s: GETCAPS\n", __FUNCTION__); + + ival = FORTE_CAPS; /* DUPLEX */ + return put_user (ival, p); + + case SNDCTL_DSP_GETFMTS: + DPRINTK ("%s: GETFMTS\n", __FUNCTION__); + + ival = FORTE_FMTS; /* U8, 16LE */ + return put_user (ival, p); + + case SNDCTL_DSP_SETFMT: /* U8, 16LE */ + DPRINTK ("%s: SETFMT\n", __FUNCTION__); + + if (get_user (ival, p)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_format (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->rec); + rval = forte_channel_format (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, p); + + case SNDCTL_DSP_STEREO: /* 0 - mono, 1 - stereo */ + DPRINTK ("%s: STEREO\n", __FUNCTION__); + + if (get_user (ival, p)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, p); + + case SNDCTL_DSP_CHANNELS: /* 1 - mono, 2 - stereo */ + DPRINTK ("%s: CHANNELS\n", __FUNCTION__); + + if (get_user (ival, p)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_stereo (&chip->rec, ival-1) + 1; + } + + if (wr) { + forte_channel_stop (&chip->play); + rval = forte_channel_stereo (&chip->play, ival-1) + 1; + } + + spin_unlock_irq (&chip->lock); + + return put_user (rval, p); + + case SNDCTL_DSP_SPEED: + DPRINTK ("%s: SPEED\n", __FUNCTION__); + + if (get_user (ival, p)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_stop (&chip->rec); + rval = forte_channel_rate (&chip->rec, ival); + } + + if (wr) { + forte_channel_stop (&chip->play); + rval = forte_channel_rate (&chip->play, ival); + } + + spin_unlock_irq (&chip->lock); + + return put_user(rval, p); + + case SNDCTL_DSP_GETBLKSIZE: + DPRINTK ("%s: GETBLKSIZE\n", __FUNCTION__); + + spin_lock_irq (&chip->lock); + + if (rd) + ival = chip->rec.frag_sz; + + if (wr) + ival = chip->play.frag_sz; + + spin_unlock_irq (&chip->lock); + + return put_user (ival, p); + + case SNDCTL_DSP_RESET: + DPRINTK ("%s: RESET\n", __FUNCTION__); + + spin_lock_irq (&chip->lock); + + if (rd) + forte_channel_reset (&chip->rec); + + if (wr) + forte_channel_reset (&chip->play); + + spin_unlock_irq (&chip->lock); + + return 0; + + case SNDCTL_DSP_SYNC: + DPRINTK ("%s: SYNC\n", __FUNCTION__); + + if (wr) + ret = forte_channel_drain (&chip->play); + + return 0; + + case SNDCTL_DSP_POST: + DPRINTK ("%s: POST\n", __FUNCTION__); + + if (wr) { + spin_lock_irq (&chip->lock); + + if (chip->play.filled_frags) + forte_channel_start (&chip->play); + + spin_unlock_irq (&chip->lock); + } + + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + DPRINTK ("%s: SETFRAGMENT\n", __FUNCTION__); + + if (get_user (ival, p)) + return -EFAULT; + + spin_lock_irq (&chip->lock); + + if (rd) { + forte_channel_buffer (&chip->rec, ival & 0xffff, + (ival >> 16) & 0xffff); + ival = (chip->rec.frag_num << 16) + chip->rec.frag_sz; + } + + if (wr) { + forte_channel_buffer (&chip->play, ival & 0xffff, + (ival >> 16) & 0xffff); + ival = (chip->play.frag_num << 16) +chip->play.frag_sz; + } + + spin_unlock_irq (&chip->lock); + + return put_user (ival, p); + + case SNDCTL_DSP_GETISPACE: + DPRINTK ("%s: GETISPACE\n", __FUNCTION__); + + if (!rd) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + abi.fragstotal = chip->rec.frag_num; + abi.fragsize = chip->rec.frag_sz; + + if (chip->rec.mapped) { + abi.fragments = chip->rec.frag_num - 2; + abi.bytes = abi.fragments * abi.fragsize; + } + else { + abi.fragments = chip->rec.filled_frags; + abi.bytes = abi.fragments * abi.fragsize; + } + + spin_unlock_irq (&chip->lock); + + return copy_to_user (argp, &abi, sizeof (abi)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETIPTR: + DPRINTK ("%s: GETIPTR\n", __FUNCTION__); + + if (!rd) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (chip->rec.active) + cinfo.ptr = chip->rec.hwptr; + else + cinfo.ptr = 0; + + cinfo.bytes = chip->rec.bytes; + cinfo.blocks = chip->rec.nr_irqs; + chip->rec.nr_irqs = 0; + + spin_unlock_irq (&chip->lock); + + return copy_to_user (argp, &cinfo, sizeof (cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOSPACE: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + abi.fragstotal = chip->play.frag_num; + abi.fragsize = chip->play.frag_sz; + + if (chip->play.mapped) { + abi.fragments = chip->play.frag_num - 2; + abi.bytes = chip->play.buf_sz; + } + else { + abi.fragments = chip->play.frag_num - + chip->play.filled_frags; + + if (chip->play.residue) + abi.fragments--; + + abi.bytes = abi.fragments * abi.fragsize + + chip->play.residue; + } + + spin_unlock_irq (&chip->lock); + + return copy_to_user (argp, &abi, sizeof (abi)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (chip->play.active) + cinfo.ptr = chip->play.hwptr; + else + cinfo.ptr = 0; + + cinfo.bytes = chip->play.bytes; + cinfo.blocks = chip->play.nr_irqs; + chip->play.nr_irqs = 0; + + spin_unlock_irq (&chip->lock); + + return copy_to_user (argp, &cinfo, sizeof (cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETODELAY: + if (!wr) + return -EINVAL; + + spin_lock_irq (&chip->lock); + + if (!chip->play.active) { + ival = 0; + } + else if (chip->play.mapped) { + count = inw (chip->play.iobase + FORTE_PLY_COUNT) + 1; + ival = chip->play.frag_sz - count; + } + else { + ival = chip->play.filled_frags * chip->play.frag_sz; + + if (chip->play.residue) + ival += chip->play.frag_sz - chip->play.residue; + } + + spin_unlock_irq (&chip->lock); + + return put_user (ival, p); + + case SNDCTL_DSP_SETDUPLEX: + DPRINTK ("%s: SETDUPLEX\n", __FUNCTION__); + + return -EINVAL; + + case SNDCTL_DSP_GETTRIGGER: + DPRINTK ("%s: GETTRIGGER\n", __FUNCTION__); + + return put_user (chip->trigger, p); + + case SNDCTL_DSP_SETTRIGGER: + + if (get_user (ival, p)) + return -EFAULT; + + DPRINTK ("%s: SETTRIGGER %d\n", __FUNCTION__, ival); + + if (wr) { + spin_lock_irq (&chip->lock); + + if (ival & PCM_ENABLE_OUTPUT) + forte_channel_start (&chip->play); + else { + chip->trigger = 1; + forte_channel_prep (&chip->play); + forte_channel_stop (&chip->play); + } + + spin_unlock_irq (&chip->lock); + } + else if (rd) { + spin_lock_irq (&chip->lock); + + if (ival & PCM_ENABLE_INPUT) + forte_channel_start (&chip->rec); + else { + chip->trigger = 1; + forte_channel_prep (&chip->rec); + forte_channel_stop (&chip->rec); + } + + spin_unlock_irq (&chip->lock); + } + + return 0; + + case SOUND_PCM_READ_RATE: + DPRINTK ("%s: PCM_READ_RATE\n", __FUNCTION__); + return put_user (chip->play.rate, p); + + case SOUND_PCM_READ_CHANNELS: + DPRINTK ("%s: PCM_READ_CHANNELS\n", __FUNCTION__); + return put_user (chip->play.stereo, p); + + case SOUND_PCM_READ_BITS: + DPRINTK ("%s: PCM_READ_BITS\n", __FUNCTION__); + return put_user (chip->play.format, p); + + case SNDCTL_DSP_NONBLOCK: + DPRINTK ("%s: DSP_NONBLOCK\n", __FUNCTION__); + file->f_flags |= O_NONBLOCK; + return 0; + + default: + DPRINTK ("Unsupported ioctl: %x (%p)\n", cmd, argp); + break; + } + + return -EINVAL; +} + + +/** + * forte_dsp_open: + */ + +static int +forte_dsp_open (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = forte; /* FIXME: HACK FROM HELL! */ + + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&chip->open_mutex)) { + DPRINTK ("%s: returning -EAGAIN\n", __FUNCTION__); + return -EAGAIN; + } + } + else { + if (mutex_lock_interruptible(&chip->open_mutex)) { + DPRINTK ("%s: returning -ERESTARTSYS\n", __FUNCTION__); + return -ERESTARTSYS; + } + } + + file->private_data = forte; + + DPRINTK ("%s: dsp opened by %d\n", __FUNCTION__, current->pid); + + if (file->f_mode & FMODE_WRITE) + forte_channel_init (forte, &forte->play); + + if (file->f_mode & FMODE_READ) + forte_channel_init (forte, &forte->rec); + + return nonseekable_open(inode, file); +} + + +/** + * forte_dsp_release: + */ + +static int +forte_dsp_release (struct inode *inode, struct file *file) +{ + struct forte_chip *chip = file->private_data; + int ret = 0; + + DPRINTK ("%s: chip @ %p\n", __FUNCTION__, chip); + + if (file->f_mode & FMODE_WRITE) { + forte_channel_drain (&chip->play); + + spin_lock_irq (&chip->lock); + + forte_channel_free (chip, &chip->play); + + spin_unlock_irq (&chip->lock); + } + + if (file->f_mode & FMODE_READ) { + while (chip->rec.filled_frags > 0) + interruptible_sleep_on (&chip->rec.wait); + + spin_lock_irq (&chip->lock); + + forte_channel_stop (&chip->rec); + forte_channel_free (chip, &chip->rec); + + spin_unlock_irq (&chip->lock); + } + + mutex_unlock(&chip->open_mutex); + + return ret; +} + + +/** + * forte_dsp_poll: + * + */ + +static unsigned int +forte_dsp_poll (struct file *file, struct poll_table_struct *wait) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int mask = 0; + + chip = file->private_data; + + if (file->f_mode & FMODE_WRITE) { + channel = &chip->play; + + if (channel->active) + poll_wait (file, &channel->wait, wait); + + spin_lock_irq (&chip->lock); + + if (channel->frag_num - channel->filled_frags > 0) + mask |= POLLOUT | POLLWRNORM; + + spin_unlock_irq (&chip->lock); + } + + if (file->f_mode & FMODE_READ) { + channel = &chip->rec; + + if (channel->active) + poll_wait (file, &channel->wait, wait); + + spin_lock_irq (&chip->lock); + + if (channel->filled_frags > 0) + mask |= POLLIN | POLLRDNORM; + + spin_unlock_irq (&chip->lock); + } + + return mask; +} + + +/** + * forte_dsp_mmap: + */ + +static int +forte_dsp_mmap (struct file *file, struct vm_area_struct *vma) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned long size; + int ret; + + chip = file->private_data; + + DPRINTK ("%s: start %lXh, size %ld, pgoff %ld\n", __FUNCTION__, + vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_pgoff); + + spin_lock_irq (&chip->lock); + + if (vma->vm_flags & VM_WRITE && chip->play.active) { + ret = -EBUSY; + goto out; + } + + if (vma->vm_flags & VM_READ && chip->rec.active) { + ret = -EBUSY; + goto out; + } + + if (file->f_mode & FMODE_WRITE) + channel = &chip->play; + else if (file->f_mode & FMODE_READ) + channel = &chip->rec; + else { + ret = -EINVAL; + goto out; + } + + forte_channel_prep (channel); + channel->mapped = 1; + + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + + size = vma->vm_end - vma->vm_start; + + if (size > channel->buf_pages * PAGE_SIZE) { + DPRINTK ("%s: size (%ld) > buf_sz (%d) \n", __FUNCTION__, + size, channel->buf_sz); + ret = -EINVAL; + goto out; + } + + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(channel->buf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) { + DPRINTK ("%s: remap el a no worko\n", __FUNCTION__); + ret = -EAGAIN; + goto out; + } + + ret = 0; + + out: + spin_unlock_irq (&chip->lock); + return ret; +} + + +/** + * forte_dsp_write: + */ + +static ssize_t +forte_dsp_write (struct file *file, const char __user *buffer, size_t bytes, + loff_t *ppos) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int i = bytes, sz = 0; + unsigned long flags; + + if (!access_ok (VERIFY_READ, buffer, bytes)) + return -EFAULT; + + chip = (struct forte_chip *) file->private_data; + + if (!chip) + BUG(); + + channel = &chip->play; + + if (!channel) + BUG(); + + spin_lock_irqsave (&chip->lock, flags); + + /* Set up buffers with the right fragment size */ + forte_channel_prep (channel); + + while (i) { + /* All fragment buffers in use -> wait */ + if (channel->frag_num - channel->filled_frags == 0) { + DECLARE_WAITQUEUE (wait, current); + + /* For trigger or non-blocking operation, get out */ + if (chip->trigger || file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore (&chip->lock, flags); + return -EAGAIN; + } + + /* Otherwise wait for buffers */ + add_wait_queue (&channel->wait, &wait); + + for (;;) { + spin_unlock_irqrestore (&chip->lock, flags); + + set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&chip->lock, flags); + + if (channel->frag_num - channel->filled_frags) + break; + } + + remove_wait_queue (&channel->wait, &wait); + set_current_state (TASK_RUNNING); + + if (signal_pending (current)) { + spin_unlock_irqrestore (&chip->lock, flags); + return -ERESTARTSYS; + } + } + + if (channel->residue) + sz = channel->residue; + else if (i > channel->frag_sz) + sz = channel->frag_sz; + else + sz = i; + + spin_unlock_irqrestore (&chip->lock, flags); + + if (copy_from_user ((void *) channel->buf + channel->swptr, buffer, sz)) + return -EFAULT; + + spin_lock_irqsave (&chip->lock, flags); + + /* Advance software pointer */ + buffer += sz; + channel->swptr += sz; + channel->swptr %= channel->buf_sz; + i -= sz; + + /* Only bump filled_frags if a full fragment has been written */ + if (channel->swptr % channel->frag_sz == 0) { + channel->filled_frags++; + channel->residue = 0; + } + else + channel->residue = channel->frag_sz - sz; + + /* If playback isn't active, start it */ + if (channel->active == 0 && chip->trigger == 0) + forte_channel_start (channel); + } + + spin_unlock_irqrestore (&chip->lock, flags); + + return bytes - i; +} + + +/** + * forte_dsp_read: + */ + +static ssize_t +forte_dsp_read (struct file *file, char __user *buffer, size_t bytes, + loff_t *ppos) +{ + struct forte_chip *chip; + struct forte_channel *channel; + unsigned int i = bytes, sz; + unsigned long flags; + + if (!access_ok (VERIFY_WRITE, buffer, bytes)) + return -EFAULT; + + chip = (struct forte_chip *) file->private_data; + + if (!chip) + BUG(); + + channel = &chip->rec; + + if (!channel) + BUG(); + + spin_lock_irqsave (&chip->lock, flags); + + /* Set up buffers with the right fragment size */ + forte_channel_prep (channel); + + /* Start recording */ + if (!chip->trigger) + forte_channel_start (channel); + + while (i) { + /* No fragment buffers in use -> wait */ + if (channel->filled_frags == 0) { + DECLARE_WAITQUEUE (wait, current); + + /* For trigger mode operation, get out */ + if (chip->trigger) { + spin_unlock_irqrestore (&chip->lock, flags); + return -EAGAIN; + } + + add_wait_queue (&channel->wait, &wait); + + for (;;) { + if (channel->active == 0) + break; + + if (channel->filled_frags) + break; + + spin_unlock_irqrestore (&chip->lock, flags); + + set_current_state (TASK_INTERRUPTIBLE); + schedule(); + + spin_lock_irqsave (&chip->lock, flags); + } + + set_current_state (TASK_RUNNING); + remove_wait_queue (&channel->wait, &wait); + } + + if (i > channel->frag_sz) + sz = channel->frag_sz; + else + sz = i; + + spin_unlock_irqrestore (&chip->lock, flags); + + if (copy_to_user (buffer, (void *)channel->buf+channel->swptr, sz)) { + DPRINTK ("%s: copy_to_user failed\n", __FUNCTION__); + return -EFAULT; + } + + spin_lock_irqsave (&chip->lock, flags); + + /* Advance software pointer */ + buffer += sz; + if (channel->filled_frags > 0) + channel->filled_frags--; + channel->swptr += channel->frag_sz; + channel->swptr %= channel->buf_sz; + i -= sz; + } + + spin_unlock_irqrestore (&chip->lock, flags); + + return bytes - i; +} + + +static struct file_operations forte_dsp_fops = { + .owner = THIS_MODULE, + .llseek = &no_llseek, + .read = &forte_dsp_read, + .write = &forte_dsp_write, + .poll = &forte_dsp_poll, + .ioctl = &forte_dsp_ioctl, + .open = &forte_dsp_open, + .release = &forte_dsp_release, + .mmap = &forte_dsp_mmap, +}; + + +/* Common ------------------------------------------------------------------ */ + + +/** + * forte_interrupt: + */ + +static irqreturn_t +forte_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct forte_chip *chip = dev_id; + struct forte_channel *channel = NULL; + u16 status, count; + + status = inw (chip->iobase + FORTE_IRQ_STATUS); + + /* If this is not for us, get outta here ASAP */ + if ((status & (FORTE_IRQ_PLAYBACK | FORTE_IRQ_CAPTURE)) == 0) + return IRQ_NONE; + + if (status & FORTE_IRQ_PLAYBACK) { + channel = &chip->play; + + spin_lock (&chip->lock); + + if (channel->frag_sz == 0) + goto pack; + + /* Declare a fragment done */ + if (channel->filled_frags > 0) + channel->filled_frags--; + channel->bytes += channel->frag_sz; + channel->nr_irqs++; + + /* Flip-flop between buffer I and II */ + channel->next_buf ^= 1; + + /* Advance hardware pointer by fragment size and wrap around */ + channel->hwptr += channel->frag_sz; + channel->hwptr %= channel->buf_sz; + + /* Buffer I or buffer II BAR */ + outl (channel->buf_handle + channel->hwptr, + channel->next_buf == 0 ? + channel->iobase + FORTE_PLY_BUF1 : + channel->iobase + FORTE_PLY_BUF2); + + /* If the currently playing fragment is last, schedule pause */ + if (channel->filled_frags == 1) + forte_channel_pause (channel); + + pack: + /* Acknowledge interrupt */ + outw (FORTE_IRQ_PLAYBACK, chip->iobase + FORTE_IRQ_STATUS); + + if (waitqueue_active (&channel->wait)) + wake_up_all (&channel->wait); + + spin_unlock (&chip->lock); + } + + if (status & FORTE_IRQ_CAPTURE) { + channel = &chip->rec; + spin_lock (&chip->lock); + + /* One fragment filled */ + channel->filled_frags++; + + /* Get # of completed bytes */ + count = inw (channel->iobase + FORTE_PLY_COUNT) + 1; + + if (count == 0) { + DPRINTK ("%s: last, filled_frags = %d\n", __FUNCTION__, + channel->filled_frags); + channel->filled_frags = 0; + goto rack; + } + + /* Buffer I or buffer II BAR */ + outl (channel->buf_handle + channel->hwptr, + channel->next_buf == 0 ? + channel->iobase + FORTE_PLY_BUF1 : + channel->iobase + FORTE_PLY_BUF2); + + /* Flip-flop between buffer I and II */ + channel->next_buf ^= 1; + + /* Advance hardware pointer by fragment size and wrap around */ + channel->hwptr += channel->frag_sz; + channel->hwptr %= channel->buf_sz; + + /* Out of buffers */ + if (channel->filled_frags == channel->frag_num - 1) + forte_channel_stop (channel); + rack: + /* Acknowledge interrupt */ + outw (FORTE_IRQ_CAPTURE, chip->iobase + FORTE_IRQ_STATUS); + + spin_unlock (&chip->lock); + + if (waitqueue_active (&channel->wait)) + wake_up_all (&channel->wait); + } + + return IRQ_HANDLED; +} + + +/** + * forte_proc_read: + */ + +static int +forte_proc_read (char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i = 0, p_rate, p_chan, r_rate; + unsigned short p_reg, r_reg; + + i += sprintf (page, "ForteMedia FM801 OSS Lite driver\n%s\n \n", + DRIVER_VERSION); + + if (!forte->iobase) + return i; + + p_rate = p_chan = -1; + p_reg = inw (forte->iobase + FORTE_PLY_CTRL); + p_rate = (p_reg >> 8) & 15; + p_chan = (p_reg >> 12) & 3; + + if (p_rate >= 0 || p_rate <= 10) + p_rate = rates[p_rate]; + + if (p_chan >= 0 || p_chan <= 2) + p_chan = channels[p_chan]; + + r_rate = -1; + r_reg = inw (forte->iobase + FORTE_CAP_CTRL); + r_rate = (r_reg >> 8) & 15; + + if (r_rate >= 0 || r_rate <= 10) + r_rate = rates[r_rate]; + + i += sprintf (page + i, + " Playback Capture\n" + "FIFO empty : %-3s %-3s\n" + "Buf1 Last : %-3s %-3s\n" + "Buf2 Last : %-3s %-3s\n" + "Started : %-3s %-3s\n" + "Paused : %-3s %-3s\n" + "Immed Stop : %-3s %-3s\n" + "Rate : %-5d %-5d\n" + "Channels : %-5d -\n" + "16-bit : %-3s %-3s\n" + "Stereo : %-3s %-3s\n" + " \n" + "Buffer Sz : %-6d %-6d\n" + "Frag Sz : %-6d %-6d\n" + "Frag Num : %-6d %-6d\n" + "Frag msecs : %-6d %-6d\n" + "Used Frags : %-6d %-6d\n" + "Mapped : %-3s %-3s\n", + p_reg & 1<<0 ? "yes" : "no", + r_reg & 1<<0 ? "yes" : "no", + p_reg & 1<<1 ? "yes" : "no", + r_reg & 1<<1 ? "yes" : "no", + p_reg & 1<<2 ? "yes" : "no", + r_reg & 1<<2 ? "yes" : "no", + p_reg & 1<<5 ? "yes" : "no", + r_reg & 1<<5 ? "yes" : "no", + p_reg & 1<<6 ? "yes" : "no", + r_reg & 1<<6 ? "yes" : "no", + p_reg & 1<<7 ? "yes" : "no", + r_reg & 1<<7 ? "yes" : "no", + p_rate, r_rate, + p_chan, + p_reg & 1<<14 ? "yes" : "no", + r_reg & 1<<14 ? "yes" : "no", + p_reg & 1<<15 ? "yes" : "no", + r_reg & 1<<15 ? "yes" : "no", + forte->play.buf_sz, forte->rec.buf_sz, + forte->play.frag_sz, forte->rec.frag_sz, + forte->play.frag_num, forte->rec.frag_num, + forte->play.frag_msecs, forte->rec.frag_msecs, + forte->play.filled_frags, forte->rec.filled_frags, + forte->play.mapped ? "yes" : "no", + forte->rec.mapped ? "yes" : "no" + ); + + return i; +} + + +/** + * forte_proc_init: + * + * Creates driver info entries in /proc + */ + +static int __init +forte_proc_init (void) +{ + if (!proc_mkdir ("driver/forte", NULL)) + return -EIO; + + if (!create_proc_read_entry ("driver/forte/chip", 0, NULL, forte_proc_read, forte)) { + remove_proc_entry ("driver/forte", NULL); + return -EIO; + } + + if (!create_proc_read_entry("driver/forte/ac97", 0, NULL, ac97_read_proc, forte->ac97)) { + remove_proc_entry ("driver/forte/chip", NULL); + remove_proc_entry ("driver/forte", NULL); + return -EIO; + } + + return 0; +} + + +/** + * forte_proc_remove: + * + * Removes driver info entries in /proc + */ + +static void +forte_proc_remove (void) +{ + remove_proc_entry ("driver/forte/ac97", NULL); + remove_proc_entry ("driver/forte/chip", NULL); + remove_proc_entry ("driver/forte", NULL); +} + + +/** + * forte_chip_init: + * @chip: Chip instance to initialize + * + * Description: + * Resets chip, configures codec and registers the driver with + * the sound subsystem. + * + * Press and hold Start for 8 secs, then switch on Run + * and hold for 4 seconds. Let go of Start. Numbers + * assume a properly oiled TWG. + */ + +static int __devinit +forte_chip_init (struct forte_chip *chip) +{ + u8 revision; + u16 cmdw; + struct ac97_codec *codec; + + pci_read_config_byte (chip->pci_dev, PCI_REVISION_ID, &revision); + + if (revision >= 0xB1) { + chip->multichannel = 1; + printk (KERN_INFO PFX "Multi-channel device detected.\n"); + } + + /* Reset chip */ + outw (FORTE_CC_CODEC_RESET | FORTE_CC_AC97_RESET, + chip->iobase + FORTE_CODEC_CTRL); + udelay(100); + outw (0, chip->iobase + FORTE_CODEC_CTRL); + + /* Request read from AC97 */ + outw (FORTE_AC97_READ | (0 << FORTE_AC97_ADDR_SHIFT), + chip->iobase + FORTE_AC97_CMD); + mdelay(750); + + if ((inw (chip->iobase + FORTE_AC97_CMD) & (3<<8)) != (1<<8)) { + printk (KERN_INFO PFX "AC97 codec not responding"); + return -EIO; + } + + /* Init volume */ + outw (0x0808, chip->iobase + FORTE_PCM_VOL); + outw (0x9f1f, chip->iobase + FORTE_FM_VOL); + outw (0x8808, chip->iobase + FORTE_I2S_VOL); + + /* I2S control - I2S mode */ + outw (0x0003, chip->iobase + FORTE_I2S_MODE); + + /* Interrupt setup - unmask PLAYBACK & CAPTURE */ + cmdw = inw (chip->iobase + FORTE_IRQ_MASK); + cmdw &= ~0x0003; + outw (cmdw, chip->iobase + FORTE_IRQ_MASK); + + /* Interrupt clear */ + outw (FORTE_IRQ_PLAYBACK|FORTE_IRQ_CAPTURE, + chip->iobase + FORTE_IRQ_STATUS); + + /* Set up the AC97 codec */ + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + codec->private_data = chip; + codec->codec_read = forte_ac97_read; + codec->codec_write = forte_ac97_write; + codec->id = 0; + + if (ac97_probe_codec (codec) == 0) { + printk (KERN_ERR PFX "codec probe failed\n"); + ac97_release_codec(codec); + return -1; + } + + /* Register mixer */ + if ((codec->dev_mixer = + register_sound_mixer (&forte_mixer_fops, -1)) < 0) { + printk (KERN_ERR PFX "couldn't register mixer!\n"); + ac97_release_codec(codec); + return -1; + } + + chip->ac97 = codec; + + /* Register DSP */ + if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) { + printk (KERN_ERR PFX "couldn't register dsp!\n"); + return -1; + } + + /* Register with /proc */ + if (forte_proc_init()) { + printk (KERN_ERR PFX "couldn't add entries to /proc!\n"); + return -1; + } + + return 0; +} + + +/** + * forte_probe: + * @pci_dev: PCI struct for probed device + * @pci_id: + * + * Description: + * Allocates chip instance, I/O region, and IRQ + */ +static int __init +forte_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct forte_chip *chip; + int ret = 0; + + /* FIXME: Support more than one chip */ + if (found++) + return -EIO; + + /* Ignition */ + if (pci_enable_device (pci_dev)) + return -EIO; + + pci_set_master (pci_dev); + + /* Allocate chip instance and configure */ + forte = (struct forte_chip *) + kmalloc (sizeof (struct forte_chip), GFP_KERNEL); + chip = forte; + + if (chip == NULL) { + printk (KERN_WARNING PFX "Out of memory"); + return -ENOMEM; + } + + memset (chip, 0, sizeof (struct forte_chip)); + chip->pci_dev = pci_dev; + + mutex_init(&chip->open_mutex); + spin_lock_init (&chip->lock); + spin_lock_init (&chip->ac97_lock); + + if (! request_region (pci_resource_start (pci_dev, 0), + pci_resource_len (pci_dev, 0), DRIVER_NAME)) { + printk (KERN_WARNING PFX "Unable to reserve I/O space"); + ret = -ENOMEM; + goto error; + } + + chip->iobase = pci_resource_start (pci_dev, 0); + chip->irq = pci_dev->irq; + + if (request_irq (chip->irq, forte_interrupt, IRQF_SHARED, DRIVER_NAME, + chip)) { + printk (KERN_WARNING PFX "Unable to reserve IRQ"); + ret = -EIO; + goto error; + } + + pci_set_drvdata (pci_dev, chip); + + printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%16llX IRQ %u\n", + chip->iobase, (unsigned long long)pci_resource_end (pci_dev, 0), + chip->irq); + + /* Power it up */ + if ((ret = forte_chip_init (chip)) == 0) + return 0; + + error: + if (chip->irq) + free_irq (chip->irq, chip); + + if (chip->iobase) + release_region (pci_resource_start (pci_dev, 0), + pci_resource_len (pci_dev, 0)); + + kfree (chip); + + return ret; +} + + +/** + * forte_remove: + * @pci_dev: PCI device to unclaim + * + */ + +static void +forte_remove (struct pci_dev *pci_dev) +{ + struct forte_chip *chip = pci_get_drvdata (pci_dev); + + if (chip == NULL) + return; + + /* Turn volume down to avoid popping */ + outw (0x1f1f, chip->iobase + FORTE_PCM_VOL); + outw (0x1f1f, chip->iobase + FORTE_FM_VOL); + outw (0x1f1f, chip->iobase + FORTE_I2S_VOL); + + forte_proc_remove(); + free_irq (chip->irq, chip); + release_region (chip->iobase, pci_resource_len (pci_dev, 0)); + + unregister_sound_dsp (chip->dsp); + unregister_sound_mixer (chip->ac97->dev_mixer); + ac97_release_codec(chip->ac97); + kfree (chip); + + printk (KERN_INFO PFX "driver released\n"); +} + + +static struct pci_device_id forte_pci_ids[] = { + { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { 0, } +}; + + +static struct pci_driver forte_pci_driver = { + .name = DRIVER_NAME, + .id_table = forte_pci_ids, + .probe = forte_probe, + .remove = forte_remove, + +}; + + +/** + * forte_init_module: + * + */ + +static int __init +forte_init_module (void) +{ + printk (KERN_INFO PFX DRIVER_VERSION "\n"); + + return pci_register_driver (&forte_pci_driver); +} + + +/** + * forte_cleanup_module: + * + */ + +static void __exit +forte_cleanup_module (void) +{ + pci_unregister_driver (&forte_pci_driver); +} + + +module_init(forte_init_module); +module_exit(forte_cleanup_module); + +MODULE_AUTHOR("Martin K. Petersen "); +MODULE_DESCRIPTION("ForteMedia FM801 OSS Driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE (pci, forte_pci_ids); diff --git a/trunk/sound/oss/gus.h b/trunk/sound/oss/gus.h new file mode 100644 index 000000000000..3d5271baf042 --- /dev/null +++ b/trunk/sound/oss/gus.h @@ -0,0 +1,24 @@ + +#include "ad1848.h" + +/* From gus_card.c */ +int gus_set_midi_irq(int num); +irqreturn_t gusintr(int irq, void *dev_id, struct pt_regs * dummy); + +/* From gus_wave.c */ +int gus_wave_detect(int baseaddr); +void gus_wave_init(struct address_info *hw_config); +void gus_wave_unload (struct address_info *hw_config); +void gus_voice_irq(void); +void gus_write8(int reg, unsigned int data); +void guswave_dma_irq(void); +void gus_delay(void); +int gus_default_mixer_ioctl (int dev, unsigned int cmd, void __user *arg); +void gus_timer_command (unsigned int addr, unsigned int val); + +/* From gus_midi.c */ +void gus_midi_init(struct address_info *hw_config); +void gus_midi_interrupt(int dummy); + +/* From ics2101.c */ +int ics2101_mixer_init(void); diff --git a/trunk/sound/oss/gus_card.c b/trunk/sound/oss/gus_card.c new file mode 100644 index 000000000000..4539269b3d95 --- /dev/null +++ b/trunk/sound/oss/gus_card.c @@ -0,0 +1,292 @@ +/* + * sound/oss/gus_card.c + * + * Detection routine for the Gravis Ultrasound. + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * + * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * Christoph Hellwig: Adapted to module_init/module_exit, simple cleanups. + * + * Status: + * Tested... + */ + + +#include +#include +#include + +#include "sound_config.h" + +#include "gus.h" +#include "gus_hw.h" + +irqreturn_t gusintr(int irq, void *dev_id, struct pt_regs *dummy); + +int gus_base = 0, gus_irq = 0, gus_dma = 0; +int gus_no_wave_dma = 0; +extern int gus_wave_volume; +extern int gus_pcm_volume; +extern int have_gus_max; +int gus_pnp_flag = 0; +#ifdef CONFIG_SOUND_GUS16 +static int db16; /* Has a Gus16 AD1848 on it */ +#endif + +static void __init attach_gus(struct address_info *hw_config) +{ + gus_wave_init(hw_config); + + if (sound_alloc_dma(hw_config->dma, "GUS")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma); + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); + gus_midi_init(hw_config); + if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) + printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); + + return; +} + +static int __init probe_gus(struct address_info *hw_config) +{ + int irq; + int io_addr; + + if (hw_config->card_subtype == 1) + gus_pnp_flag = 1; + + irq = hw_config->irq; + + if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ + if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && + irq != 11 && irq != 12 && irq != 15) + { + printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq); + return 0; + } + if (gus_wave_detect(hw_config->io_base)) + return 1; + +#ifndef EXCLUDE_GUS_IODETECT + + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) { + if (io_addr == hw_config->io_base) /* Already tested */ + continue; + if (gus_wave_detect(io_addr)) { + hw_config->io_base = io_addr; + return 1; + } + } +#endif + + printk("NO GUS card found !\n"); + return 0; +} + +static void __exit unload_gus(struct address_info *hw_config) +{ + DDB(printk("unload_gus(%x)\n", hw_config->io_base)); + + gus_wave_unload(hw_config); + + release_region(hw_config->io_base, 16); + release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ + free_irq(hw_config->irq, hw_config); + + sound_free_dma(hw_config->dma); + + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + sound_free_dma(hw_config->dma2); +} + +irqreturn_t gusintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + unsigned char src; + extern int gus_timer_enabled; + int handled = 0; + +#ifdef CONFIG_SOUND_GUSMAX + if (have_gus_max) { + struct address_info *hw_config = dev_id; + adintr(irq, (void *)hw_config->slots[1], NULL); + } +#endif +#ifdef CONFIG_SOUND_GUS16 + if (db16) { + struct address_info *hw_config = dev_id; + adintr(irq, (void *)hw_config->slots[3], NULL); + } +#endif + + while (1) + { + if (!(src = inb(u_IrqStatus))) + break; + handled = 1; + if (src & DMA_TC_IRQ) + { + guswave_dma_irq(); + } + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + { + gus_midi_interrupt(0); + } + if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) + { + if (gus_timer_enabled) + sound_timer_interrupt(); + gus_write8(0x45, 0); /* Ack IRQ */ + gus_timer_command(4, 0x80); /* Reset IRQ flags */ + } + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + gus_voice_irq(); + } + return IRQ_RETVAL(handled); +} + +/* + * Some extra code for the 16 bit sampling option + */ + +#ifdef CONFIG_SOUND_GUS16 + +static int __init init_gus_db16(struct address_info *hw_config) +{ + struct resource *ports; + + ports = request_region(hw_config->io_base, 4, "ad1848"); + if (!ports) + return 0; + + if (!ad1848_detect(ports, NULL, hw_config->osp)) { + release_region(hw_config->io_base, 4); + return 0; + } + + gus_pcm_volume = 100; + gus_wave_volume = 90; + + hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", ports, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0, + hw_config->osp, + THIS_MODULE); + return 1; +} + +static void __exit unload_gus_db16(struct address_info *hw_config) +{ + + ad1848_unload(hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0); + sound_unload_audiodev(hw_config->slots[3]); +} +#endif + +#ifdef CONFIG_SOUND_GUS16 +static int gus16; +#endif +#ifdef CONFIG_SOUND_GUSMAX +static int no_wave_dma; /* Set if no dma is to be used for the + wave table (GF1 chip) */ +#endif + + +/* + * Note DMA2 of -1 has the right meaning in the GUS driver as well + * as here. + */ + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata type = 0; /* 1 for PnP */ + +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(dma, int, 0); +module_param(dma16, int, 0); +module_param(type, int, 0); +#ifdef CONFIG_SOUND_GUSMAX +module_param(no_wave_dma, int, 0); +#endif +#ifdef CONFIG_SOUND_GUS16 +module_param(db16, int, 0); +module_param(gus16, int, 0); +#endif +MODULE_LICENSE("GPL"); + +static int __init init_gus(void) +{ + printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + cfg.card_subtype = type; +#ifdef CONFIG_SOUND_GUSMAX + gus_no_wave_dma = no_wave_dma; +#endif + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + +#ifdef CONFIG_SOUND_GUS16 + if (gus16 && init_gus_db16(&cfg)) + db16 = 1; +#endif + if (!probe_gus(&cfg)) + return -ENODEV; + attach_gus(&cfg); + + return 0; +} + +static void __exit cleanup_gus(void) +{ +#ifdef CONFIG_SOUND_GUS16 + if (db16) + unload_gus_db16(&cfg); +#endif + unload_gus(&cfg); +} + +module_init(init_gus); +module_exit(cleanup_gus); + +#ifndef MODULE +static int __init setup_gus(char *str) +{ + /* io, irq, dma, dma2 */ + int ints[5]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + + return 1; +} + +__setup("gus=", setup_gus); +#endif diff --git a/trunk/sound/oss/gus_hw.h b/trunk/sound/oss/gus_hw.h new file mode 100644 index 000000000000..f97a0b8670e3 --- /dev/null +++ b/trunk/sound/oss/gus_hw.h @@ -0,0 +1,50 @@ + +/* + * I/O addresses + */ + +#define u_Base (gus_base + 0x000) +#define u_Mixer u_Base +#define u_Status (gus_base + 0x006) +#define u_TimerControl (gus_base + 0x008) +#define u_TimerData (gus_base + 0x009) +#define u_IRQDMAControl (gus_base + 0x00b) +#define u_MidiControl (gus_base + 0x100) +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 +#define u_MidiStatus u_MidiControl +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 +#define u_MidiData (gus_base + 0x101) +#define u_Voice (gus_base + 0x102) +#define u_Command (gus_base + 0x103) +#define u_DataLo (gus_base + 0x104) +#define u_DataHi (gus_base + 0x105) +#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ +#define u_MixSelect (gus_base + 0x506) /* registers. */ +#define u_IrqStatus u_Status +# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + +#define ICS2101 1 +# define ICS_MIXDEVS 6 +# define DEV_MIC 0 +# define DEV_LINE 1 +# define DEV_CD 2 +# define DEV_GF1 3 +# define DEV_UNUSED 4 +# define DEV_VOL 5 + +# define CHN_LEFT 0 +# define CHN_RIGHT 1 +#define CS4231 2 +#define u_DRAMIO (gus_base + 0x107) diff --git a/trunk/sound/oss/gus_linearvol.h b/trunk/sound/oss/gus_linearvol.h new file mode 100644 index 000000000000..7ad0c30d4fd9 --- /dev/null +++ b/trunk/sound/oss/gus_linearvol.h @@ -0,0 +1,18 @@ +static unsigned short gus_linearvol[128] = { + 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, + 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, + 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, + 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, + 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, + 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, + 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, + 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, + 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, + 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, + 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, + 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc +}; diff --git a/trunk/sound/oss/gus_midi.c b/trunk/sound/oss/gus_midi.c new file mode 100644 index 000000000000..d1997a417ad0 --- /dev/null +++ b/trunk/sound/oss/gus_midi.c @@ -0,0 +1,256 @@ +/* + * sound/oss/gus_midi.c + * + * The low level driver for the GUS Midi Interface. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to gus_midi_init() + */ + +#include +#include +#include "sound_config.h" + +#include "gus.h" +#include "gus_hw.h" + +static int midi_busy, input_opened; +static int my_dev; +static int output_used; +static volatile unsigned char gus_midi_control; +static void (*midi_input_intr) (int dev, unsigned char data); + +static unsigned char tmp_queue[256]; +extern int gus_pnp_flag; +static volatile int qlen; +static volatile unsigned char qhead, qtail; +extern int gus_base, gus_irq, gus_dma; +extern int *gus_osp; +extern spinlock_t gus_lock; + +static int GUS_MIDI_STATUS(void) +{ + return inb(u_MidiStatus); +} + +static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)) +{ + if (midi_busy) + { +/* printk("GUS: Midi busy\n");*/ + return -EBUSY; + } + outb((MIDI_RESET), u_MidiControl); + gus_delay(); + + gus_midi_control = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) + if (!gus_pnp_flag) + { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + outb((gus_midi_control), u_MidiControl); /* Enable */ + + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + midi_input_intr = input; + + return 0; +} + +static int dump_to_midi(unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; + + output_used = 1; + + spin_lock_irqsave(&gus_lock, flags); + + if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) + { + ok = 1; + outb((midi_byte), u_MidiData); + } + else + { + /* + * Enable Midi xmit interrupts (again) + */ + gus_midi_control |= MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + } + + spin_unlock_irqrestore(&gus_lock,flags); + return ok; +} + +static void gus_midi_close(int dev) +{ + /* + * Reset FIFO pointers, disable intrs + */ + + outb((MIDI_RESET), u_MidiControl); + midi_busy = 0; +} + +static int gus_midi_out(int dev, unsigned char midi_byte) +{ + unsigned long flags; + + /* + * Drain the local queue first + */ + spin_lock_irqsave(&gus_lock, flags); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + spin_unlock_irqrestore(&gus_lock,flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; /* + * OK + */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* + * Local queue full + */ + spin_lock_irqsave(&gus_lock, flags); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + spin_unlock_irqrestore(&gus_lock,flags); + return 1; +} + +static int gus_midi_start_read(int dev) +{ + return 0; +} + +static int gus_midi_end_read(int dev) +{ + return 0; +} + +static void gus_midi_kick(int dev) +{ +} + +static int gus_midi_buffer_status(int dev) +{ + unsigned long flags; + + if (!output_used) + return 0; + + spin_lock_irqsave(&gus_lock, flags); + + if (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + spin_unlock_irqrestore(&gus_lock,flags); + return (qlen > 0) || !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); +} + +#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct midi_operations gus_midi_operations = +{ + .owner = THIS_MODULE, + .info = {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, + .converter = &std_midi_synth, + .in_info = {0}, + .open = gus_midi_open, + .close = gus_midi_close, + .outputc = gus_midi_out, + .start_read = gus_midi_start_read, + .end_read = gus_midi_end_read, + .kick = gus_midi_kick, + .buffer_status = gus_midi_buffer_status, +}; + +void __init gus_midi_init(struct address_info *hw_config) +{ + int dev = sound_alloc_mididev(); + + if (dev == -1) + { + printk(KERN_INFO "gus_midi: Too many midi devices detected\n"); + return; + } + outb((MIDI_RESET), u_MidiControl); + + std_midi_synth.midi_dev = my_dev = dev; + hw_config->slots[2] = dev; + midi_devs[dev] = &gus_midi_operations; + sequencer_init(); + return; +} + +void gus_midi_interrupt(int dummy) +{ + volatile unsigned char stat, data; + int timeout = 10; + + spin_lock(&gus_lock); + + while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) + { + if (stat & MIDI_RCV_FULL) + { + data = inb(u_MidiData); + if (input_opened) + midi_input_intr(my_dev, data); + } + if (stat & MIDI_XMIT_EMPTY) + { + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + if (!qlen) + { + /* + * Disable Midi output interrupts, since no data in the buffer + */ + gus_midi_control &= ~MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + outb((gus_midi_control), u_MidiControl); + } + } + } + spin_unlock(&gus_lock); +} diff --git a/trunk/sound/oss/gus_vol.c b/trunk/sound/oss/gus_vol.c new file mode 100644 index 000000000000..6ae6924e1647 --- /dev/null +++ b/trunk/sound/oss/gus_vol.c @@ -0,0 +1,153 @@ + +/* + * gus_vol.c - Compute volume for GUS. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include "sound_config.h" + +#include "gus.h" +#include "gus_linearvol.h" + +#define GUS_VOLUME gus_wave_volume + + +extern int gus_wave_volume; + +/* + * Calculate gus volume from note velocity, main volume, expression, and + * intrinsic patch volume given in patch library. Expression is multiplied + * in, so it emphasizes differences in note velocity, while main volume is + * added in -- I don't know whether this is right, but it seems reasonable to + * me. (In the previous stage, main volume controller messages were changed + * to expression controller messages, if they were found to be used for + * dynamic volume adjustments, so here, main volume can be assumed to be + * constant throughout a song.) + * + * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so + * we can give a big boost to very weak voices like nylon guitar and the + * basses. The normal value is 64. Strings are assigned lower values. + */ + +unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev) +{ + int i, m, n, x; + + + /* + * A voice volume of 64 is considered neutral, so adjust the main volume if + * something other than this neutral value was assigned in the patch + * library. + */ + x = 256 + 6 * (voicev - 64); + + /* + * Boost expression by voice volume above neutral. + */ + + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; + + /* + * Combine multiplicative and level components. + */ + x = vel * xpn * 6 + (voicev / 4) * x; + +#ifdef GUS_VOLUME + /* + * Further adjustment by installation-specific master volume control + * (default 60). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + /* + * Experimental support for the channel main volume + */ + + mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ + x = (x * mainv * mainv) / 16384; +#endif + + if (x < 2) + return (0); + else if (x >= 65535) + return ((15 << 8) | 255); + + /* + * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit + * mantissa m. + */ + + n = x; + i = 7; + if (n < 128) + { + while (i > 0 && n < (1 << i)) + i--; + } + else + { + while (n > 255) + { + n >>= 1; + i++; + } + } + /* + * Mantissa is part of linear volume not expressed in exponent. (This is + * not quite like real logs -- I wonder if it's right.) + */ + m = x - (1 << i); + + /* + * Adjust mantissa to 8 bits. + */ + if (m > 0) + { + if (i > 8) + m >>= i - 8; + else if (i < 8) + m <<= 8 - i; + } + return ((i << 8) + m); +} + +/* + * Volume-values are interpreted as linear values. Volume is based on the + * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) + * and the volume set by the mixer-device (default 60%). + */ + +unsigned short gus_linear_vol(int vol, int mainvol) +{ + int mixer_mainvol; + + if (vol <= 0) + vol = 0; + else if (vol >= 127) + vol = 127; + +#ifdef GUS_VOLUME + mixer_mainvol = GUS_VOLUME; +#else + mixer_mainvol = 100; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + if (mainvol <= 0) + mainvol = 0; + else if (mainvol >= 127) + mainvol = 127; +#else + mainvol = 127; +#endif + return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; +} diff --git a/trunk/sound/oss/gus_wave.c b/trunk/sound/oss/gus_wave.c new file mode 100644 index 000000000000..de10cedee1c7 --- /dev/null +++ b/trunk/sound/oss/gus_wave.c @@ -0,0 +1,3463 @@ +/* + * sound/oss/gus_wave.c + * + * Driver for the Gravis UltraSound wave table synth. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * Bartlomiej Zolnierkiewicz : added some __init/__exit + */ + +#include +#include + +#define GUSPNP_AUTODETECT + +#include "sound_config.h" +#include + +#include "gus.h" +#include "gus_hw.h" + +#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) + +#define MAX_SAMPLE 150 +#define MAX_PATCH 256 + +#define NOT_SAMPLE 0xffff + +struct voice_info +{ + unsigned long orig_freq; + unsigned long current_freq; + unsigned long mode; + int fixed_pitch; + int bender; + int bender_range; + int panning; + int midi_volume; + unsigned int initial_volume; + unsigned int current_volume; + int loop_irq_mode, loop_irq_parm; +#define LMODE_FINISH 1 +#define LMODE_PCM 2 +#define LMODE_PCM_STOP 3 + int volume_irq_mode, volume_irq_parm; +#define VMODE_HALT 1 +#define VMODE_ENVELOPE 2 +#define VMODE_START_NOTE 3 + + int env_phase; + unsigned char env_rate[6]; + unsigned char env_offset[6]; + + /* + * Volume computation parameters for gus_adagio_vol() + */ + int main_vol, expression_vol, patch_vol; + + /* Variables for "Ultraclick" removal */ + int dev_pending, note_pending, volume_pending, + sample_pending; + char kill_pending; + long offset_pending; + +}; + +static struct voice_alloc_info *voice_alloc; +static struct address_info *gus_hw_config; +extern int gus_base; +extern int gus_irq, gus_dma; +extern int gus_pnp_flag; +extern int gus_no_wave_dma; +static int gus_dma2 = -1; +static int dual_dma_mode; +static long gus_mem_size; +static long free_mem_ptr; +static int gus_busy; +static int gus_no_dma; +static int nr_voices; +static int gus_devnum; +static int volume_base, volume_scale, volume_method; +static int gus_recmask = SOUND_MASK_MIC; +static int recording_active; +static int only_read_access; +static int only_8_bits; + +static int iw_mode = 0; +int gus_wave_volume = 60; +int gus_pcm_volume = 80; +int have_gus_max = 0; +static int gus_line_vol = 100, gus_mic_vol; +static unsigned char mix_image = 0x00; + +int gus_timer_enabled = 0; + +/* + * Current version of this driver doesn't allow synth and PCM functions + * at the same time. The active_device specifies the active driver + */ + +static int active_device; + +#define GUS_DEV_WAVE 1 /* Wave table synth */ +#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ + +static int gus_audio_speed; +static int gus_audio_channels; +static int gus_audio_bits; +static int gus_audio_bsize; +static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */ + +static DECLARE_WAIT_QUEUE_HEAD(dram_sleeper); + +/* + * Variables and buffers for PCM output + */ + +#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */ + +static int pcm_bsize, pcm_nblk, pcm_banksize; +static int pcm_datasize[MAX_PCM_BUFFERS]; +static volatile int pcm_head, pcm_tail, pcm_qlen; +static volatile int pcm_active; +static volatile int dma_active; +static int pcm_opened; +static int pcm_current_dev; +static int pcm_current_block; +static unsigned long pcm_current_buf; +static int pcm_current_count; +static int pcm_current_intrflag; +DEFINE_SPINLOCK(gus_lock); + +extern int *gus_osp; + +static struct voice_info voices[32]; + +static int freq_div_table[] = +{ + 44100, /* 14 */ + 41160, /* 15 */ + 38587, /* 16 */ + 36317, /* 17 */ + 34300, /* 18 */ + 32494, /* 19 */ + 30870, /* 20 */ + 29400, /* 21 */ + 28063, /* 22 */ + 26843, /* 23 */ + 25725, /* 24 */ + 24696, /* 25 */ + 23746, /* 26 */ + 22866, /* 27 */ + 22050, /* 28 */ + 21289, /* 29 */ + 20580, /* 30 */ + 19916, /* 31 */ + 19293 /* 32 */ +}; + +static struct patch_info *samples; +static long sample_ptrs[MAX_SAMPLE + 1]; +static int sample_map[32]; +static int free_sample; +static int mixer_type; + + +static int patch_table[MAX_PATCH]; +static int patch_map[32]; + +static struct synth_info gus_info = { + "Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, + 0, 16, 0, MAX_PATCH +}; + +static void gus_poke(long addr, unsigned char data); +static void compute_and_set_volume(int voice, int volume, int ramp_time); +extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); +extern unsigned short gus_linear_vol(int vol, int mainvol); +static void compute_volume(int voice, int volume); +static void do_volume_irq(int voice); +static void set_input_volumes(void); +static void gus_tmr_install(int io_base); + +#define INSTANT_RAMP -1 /* Instant change. No ramping */ +#define FAST_RAMP 0 /* Fastest possible ramp */ + +static void reset_sample_memory(void) +{ + int i; + + for (i = 0; i <= MAX_SAMPLE; i++) + sample_ptrs[i] = -1; + for (i = 0; i < 32; i++) + sample_map[i] = -1; + for (i = 0; i < 32; i++) + patch_map[i] = -1; + + gus_poke(0, 0); /* Put a silent sample to the beginning */ + gus_poke(1, 0); + free_mem_ptr = 2; + + free_sample = 0; + + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = NOT_SAMPLE; +} + +void gus_delay(void) +{ + int i; + + for (i = 0; i < 7; i++) + inb(u_DRAMIO); +} + +static void gus_poke(long addr, unsigned char data) +{ /* Writes a byte to the DRAM */ + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); + + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + outb((data), u_DRAMIO); +} + +static unsigned char gus_peek(long addr) +{ /* Reads a byte from the DRAM */ + unsigned char tmp; + + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); + + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + tmp = inb(u_DRAMIO); + + return tmp; +} + +void gus_write8(int reg, unsigned int data) +{ /* Writes to an indirect register (8 bit) */ + outb((reg), u_Command); + outb(((unsigned char) (data & 0xff)), u_DataHi); +} + +static unsigned char gus_read8(int reg) +{ + /* Reads from an indirect register (8 bit). Offset 0x80. */ + unsigned char val; + + outb((reg | 0x80), u_Command); + val = inb(u_DataHi); + + return val; +} + +static unsigned char gus_look8(int reg) +{ + /* Reads from an indirect register (8 bit). No additional offset. */ + unsigned char val; + + outb((reg), u_Command); + val = inb(u_DataHi); + + return val; +} + +static void gus_write16(int reg, unsigned int data) +{ + /* Writes to an indirect register (16 bit) */ + outb((reg), u_Command); + + outb(((unsigned char) (data & 0xff)), u_DataLo); + outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); +} + +static unsigned short gus_read16(int reg) +{ + /* Reads from an indirect register (16 bit). Offset 0x80. */ + unsigned char hi, lo; + + outb((reg | 0x80), u_Command); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + return ((hi << 8) & 0xff00) | lo; +} + +static unsigned short gus_look16(int reg) +{ + /* Reads from an indirect register (16 bit). No additional offset. */ + unsigned char hi, lo; + + outb((reg), u_Command); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + return ((hi << 8) & 0xff00) | lo; +} + +static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit) +{ + /* Writes an 24 bit memory address */ + unsigned long hold_address; + + if (is16bit) + { + if (iw_mode) + { + /* Interwave spesific address translations */ + address >>= 1; + } + else + { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + } + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ + gus_delay(); + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); +} + +static void gus_select_voice(int voice) +{ + if (voice < 0 || voice > 31) + return; + outb((voice), u_Voice); +} + +static void gus_select_max_voices(int nvoices) +{ + if (iw_mode) + nvoices = 32; + if (nvoices < 14) + nvoices = 14; + if (nvoices > 32) + nvoices = 32; + + voice_alloc->max_voice = nr_voices = nvoices; + gus_write8(0x0e, (nvoices - 1) | 0xc0); +} + +static void gus_voice_on(unsigned int mode) +{ + gus_write8(0x00, (unsigned char) (mode & 0xfc)); + gus_delay(); + gus_write8(0x00, (unsigned char) (mode & 0xfc)); +} + +static void gus_voice_off(void) +{ + gus_write8(0x00, gus_read8(0x00) | 0x03); +} + +static void gus_voice_mode(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x00, (gus_read8(0x00) & 0x03) | + (mode & 0xfc)); /* Don't touch last two bits */ + gus_delay(); + gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc)); +} + +static void gus_voice_freq(unsigned long freq) +{ + unsigned long divisor = freq_div_table[nr_voices - 14]; + unsigned short fc; + + /* Interwave plays at 44100 Hz with any number of voices */ + if (iw_mode) + fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); + else + fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; + + gus_write16(0x01, fc); +} + +static void gus_voice_volume(unsigned int vol) +{ + gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16(0x09, (unsigned short) (vol << 4)); +} + +static void gus_voice_balance(unsigned int balance) +{ + gus_write8(0x0c, (unsigned char) (balance & 0xff)); +} + +static void gus_ramp_range(unsigned int low, unsigned int high) +{ + gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff)); + gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff)); +} + +static void gus_ramp_rate(unsigned int scale, unsigned int rate) +{ + gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); +} + +static void gus_rampon(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x0d, mode & 0xfc); + gus_delay(); + gus_write8(0x0d, mode & 0xfc); +} + +static void gus_ramp_mode(unsigned int m) +{ + unsigned char mode = (unsigned char) (m & 0xff); + + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | + (mode & 0xfc)); /* Leave the last 2 bits alone */ + gus_delay(); + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc)); +} + +static void gus_rampoff(void) +{ + gus_write8(0x0d, 0x03); +} + +static void gus_set_voice_pos(int voice, long position) +{ + int sample_no; + + if ((sample_no = sample_map[voice]) != -1) { + if (position < samples[sample_no].len) { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].offset_pending = position; + else + gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, + samples[sample_no].mode & WAVE_16_BITS); + } + } +} + +static void gus_voice_init(int voice) +{ + unsigned long flags; + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_volume(0); + gus_voice_off(); + gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ + gus_write8(0x00, 0x03); /* Voice off */ + gus_write8(0x0d, 0x03); /* Ramping off */ + voice_alloc->map[voice] = 0; + voice_alloc->alloc_times[voice] = 0; + spin_unlock_irqrestore(&gus_lock,flags); + +} + +static void gus_voice_init2(int voice) +{ + voices[voice].panning = 0; + voices[voice].mode = 0; + voices[voice].orig_freq = 20000; + voices[voice].current_freq = 20000; + voices[voice].bender = 0; + voices[voice].bender_range = 200; + voices[voice].initial_volume = 0; + voices[voice].current_volume = 0; + voices[voice].loop_irq_mode = 0; + voices[voice].loop_irq_parm = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].volume_irq_parm = 0; + voices[voice].env_phase = 0; + voices[voice].main_vol = 127; + voices[voice].patch_vol = 127; + voices[voice].expression_vol = 127; + voices[voice].sample_pending = -1; + voices[voice].fixed_pitch = 0; +} + +static void step_envelope(int voice) +{ + unsigned vol, prev_vol, phase; + unsigned char rate; + unsigned long flags; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) + { + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_rampoff(); + spin_unlock_irqrestore(&gus_lock,flags); + return; + /* + * Sustain phase begins. Continue envelope after receiving note off. + */ + } + if (voices[voice].env_phase >= 5) + { + /* Envelope finished. Shoot the voice down */ + gus_voice_init(voice); + return; + } + prev_vol = voices[voice].current_volume; + phase = ++voices[voice].env_phase; + compute_volume(voice, voices[voice].midi_volume); + vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; + rate = voices[voice].env_rate[phase]; + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + + gus_voice_volume(prev_vol); + + + gus_write8(0x06, rate); /* Ramping rate */ + + voices[voice].volume_irq_mode = VMODE_ENVELOPE; + + if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + { + spin_unlock_irqrestore(&gus_lock,flags); + step_envelope(voice); /* Continue the envelope on the next step */ + return; + } + if (vol > prev_vol) + { + if (vol >= (4096 - 64)) + vol = 4096 - 65; + gus_ramp_range(0, vol); + gus_rampon(0x20); /* Increasing volume, with IRQ */ + } + else + { + if (vol <= 64) + vol = 65; + gus_ramp_range(vol, 4030); + gus_rampon(0x60); /* Decreasing volume, with IRQ */ + } + voices[voice].current_volume = vol; + spin_unlock_irqrestore(&gus_lock,flags); +} + +static void init_envelope(int voice) +{ + voices[voice].env_phase = -1; + voices[voice].current_volume = 64; + + step_envelope(voice); +} + +static void start_release(int voice) +{ + if (gus_read8(0x00) & 0x03) + return; /* Voice already stopped */ + + voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ + + voices[voice].current_volume = voices[voice].initial_volume = + gus_read16(0x09) >> 4; /* Get current volume */ + + voices[voice].mode &= ~WAVE_SUSTAIN_ON; + gus_rampoff(); + step_envelope(voice); +} + +static void gus_voice_fade(int voice) +{ + int instr_no = sample_map[voice], is16bits; + unsigned long flags; + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + + if (instr_no < 0 || instr_no > MAX_SAMPLE) + { + gus_write8(0x00, 0x03); /* Hard stop */ + voice_alloc->map[voice] = 0; + spin_unlock_irqrestore(&gus_lock,flags); + return; + } + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + start_release(voice); + spin_unlock_irqrestore(&gus_lock,flags); + return; + } + /* + * Ramp the volume down but not too quickly. + */ + if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + spin_unlock_irqrestore(&gus_lock,flags); + return; + } + gus_ramp_range(65, 4030); + gus_ramp_rate(2, 4); + gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */ + voices[voice].volume_irq_mode = VMODE_HALT; + spin_unlock_irqrestore(&gus_lock,flags); +} + +static void gus_reset(void) +{ + int i; + + gus_select_max_voices(24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); /* Turn voice off */ + gus_voice_init2(i); + } +} + +static void gus_initialize(void) +{ + unsigned long flags; + unsigned char dma_image, irq_image, tmp; + + static unsigned char gus_irq_map[16] = { + 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 + }; + + static unsigned char gus_dma_map[8] = { + 0, 1, 0, 2, 0, 3, 4, 5 + }; + + spin_lock_irqsave(&gus_lock,flags); + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + + /* + * Clear all interrupts + */ + + gus_write8(0x41, 0); /* DMA control */ + gus_write8(0x45, 0); /* Timer control */ + gus_write8(0x49, 0); /* Sample control */ + + gus_select_max_voices(24); + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_reset(); /* Resets all voices */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + + /* + * Set up for Digital ASIC + */ + + outb((0x05), gus_base + 0x0f); + + mix_image |= 0x02; /* Disable line out (for a moment) */ + outb((mix_image), u_Mixer); + + outb((0x00), u_IRQDMAControl); + + outb((0x00), gus_base + 0x0f); + + /* + * Now set up the DMA and IRQ interface + * + * The GUS supports two IRQs and two DMAs. + * + * Just one DMA channel is used. This prevents simultaneous ADC and DAC. + * Adding this support requires significant changes to the dmabuf.c, dsp.c + * and audio.c also. + */ + + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!gus_pnp_flag && !tmp) + printk(KERN_WARNING "Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + + dual_dma_mode = 1; + if (gus_dma2 == gus_dma || gus_dma2 == -1) + { + dual_dma_mode = 0; + dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + dma_image |= tmp; + } + else + { + /* Setup dual DMA channel mode for GUS MAX */ + + dma_image = gus_dma_map[gus_dma]; + if (!dma_image) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + tmp = gus_dma_map[gus_dma2] << 3; + if (!tmp) + { + printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n"); + tmp = 0x40; /* Combine DMA channels */ + dual_dma_mode = 0; + } + dma_image |= tmp; + } + + /* + * For some reason the IRQ and DMA addresses must be written twice + */ + + /* + * Doing it first time + */ + + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */ + + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ + + /* + * Doing it second time + */ + + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image), u_IRQDMAControl); /* Set DMA address */ + + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + outb((mix_image), u_Mixer); /* + * Turn mixer channels on + * Note! Mic in is left off. + */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + + gus_read8(0x0f); /* Clear pending IRQs */ + + if (iw_mode) + gus_write8(0x19, gus_read8(0x19) | 0x01); + spin_unlock_irqrestore(&gus_lock,flags); +} + + +static void __init pnp_mem_init(void) +{ +#include "iwmem.h" +#define CHUNK_SIZE (256*1024) +#define BANK_SIZE (4*1024*1024) +#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE) + + int bank, chunk, addr, total = 0; + int bank_sizes[4]; + int i, j, bits = -1, testbits = -1, nbanks = 0; + + /* + * This routine determines what kind of RAM is installed in each of the four + * SIMM banks and configures the DRAM address decode logic accordingly. + */ + + /* + * Place the chip into enhanced mode + */ + gus_write8(0x19, gus_read8(0x19) | 0x01); + gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */ + + /* + * Set memory configuration to 4 DRAM banks of 4M in each (16M total). + */ + + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c); + + /* + * Perform the DRAM size detection for each bank individually. + */ + for (bank = 0; bank < 4; bank++) + { + int size = 0; + + addr = bank * BANK_SIZE; + + /* Clean check points of each chunk */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + + /* Write a value to each chunk point and verify the result */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA); + + if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 && + gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA) + { + /* OK. There is RAM. Now check for possible shadows */ + int ok = 1, chunk2; + + for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) + if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) || + gus_peek(addr + chunk2 * CHUNK_SIZE + 1L)) + ok = 0; /* Addressing wraps */ + + if (ok) + size = (chunk + 1) * CHUNK_SIZE; + } + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + bank_sizes[bank] = size; + if (size) + nbanks = bank + 1; + DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); + } + + if (nbanks == 0) /* No RAM - Give up */ + { + printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n"); + printk(KERN_ERR "Sound: Unable to work with this card.\n"); + gus_write8(0x19, gus_read8(0x19) & ~0x01); + gus_mem_size = 0; + return; + } + + /* + * Now we know how much DRAM there is in each bank. The next step is + * to find a DRAM size encoding (0 to 12) which is best for the combination + * we have. + * + * First try if any of the possible alternatives matches exactly the amount + * of memory we have. + */ + + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < 4; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + } + + /* + * If necessary, try to find a combination where other than the last + * bank matches our configuration and the last bank is left oversized. + * In this way we don't leave holes in the middle of memory. + */ + + if (bits == -1) /* No luck yet */ + { + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) + bits = -1; /* The last bank is too small */ + } + } + /* + * The last resort is to search for a combination where the banks are + * smaller than the actual SIMMs. This leaves some memory in the banks + * unused but doesn't leave holes in the DRAM address space. + */ + if (bits == -1) /* No luck yet */ + { + for (i = 0; i < 13; i++) + { + testbits = i; + for (j = 0; testbits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] > bank_sizes[j]) { + testbits = -1; + } + if(testbits > bits) bits = testbits; + } + if (bits != -1) + { + printk(KERN_INFO "Interwave: Can't use all installed RAM.\n"); + printk(KERN_INFO "Interwave: Try reordering SIMMS.\n"); + } + printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n"); + printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); + bits = 0; + } + DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits)); + + for (bank = 0; bank < 4; bank++) + { + DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); + + if (bank_sizes[bank] > mem_decode[bits][bank]) + total += mem_decode[bits][bank]; + else + total += bank_sizes[bank]; + } + + DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024)); + + /* + * Set the memory addressing mode. + */ + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits); + +/* Leave the chip into enhanced mode. Disable LFO */ + gus_mem_size = total; + iw_mode = 1; + gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02); +} + +int __init gus_wave_detect(int baseaddr) +{ + unsigned long i, max_mem = 1024L; + unsigned long loc; + unsigned char val; + + if (!request_region(baseaddr, 16, "GUS")) + return 0; + if (!request_region(baseaddr + 0x100, 12, "GUS")) { /* 0x10c-> is MAX */ + release_region(baseaddr, 16); + return 0; + } + + gus_base = baseaddr; + + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + +#ifdef GUSPNP_AUTODETECT + val = gus_look8(0x5b); /* Version number register */ + gus_write8(0x5b, ~val); /* Invert all bits */ + + if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */ + { + if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ + { + DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); + gus_pnp_flag = 1; + } + else + { + DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b))); + gus_pnp_flag = 0; + } + } + gus_write8(0x5b, val); /* Restore all bits */ +#endif + + if (gus_pnp_flag) + pnp_mem_init(); + if (iw_mode) + return 1; + + /* See if there is first block there.... */ + gus_poke(0L, 0xaa); + if (gus_peek(0L) != 0xaa) { + release_region(baseaddr + 0x100, 12); + release_region(baseaddr, 16); + return 0; + } + + /* Now zero it out so that I can check for mirroring .. */ + gus_poke(0L, 0x00); + for (i = 1L; i < max_mem; i++) + { + int n, failed; + + /* check for mirroring ... */ + if (gus_peek(0L) != 0) + break; + loc = i << 10; + + for (n = loc - 1, failed = 0; n <= loc; n++) + { + gus_poke(loc, 0xaa); + if (gus_peek(loc) != 0xaa) + failed = 1; + gus_poke(loc, 0x55); + if (gus_peek(loc) != 0x55) + failed = 1; + } + if (failed) + break; + } + gus_mem_size = i << 10; + return 1; +} + +static int guswave_ioctl(int dev, unsigned int cmd, void __user *arg) +{ + + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + if (copy_to_user(arg, &gus_info, sizeof(gus_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory(); + return 0; + + case SNDCTL_SEQ_PERCMODE: + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32; + + default: + return -EINVAL; + } +} + +static int guswave_set_instr(int dev, int voice, int instr_no) +{ + int sample_no; + + if (instr_no < 0 || instr_no > MAX_PATCH) + instr_no = 0; /* Default to acoustic piano */ + + if (voice < 0 || voice > 31) + return -EINVAL; + + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].sample_pending = instr_no; + return 0; + } + sample_no = patch_table[instr_no]; + patch_map[voice] = -1; + + if (sample_no == NOT_SAMPLE) + { +/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ + return -EINVAL; /* Patch not defined */ + } + if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + { +/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ + return -EINVAL; + } + sample_map[voice] = sample_no; + patch_map[voice] = instr_no; + return 0; +} + +static int guswave_kill_note(int dev, int voice, int note, int velocity) +{ + unsigned long flags; + + spin_lock_irqsave(&gus_lock,flags); + /* voice_alloc->map[voice] = 0xffff; */ + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].kill_pending = 1; + spin_unlock_irqrestore(&gus_lock,flags); + } + else + { + spin_unlock_irqrestore(&gus_lock,flags); + gus_voice_fade(voice); + } + + return 0; +} + +static void guswave_aftertouch(int dev, int voice, int pressure) +{ +} + +static void guswave_panning(int dev, int voice, int value) +{ + if (voice >= 0 || voice < 32) + voices[voice].panning = value; +} + +static void guswave_volume_method(int dev, int mode) +{ + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; +} + +static void compute_volume(int voice, int volume) +{ + if (volume < 128) + voices[voice].midi_volume = volume; + + switch (volume_method) + { + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; + + case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ + voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); + break; + + default: + voices[voice].initial_volume = volume_base + + (voices[voice].midi_volume * volume_scale); + } + + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; +} + +static void compute_and_set_volume(int voice, int volume, int ramp_time) +{ + int curr, target, rate; + unsigned long flags; + + compute_volume(voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; + + spin_lock_irqsave(&gus_lock,flags); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ + + gus_select_voice(voice); + + curr = gus_read16(0x09) >> 4; + target = voices[voice].initial_volume; + + if (ramp_time == INSTANT_RAMP) + { + gus_rampoff(); + gus_voice_volume(target); + spin_unlock_irqrestore(&gus_lock,flags); + return; + } + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate(0, rate); + + if ((target - curr) / 64 == 0) /* Close enough to target. */ + { + gus_rampoff(); + gus_voice_volume(target); + spin_unlock_irqrestore(&gus_lock,flags); + return; + } + if (target > curr) + { + if (target > (4095 - 65)) + target = 4095 - 65; + gus_ramp_range(curr, target); + gus_rampon(0x00); /* Ramp up, once, no IRQ */ + } + else + { + if (target < 65) + target = 65; + + gus_ramp_range(target, curr); + gus_rampon(0x40); /* Ramp down, once, no irq */ + } + spin_unlock_irqrestore(&gus_lock,flags); +} + +static void dynamic_volume_change(int voice) +{ + unsigned char status; + unsigned long flags; + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + status = gus_read8(0x00); /* Get voice status */ + spin_unlock_irqrestore(&gus_lock,flags); + + if (status & 0x03) + return; /* Voice was not running */ + + if (!(voices[voice].mode & WAVE_ENVELOPES)) + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + + /* + * Voice is running and has envelopes. + */ + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + status = gus_read8(0x0d); /* Ramping status */ + spin_unlock_irqrestore(&gus_lock,flags); + + if (status & 0x03) /* Sustain phase? */ + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + if (voices[voice].env_phase < 0) + return; + + compute_volume(voice, voices[voice].midi_volume); + +} + +static void guswave_controller(int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + unsigned long freq; + + if (voice < 0 || voice > 31) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + { + freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_freq(freq); + spin_unlock_irqrestore(&gus_lock,flags); + } + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + if (volume_method == VOL_METHOD_ADAGIO) + { + voices[voice].expression_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + } + break; + + case CTL_PAN: + voices[voice].panning = (value * 2) - 128; + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + voices[voice].main_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + break; + + default: + break; + } +} + +static int guswave_start_note2(int dev, int voice, int note_num, int volume) +{ + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + unsigned long note_freq, base_note, freq, flags; + unsigned char mode = 0; + + if (voice < 0 || voice > 31) + { +/* printk("GUS: Invalid voice\n");*/ + return -EINVAL; + } + if (note_num == 255) + { + if (voices[voice].mode & WAVE_ENVELOPES) + { + voices[voice].midi_volume = volume; + dynamic_volume_change(voice); + return 0; + } + compute_and_set_volume(voice, volume, 1); + return 0; + } + if ((patch = patch_map[voice]) == -1) + return -EINVAL; + if ((samplep = patch_table[patch]) == NOT_SAMPLE) + { + return -EINVAL; + } + note_freq = note_to_freq(note_num); + + /* + * Find a sample within a patch so that the note_freq is between low_note + * and high_note. + */ + sample = -1; + + best_sample = samplep; + best_delta = 1000000; + while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) + { + delta_freq = note_freq - samples[samplep].base_note; + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = samplep; + best_delta = delta_freq; + } + if (samples[samplep].low_note <= note_freq && + note_freq <= samples[samplep].high_note) + { + sample = samplep; + } + else + samplep = samples[samplep].key; /* Link to next sample */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) + { +/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ + return 0; /* Should play default patch ??? */ + } + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; + voices[voice].mode = samples[sample].mode; + voices[voice].patch_vol = samples[sample].volume; + + if (iw_mode) + gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ + + if (voices[voice].mode & WAVE_ENVELOPES) + { + int i; + + for (i = 0; i < 6; i++) + { + voices[voice].env_rate[i] = samples[sample].env_rate[i]; + voices[voice].env_offset[i] = samples[sample].env_offset[i]; + } + } + sample_map[voice] = sample; + + if (voices[voice].fixed_pitch) /* Fixed pitch */ + { + freq = samples[sample].base_freq; + } + else + { + base_note = samples[sample].base_note / 100; + note_freq /= 100; + + freq = samples[sample].base_freq * note_freq / base_note; + } + + voices[voice].orig_freq = freq; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, + voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + pan = (samples[sample].panning + voices[voice].panning) / 32; + pan += 7; + if (pan < 0) + pan = 0; + if (pan > 15) + pan = 15; + + if (samples[sample].mode & WAVE_16_BITS) + { + mode |= 0x04; /* 16 bits */ + if ((sample_ptrs[sample] / GUS_BANK_SIZE) != + ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) + printk(KERN_ERR "GUS: Sample address error\n"); + } + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_off(); + gus_rampoff(); + + spin_unlock_irqrestore(&gus_lock,flags); + + if (voices[voice].mode & WAVE_ENVELOPES) + { + compute_volume(voice, volume); + init_envelope(voice); + } + else + { + compute_and_set_volume(voice, volume, 0); + } + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + + if (samples[sample].mode & WAVE_LOOP_BACK) + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - + voices[voice].offset_pending, 0, is16bits); /* start=end */ + else + gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ + + if (samples[sample].mode & WAVE_LOOPING) + { + mode |= 0x08; + + if (samples[sample].mode & WAVE_BIDIR_LOOP) + mode |= 0x10; + + if (samples[sample].mode & WAVE_LOOP_BACK) + { + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - + voices[voice].offset_pending, + (samples[sample].fractions >> 4) & 0x0f, is16bits); + mode |= 0x40; + } + gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, + samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + else + { + mode |= 0x20; /* Loop IRQ at the end */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ + voices[voice].loop_irq_parm = 1; + gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + gus_voice_freq(freq); + gus_voice_balance(pan); + gus_voice_on(mode); + spin_unlock_irqrestore(&gus_lock,flags); + + return 0; +} + +/* + * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking + * when the note playing on the voice is changed. It uses volume + * ramping. + */ + +static int guswave_start_note(int dev, int voice, int note_num, int volume) +{ + unsigned long flags; + int mode; + int ret_val = 0; + + spin_lock_irqsave(&gus_lock,flags); + if (note_num == 255) + { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].volume_pending = volume; + } + else + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + } + else + { + gus_select_voice(voice); + mode = gus_read8(0x00); + if (mode & 0x20) + gus_write8(0x00, mode & 0xdf); /* No interrupt! */ + + voices[voice].offset_pending = 0; + voices[voice].kill_pending = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].loop_irq_mode = 0; + + if (voices[voice].sample_pending >= 0) + { + spin_unlock_irqrestore(&gus_lock,flags); /* Run temporarily with interrupts enabled */ + guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); + voices[voice].sample_pending = -1; + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); /* Reselect the voice (just to be sure) */ + } + if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + else + { + voices[voice].dev_pending = dev; + voices[voice].note_pending = note_num; + voices[voice].volume_pending = volume; + voices[voice].volume_irq_mode = VMODE_START_NOTE; + + gus_rampoff(); + gus_ramp_range(2000, 4065); + gus_ramp_rate(0, 63); /* Fastest possible rate */ + gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ + } + } + spin_unlock_irqrestore(&gus_lock,flags); + return ret_val; +} + +static void guswave_reset(int dev) +{ + int i; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); + gus_voice_init2(i); + } +} + +static int guswave_open(int dev, int mode) +{ + int err; + + if (gus_busy) + return -EBUSY; + + voice_alloc->timestamp = 0; + + if (gus_no_wave_dma) { + gus_no_dma = 1; + } else { + if ((err = DMAbuf_open_dma(gus_devnum)) < 0) + { + /* printk( "GUS: Loading samples without DMA\n"); */ + gus_no_dma = 1; /* Upload samples using PIO */ + } + else + gus_no_dma = 0; + } + + init_waitqueue_head(&dram_sleeper); + gus_busy = 1; + active_device = GUS_DEV_WAVE; + + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + gus_initialize(); + gus_reset(); + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + + return 0; +} + +static void guswave_close(int dev) +{ + gus_busy = 0; + active_device = 0; + gus_reset(); + + if (!gus_no_dma) + DMAbuf_close_dma(gus_devnum); +} + +static int guswave_load_patch(int dev, int format, const char __user *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + long sizeof_patch; + + unsigned long blk_sz, blk_end, left, src_offs, target; + + sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ + + if (format != GUS_PATCH) + { +/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < sizeof_patch) + { +/* printk("GUS Error: Patch header too short\n");*/ + return -EINVAL; + } + count -= sizeof_patch; + + if (free_sample >= MAX_SAMPLE) + { +/* printk("GUS: Sample table full\n");*/ + return -ENOSPC; + } + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + if (copy_from_user(&((char *) &patch)[offs], &(addr)[offs], + sizeof_patch - offs)) + return -EFAULT; + + if (patch.mode & WAVE_ROM) + return -EINVAL; + if (gus_mem_size == 0) + return -ENOSPC; + + instr = patch.instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { +/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/ + return -EINVAL; + } + if (count < patch.len) + { +/* printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/ + patch.len = count; + } + if (patch.len <= 0 || patch.len > gus_mem_size) + { +/* printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/ + return -EINVAL; + } + if (patch.mode & WAVE_LOOPING) + { + if (patch.loop_start < 0 || patch.loop_start >= patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop start\n");*/ + return -EINVAL; + } + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop end\n");*/ + return -EINVAL; + } + } + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ + + if (patch.mode & WAVE_16_BITS) + { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) + { +/* printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/ + return -ENOSPC; + } + if ((free_mem_ptr / GUS_BANK_SIZE) != + ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + { + unsigned long tmp_mem = + /* Align to 256K */ + ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; + + if ((tmp_mem + patch.len) > gus_mem_size) + return -ENOSPC; + + free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + } + } + if ((free_mem_ptr + patch.len) > gus_mem_size) + return -ENOSPC; + + sample_ptrs[free_sample] = free_mem_ptr; + + /* + * Tremolo is not possible with envelopes + */ + + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; + + if (!(patch.mode & WAVE_FRACTIONS)) + { + patch.fractions = 0; + } + memcpy((char *) &samples[free_sample], &patch, sizeof_patch); + + /* + * Link this_one sample to the list of samples for patch 'instr'. + */ + + samples[free_sample].key = patch_table[instr]; + patch_table[instr] = free_sample; + + /* + * Use DMA to transfer the wave data to the DRAM + */ + + left = patch.len; + src_offs = 0; + target = free_mem_ptr; + + while (left) /* Not completely transferred yet */ + { + blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; + if (blk_sz > left) + blk_sz = left; + + /* + * DMA cannot cross bank (256k) boundaries. Check for that. + */ + + blk_end = target + blk_sz; + + if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE)) + { + /* Split the block */ + blk_end &= ~(GUS_BANK_SIZE - 1); + blk_sz = blk_end - target; + } + if (gus_no_dma) + { + /* + * For some reason the DMA is not possible. We have to use PIO. + */ + long i; + unsigned char data; + + for (i = 0; i < blk_sz; i++) + { + get_user(*(unsigned char *) &data, (unsigned char __user *) &((addr)[sizeof_patch + i])); + if (patch.mode & WAVE_UNSIGNED) + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* Convert to signed */ + gus_poke(target + i, data); + } + } + else + { + unsigned long address, hold_address; + unsigned char dma_command; + unsigned long flags; + + if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) + { + printk(KERN_ERR "GUS: DMA buffer == NULL\n"); + return -ENOSPC; + } + /* + * OK, move now. First in and then out. + */ + + if (copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, + &(addr)[sizeof_patch + src_offs], + blk_sz)) + return -EFAULT; + + spin_lock_irqsave(&gus_lock,flags); + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys, + blk_sz, DMA_MODE_WRITE); + + /* + * Set the DRAM address for the wave data + */ + + if (iw_mode) + { + /* Different address translation in enhanced mode */ + + unsigned char hi; + + if (gus_dma > 4) + address = target >> 1; /* Convert to 16 bit word address */ + else + address = target; + + hi = (unsigned char) ((address >> 16) & 0xf0); + hi += (unsigned char) (address & 0x0f); + + gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */ + gus_write8(0x50, hi); + } + else + { + address = target; + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + } + + /* + * Start the DMA transfer + */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + if (patch.mode & WAVE_UNSIGNED) + dma_command |= 0x80; /* Invert MSB */ + if (patch.mode & WAVE_16_BITS) + dma_command |= 0x40; /* 16 bit _DATA_ */ + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA _channel_ */ + + /* + * Sleep here until the DRAM DMA done interrupt is served + */ + active_device = GUS_DEV_WAVE; + gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */ + + spin_unlock_irqrestore(&gus_lock,flags); /* opens a race */ + if (!interruptible_sleep_on_timeout(&dram_sleeper, HZ)) + printk("GUS: DMA Transfer timed out\n"); + } + + /* + * Now the next part + */ + + left -= blk_sz; + src_offs += blk_sz; + target += blk_sz; + + gus_write8(0x41, 0); /* Stop DMA */ + } + + free_mem_ptr += patch.len; + free_sample++; + return 0; +} + +static void guswave_hw_control(int dev, unsigned char *event_rec) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned int plong; + unsigned long flags; + + cmd = event_rec[2]; + voice = event_rec[3]; + p1 = *(unsigned short *) &event_rec[4]; + p2 = *(unsigned short *) &event_rec[6]; + plong = *(unsigned int *) &event_rec[4]; + + if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && + (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) + do_volume_irq(voice); + + switch (cmd) + { + case _GUS_NUMVOICES: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_select_max_voices(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr(dev, voice, p1); + break; + + case _GUS_VOICEON: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_on(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICEOFF: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_off(); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICEFADE: + gus_voice_fade(voice); + break; + + case _GUS_VOICEMODE: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_mode(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICEBALA: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_balance(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICEFREQ: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_freq(plong); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICEVOL: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_volume(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOICEVOL2: /* Just update the software voice level */ + voices[voice].initial_volume = voices[voice].current_volume = p1; + break; + + case _GUS_RAMPRANGE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_ramp_range(p1, p2); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NJET-NJET */ + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_ramp_rate(p1, p2); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_ramp_mode(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* EI-EI */ + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_rampon(p1); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NEJ-NEJ */ + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_rampoff(); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + case _GUS_VOICE_POS: + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_set_voice_pos(voice, plong); + spin_unlock_irqrestore(&gus_lock,flags); + break; + + default: + break; + } +} + +static int gus_audio_set_speed(int speed) +{ + if (speed <= 0) + speed = gus_audio_speed; + + if (speed < 4000) + speed = 4000; + + if (speed > 44100) + speed = 44100; + + gus_audio_speed = speed; + + if (only_read_access) + { + /* Compute nearest valid recording speed and return it */ + + /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ + speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + speed = (9878400 / (speed * 16)) - 2; + } + return speed; +} + +static int gus_audio_set_channels(int channels) +{ + if (!channels) + return gus_audio_channels; + if (channels > 2) + channels = 2; + if (channels < 1) + channels = 1; + gus_audio_channels = channels; + return channels; +} + +static int gus_audio_set_bits(int bits) +{ + if (!bits) + return gus_audio_bits; + + if (bits != 8 && bits != 16) + bits = 8; + + if (only_8_bits) + bits = 8; + + gus_audio_bits = bits; + return bits; +} + +static int gus_audio_ioctl(int dev, unsigned int cmd, void __user *arg) +{ + int val; + + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int __user*)arg)) + return -EFAULT; + val = gus_audio_set_speed(val); + break; + + case SOUND_PCM_READ_RATE: + val = gus_audio_speed; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int __user *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int __user *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val); + break; + + case SOUND_PCM_READ_CHANNELS: + val = gus_audio_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int __user *)arg)) + return -EFAULT; + val = gus_audio_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + val = gus_audio_bits; + break; + + case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ + case SOUND_PCM_READ_FILTER: + val = -EINVAL; + break; + default: + return -EINVAL; + } + return put_user(val, (int __user *)arg); +} + +static void gus_audio_reset(int dev) +{ + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } +} + +static int saved_iw_mode; /* A hack hack hack */ + +static int gus_audio_open(int dev, int mode) +{ + if (gus_busy) + return -EBUSY; + + if (gus_pnp_flag && mode & OPEN_READ) + { +/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/ + return -EIO; + } + gus_initialize(); + + gus_busy = 1; + active_device = 0; + + saved_iw_mode = iw_mode; + if (iw_mode) + { + /* There are some problems with audio in enhanced mode so disable it */ + gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */ + iw_mode = 0; + } + + gus_reset(); + reset_sample_memory(); + gus_select_max_voices(14); + + pcm_active = 0; + dma_active = 0; + pcm_opened = 1; + if (mode & OPEN_READ) + { + recording_active = 1; + set_input_volumes(); + } + only_read_access = !(mode & OPEN_WRITE); + only_8_bits = mode & OPEN_READ; + if (only_8_bits) + audio_devs[dev]->format_mask = AFMT_U8; + else + audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; + + return 0; +} + +static void gus_audio_close(int dev) +{ + iw_mode = saved_iw_mode; + gus_reset(); + gus_busy = 0; + pcm_opened = 0; + active_device = 0; + + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } + recording_active = 0; +} + +static void gus_audio_update_volume(void) +{ + unsigned long flags; + int voice; + + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_audio_channels; voice++) + { + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + spin_unlock_irqrestore(&gus_lock,flags); + } +} + +static void play_next_pcm_block(void) +{ + unsigned long flags; + int speed = gus_audio_speed; + int this_one, is16bits, chn; + unsigned long dram_loc; + unsigned char mode[2], ramp_mode[2]; + + if (!pcm_qlen) + return; + + this_one = pcm_head; + + for (chn = 0; chn < gus_audio_channels; chn++) + { + mode[chn] = 0x00; + ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + + if (chn == 0) + { + mode[chn] |= 0x20; /* Loop IRQ */ + voices[chn].loop_irq_mode = LMODE_PCM; + } + if (gus_audio_bits != 8) + { + is16bits = 1; + mode[chn] |= 0x04; /* 16 bit data */ + } + else + is16bits = 0; + + dram_loc = this_one * pcm_bsize; + dram_loc += chn * pcm_banksize; + + if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ + { + mode[chn] |= 0x08; /* Enable loop */ + ramp_mode[chn] = 0x03; /* Disable rollover bit */ + } + else + { + if (chn == 0) + ramp_mode[chn] = 0x04; /* Enable rollover bit */ + } + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(chn); + gus_voice_freq(speed); + + if (gus_audio_channels == 1) + gus_voice_balance(7); /* mono */ + else if (chn == 0) + gus_voice_balance(0); /* left */ + else + gus_voice_balance(15); /* right */ + + if (!pcm_active) /* Playback not already active */ + { + /* + * The playback was not started yet (or there has been a pause). + * Start the voice (again) and ask for a rollover irq at the end of + * this_one block. If this_one one is last of the buffers, use just + * the normal loop with irq. + */ + + gus_voice_off(); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + + gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ + gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ + + if (chn != 0) + gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, + 0, is16bits); /* Loop end location */ + } + if (chn == 0) + gus_write_addr(0x04, dram_loc + pcm_bsize - 1, + 0, is16bits); /* Loop end location */ + else + mode[chn] |= 0x08; /* Enable looping */ + spin_unlock_irqrestore(&gus_lock,flags); + } + for (chn = 0; chn < gus_audio_channels; chn++) + { + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(chn); + gus_write8(0x0d, ramp_mode[chn]); + if (iw_mode) + gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ + gus_voice_on(mode[chn]); + spin_unlock_irqrestore(&gus_lock,flags); + } + pcm_active = 1; +} + +static void gus_transfer_output_block(int dev, unsigned long buf, + int total_count, int intrflag, int chn) +{ + /* + * This routine transfers one block of audio data to the DRAM. In mono mode + * it's called just once. When in stereo mode, this_one routine is called + * once for both channels. + * + * The left/mono channel data is transferred to the beginning of dram and the + * right data to the area pointed by gus_page_size. + */ + + int this_one, count; + unsigned long flags; + unsigned char dma_command; + unsigned long address, hold_address; + + spin_lock_irqsave(&gus_lock,flags); + + count = total_count / gus_audio_channels; + + if (chn == 0) + { + if (pcm_qlen >= pcm_nblk) + printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n"); + + this_one = pcm_current_block = pcm_tail; + pcm_qlen++; + pcm_tail = (pcm_tail + 1) % pcm_nblk; + pcm_datasize[this_one] = count; + } + else + this_one = pcm_current_block; + + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE); + + address = this_one * pcm_bsize; + address += chn * pcm_banksize; + + if (audio_devs[dev]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + + if (gus_audio_bits != 8) + dma_command |= 0x40; /* 16 bit _DATA_ */ + else + dma_command |= 0x80; /* Invert MSB */ + + if (audio_devs[dev]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ + + gus_write8(0x41, dma_command); /* Kick start */ + + if (chn == (gus_audio_channels - 1)) /* Last channel */ + { + /* + * Last (right or mono) channel data + */ + dma_active = 1; /* DMA started. There is a unacknowledged buffer */ + active_device = GUS_DEV_PCM_DONE; + if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize)) + { + play_next_pcm_block(); + } + } + else + { + /* + * Left channel data. The right channel + * is transferred after DMA interrupt + */ + active_device = GUS_DEV_PCM_CONTINUE; + } + + spin_unlock_irqrestore(&gus_lock,flags); +} + +static void gus_uninterleave8(char *buf, int l) +{ +/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + char *buf2 = buf + halfsize, *src = bounce_buf; + + memcpy(bounce_buf, buf, l); + + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } +} + +static void gus_uninterleave16(short *buf, int l) +{ +/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + short *buf2 = buf + halfsize, *src = (short *) bounce_buf; + + memcpy(bounce_buf, (char *) buf, l * 2); + + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } +} + +static void gus_audio_output_block(int dev, unsigned long buf, int total_count, + int intrflag) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; + + pcm_current_buf = buf; + pcm_current_count = total_count; + pcm_current_intrflag = intrflag; + pcm_current_dev = dev; + if (gus_audio_channels == 2) + { + char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); + + if (gus_audio_bits == 8) + gus_uninterleave8(b, total_count); + else + gus_uninterleave16((short *) b, total_count / 2); + } + gus_transfer_output_block(dev, buf, total_count, intrflag, 0); +} + +static void gus_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + unsigned char mode; + + spin_lock_irqsave(&gus_lock,flags); + + DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); + mode = 0xa0; /* DMA IRQ enabled, invert MSB */ + + if (audio_devs[dev]->dmap_in->dma > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_audio_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ + + gus_write8(0x49, mode); + spin_unlock_irqrestore(&gus_lock,flags); +} + +static int gus_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + unsigned int rate; + + gus_audio_bsize = bsize; + audio_devs[dev]->dmap_in->flags |= DMA_NODMA; + rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + + gus_write8(0x48, rate & 0xff); /* Set sampling rate */ + + if (gus_audio_bits != 8) + { +/* printk("GUS Error: 16 bit recording not supported\n");*/ + return -EINVAL; + } + return 0; +} + +static int gus_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + int i; + + long mem_ptr, mem_size; + + audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; + mem_ptr = 0; + mem_size = gus_mem_size / gus_audio_channels; + + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; + + pcm_bsize = bsize / gus_audio_channels; + pcm_head = pcm_tail = pcm_qlen = 0; + + pcm_nblk = 2; /* MAX_PCM_BUFFERS; */ + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; + + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; + + pcm_banksize = pcm_nblk * pcm_bsize; + + if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + gus_write8(0x41, 0); /* Disable GF1 DMA */ + return 0; +} + +static int gus_local_qlen(int dev) +{ + return pcm_qlen; +} + + +static struct audio_driver gus_audio_driver = +{ + .owner = THIS_MODULE, + .open = gus_audio_open, + .close = gus_audio_close, + .output_block = gus_audio_output_block, + .start_input = gus_audio_start_input, + .ioctl = gus_audio_ioctl, + .prepare_for_input = gus_audio_prepare_for_input, + .prepare_for_output = gus_audio_prepare_for_output, + .halt_io = gus_audio_reset, + .local_qlen = gus_local_qlen, +}; + +static void guswave_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = &synth_devs[dev]->chn_info[chn]; + + guswave_set_instr(dev, voice, info->pgm_num); + voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ + voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; + voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; + voices[voice].bender = 0; + voices[voice].bender_range = info->bender_range; + + if (chn == 9) + voices[voice].fixed_pitch = 1; +} + +static void guswave_bender(int dev, int voice, int value) +{ + int freq; + unsigned long flags; + + voices[voice].bender = value - 8192; + freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + spin_lock_irqsave(&gus_lock,flags); + gus_select_voice(voice); + gus_voice_freq(freq); + spin_unlock_irqrestore(&gus_lock,flags); +} + +static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best = -1, best_time = 0x7fffffff; + + p = alloc->ptr; + /* + * First look for a completely stopped voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0) + { + alloc->ptr = p; + return p; + } + if (alloc->alloc_times[p] < best_time) + { + best = p; + best_time = alloc->alloc_times[p]; + } + p = (p + 1) % alloc->max_voice; + } + + /* + * Then look for a releasing voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + return p; + } + p = (p + 1) % alloc->max_voice; + } + if (best >= 0) + p = best; + + alloc->ptr = p; + return p; +} + +static struct synth_operations guswave_operations = +{ + .owner = THIS_MODULE, + .id = "GUS", + .info = &gus_info, + .midi_dev = 0, + .synth_type = SYNTH_TYPE_SAMPLE, + .synth_subtype = SAMPLE_TYPE_GUS, + .open = guswave_open, + .close = guswave_close, + .ioctl = guswave_ioctl, + .kill_note = guswave_kill_note, + .start_note = guswave_start_note, + .set_instr = guswave_set_instr, + .reset = guswave_reset, + .hw_control = guswave_hw_control, + .load_patch = guswave_load_patch, + .aftertouch = guswave_aftertouch, + .controller = guswave_controller, + .panning = guswave_panning, + .volume_method = guswave_volume_method, + .bender = guswave_bender, + .alloc_voice = guswave_alloc, + .setup_voice = guswave_setup_voice +}; + +static void set_input_volumes(void) +{ + unsigned long flags; + unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ + + if (have_gus_max) /* Don't disturb GUS MAX */ + return; + + spin_lock_irqsave(&gus_lock,flags); + + /* + * Enable channels having vol > 10% + * Note! bit 0x01 means the line in DISABLED while 0x04 means + * the mic in ENABLED. + */ + if (gus_line_vol > 10) + mask &= ~0x01; + if (gus_mic_vol > 10) + mask |= 0x04; + + if (recording_active) + { + /* + * Disable channel, if not selected for recording + */ + if (!(gus_recmask & SOUND_MASK_LINE)) + mask |= 0x01; + if (!(gus_recmask & SOUND_MASK_MIC)) + mask &= ~0x04; + } + mix_image &= ~0x07; + mix_image |= mask & 0x07; + outb((mix_image), u_Mixer); + + spin_unlock_irqrestore(&gus_lock,flags); +} + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH|SOUND_MASK_PCM) + +int gus_default_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) +{ + int vol, val; + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (!access_ok(VERIFY_WRITE, arg, sizeof(int))) + return -EFAULT; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (__get_user(val, (int __user *) arg)) + return -EFAULT; + + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + gus_recmask = val & MIX_DEVS; + if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) + gus_recmask = SOUND_MASK_MIC; + /* Note! Input volumes are updated during next open for recording */ + val = gus_recmask; + break; + + case SOUND_MIXER_MIC: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_LINE: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = val & 0xff; + if (gus_pcm_volume < 0) + gus_pcm_volume = 0; + if (gus_pcm_volume > 100) + gus_pcm_volume = 100; + gus_audio_update_volume(); + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + gus_wave_volume = val & 0xff; + if (gus_wave_volume < 0) + gus_wave_volume = 0; + if (gus_wave_volume > 100) + gus_wave_volume = 100; + if (active_device == GUS_DEV_WAVE) + { + int voice; + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change(voice); /* Apply the new vol */ + } + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + else + { + switch (cmd & 0xff) + { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + val = gus_recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = gus_mic_vol | (gus_mic_vol << 8); + break; + + case SOUND_MIXER_LINE: + val = gus_line_vol | (gus_line_vol << 8); + break; + + case SOUND_MIXER_PCM: + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + return __put_user(val, (int __user *)arg); +} + +static struct mixer_operations gus_mixer_operations = +{ + .owner = THIS_MODULE, + .id = "GUS", + .name = "Gravis Ultrasound", + .ioctl = gus_default_mixer_ioctl +}; + +static int __init gus_default_mixer_init(void) +{ + int n; + + if ((n = sound_alloc_mixerdev()) != -1) + { + /* + * Don't install if there is another + * mixer + */ + mixer_devs[n] = &gus_mixer_operations; + } + if (have_gus_max) + { + /* + * Enable all mixer channels on the GF1 side. Otherwise recording will + * not be possible using GUS MAX. + */ + mix_image &= ~0x07; + mix_image |= 0x04; /* All channels enabled */ + outb((mix_image), u_Mixer); + } + return n; +} + +void __init gus_wave_init(struct address_info *hw_config) +{ + unsigned long flags; + unsigned char val; + char *model_num = "2.4"; + char tmp[64]; + int gus_type = 0x24; /* 2.4 */ + + int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; + int sdev; + + hw_config->slots[0] = -1; /* No wave */ + hw_config->slots[1] = -1; /* No ad1848 */ + hw_config->slots[4] = -1; /* No audio */ + hw_config->slots[5] = -1; /* No mixer */ + + if (!gus_pnp_flag) + { + if (irq < 0 || irq > 15) + { + printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return; + } + } + + if (dma < 0 || dma > 7 || dma == 4) + { + printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma); + return; + } + gus_irq = irq; + gus_dma = dma; + gus_dma2 = dma2; + gus_hw_config = hw_config; + + if (gus_dma2 == -1) + gus_dma2 = dma; + + /* + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + */ + + spin_lock_irqsave(&gus_lock,flags); + outb((0x20), gus_base + 0x0f); + val = inb(gus_base + 0x0f); + spin_unlock_irqrestore(&gus_lock,flags); + + if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ + { + int ad_flags = 0; + + if (gus_pnp_flag) + ad_flags = 0x12345678; /* Interwave "magic" */ + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. + */ + + if (gus_pnp_flag) /* Hack hack hack */ + val = 10; + else + val = inb(u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + request_region(u_MixSelect, 1, "GUS mixer"); + } + else + { + struct resource *ports; + ports = request_region(gus_base + 0x10c, 4, "ad1848"); + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; +#ifdef CONFIG_SOUND_GUSMAX + { + unsigned char max_config = 0x40; /* Codec enable */ + + if (gus_dma2 == -1) + gus_dma2 = gus_dma; + + if (gus_dma > 3) + max_config |= 0x10; /* 16 bit capture DMA */ + + if (gus_dma2 > 3) + max_config |= 0x20; /* 16 bit playback DMA */ + + max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + + outb((max_config), gus_base + 0x106); /* UltraMax control */ + } + + if (!ports) + goto no_cs4231; + + if (ad1848_detect(ports, &ad_flags, hw_config->osp)) + { + char *name = "GUS MAX"; + int old_num_mixers = num_mixers; + + if (gus_pnp_flag) + name = "GUS PnP"; + + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + if (hw_config->name) + name = hw_config->name; + + hw_config->slots[1] = ad1848_init(name, ports, + -irq, gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp, + THIS_MODULE); + + if (num_mixers > old_num_mixers) + { + /* GUS has it's own mixer map */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + } + else { + release_region(gus_base + 0x10c, 4); + no_cs4231: + printk(KERN_WARNING "GUS: No CS4231 ??"); + } +#else + printk(KERN_ERR "GUS MAX found, but not compiled in\n"); +#endif + } + } + else + { + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + */ + } + + if (hw_config->name) + snprintf(tmp, sizeof(tmp), "%s (%dk)", hw_config->name, + (int) gus_mem_size / 1024); + else if (gus_pnp_flag) + snprintf(tmp, sizeof(tmp), "Gravis UltraSound PnP (%dk)", + (int) gus_mem_size / 1024); + else + snprintf(tmp, sizeof(tmp), "Gravis UltraSound %s (%dk)", model_num, + (int) gus_mem_size / 1024); + + + samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); + if (samples == NULL) + { + printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); + return; + } + conf_printf(tmp, hw_config); + strlcpy(gus_info.name, tmp, sizeof(gus_info.name)); + + if ((sdev = sound_alloc_synthdev()) == -1) + printk(KERN_WARNING "gus_init: Too many synthesizers\n"); + else + { + voice_alloc = &guswave_operations.alloc; + if (iw_mode) + guswave_operations.id = "IWAVE"; + hw_config->slots[0] = sdev; + synth_devs[sdev] = &guswave_operations; + sequencer_init(); + gus_tmr_install(gus_base + 8); + } + + reset_sample_memory(); + + gus_initialize(); + + if ((gus_mem_size > 0) && !gus_no_wave_dma) + { + hw_config->slots[4] = -1; + if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Ultrasound", + &gus_audio_driver, + sizeof(struct audio_driver), + NEEDS_RESTART | + ((!iw_mode && dma2 != dma && dma2 != -1) ? + DMA_DUPLEX : 0), + AFMT_U8 | AFMT_S16_LE, + NULL, dma, dma2)) < 0) + { + return; + } + + hw_config->slots[4] = gus_devnum; + audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ + audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ + audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ + audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; + } + + /* + * Mixer dependent initialization. + */ + + switch (mixer_type) + { + case ICS2101: + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + request_region(u_MixSelect, 1, "GUS mixer"); + hw_config->slots[5] = ics2101_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + hw_config->slots[5] = gus_default_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + } +} + +void __exit gus_wave_unload(struct address_info *hw_config) +{ +#ifdef CONFIG_SOUND_GUSMAX + if (have_gus_max) + { + ad1848_unload(gus_base + 0x10c, + -gus_irq, + gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1); /* Share DMA channels with GF1 */ + } +#endif + + if (mixer_type == ICS2101) + { + release_region(u_MixSelect, 1); + } + if (hw_config->slots[0] != -1) + sound_unload_synthdev(hw_config->slots[0]); + if (hw_config->slots[1] != -1) + sound_unload_audiodev(hw_config->slots[1]); + if (hw_config->slots[2] != -1) + sound_unload_mididev(hw_config->slots[2]); + if (hw_config->slots[4] != -1) + sound_unload_audiodev(hw_config->slots[4]); + if (hw_config->slots[5] != -1) + sound_unload_mixerdev(hw_config->slots[5]); + + vfree(samples); + samples=NULL; +} +/* called in interrupt context */ +static void do_loop_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + + spin_lock(&gus_lock); + gus_select_voice(voice); + + tmp = gus_read8(0x00); + tmp &= ~0x20; /* + * Disable wave IRQ for this_one voice + */ + gus_write8(0x00, tmp); + + if (tmp & 0x03) /* Voice stopped */ + voice_alloc->map[voice] = 0; + + mode = voices[voice].loop_irq_mode; + voices[voice].loop_irq_mode = 0; + parm = voices[voice].loop_irq_parm; + + switch (mode) + { + case LMODE_FINISH: /* + * Final loop finished, shoot volume down + */ + + if ((int) (gus_read16(0x09) >> 4) < 100) /* + * Get current volume + */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + break; + } + gus_ramp_range(65, 4065); + gus_ramp_rate(0, 63); /* + * Fastest possible rate + */ + gus_rampon(0x20 | 0x40); /* + * Ramp down, once, irq + */ + voices[voice].volume_irq_mode = VMODE_HALT; + break; + + case LMODE_PCM_STOP: + pcm_active = 0; /* Signal to the play_next_pcm_block routine */ + case LMODE_PCM: + { + pcm_qlen--; + pcm_head = (pcm_head + 1) % pcm_nblk; + if (pcm_qlen && pcm_active) + { + play_next_pcm_block(); + } + else + { + /* Underrun. Just stop the voice */ + gus_select_voice(0); /* Left channel */ + gus_voice_off(); + gus_rampoff(); + gus_select_voice(1); /* Right channel */ + gus_voice_off(); + gus_rampoff(); + pcm_active = 0; + } + + /* + * If the queue was full before this interrupt, the DMA transfer was + * suspended. Let it continue now. + */ + + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + break; + + default: + break; + } + spin_unlock(&gus_lock); +} + +static void do_volume_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + spin_lock_irqsave(&gus_lock,flags); + + gus_select_voice(voice); + tmp = gus_read8(0x0d); + tmp &= ~0x20; /* + * Disable volume ramp IRQ + */ + gus_write8(0x0d, tmp); + + mode = voices[voice].volume_irq_mode; + voices[voice].volume_irq_mode = 0; + parm = voices[voice].volume_irq_parm; + + switch (mode) + { + case VMODE_HALT: /* Decay phase finished */ + if (iw_mode) + gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */ + spin_unlock_irqrestore(&gus_lock,flags); + gus_voice_init(voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff(); + spin_unlock_irqrestore(&gus_lock,flags); + step_envelope(voice); + break; + + case VMODE_START_NOTE: + spin_unlock_irqrestore(&gus_lock,flags); + guswave_start_note2(voices[voice].dev_pending, voice, + voices[voice].note_pending, voices[voice].volume_pending); + if (voices[voice].kill_pending) + guswave_kill_note(voices[voice].dev_pending, voice, + voices[voice].note_pending, 0); + + if (voices[voice].sample_pending >= 0) + { + guswave_set_instr(voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + } + break; + + default: + spin_unlock_irqrestore(&gus_lock,flags); + } +} +/* called in irq context */ +void gus_voice_irq(void) +{ + unsigned long wave_ignore = 0, volume_ignore = 0; + unsigned long voice_bit; + + unsigned char src, voice; + + while (1) + { + src = gus_read8(0x0f); /* + * Get source info + */ + voice = src & 0x1f; + src &= 0xc0; + + if (src == (0x80 | 0x40)) + return; /* + * No interrupt + */ + + voice_bit = 1 << voice; + + if (!(src & 0x80)) /* + * Wave IRQ pending + */ + if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + wave_ignore |= voice_bit; + do_loop_irq(voice); + } + if (!(src & 0x40)) /* + * Volume IRQ pending + */ + if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + volume_ignore |= voice_bit; + do_volume_irq(voice); + } + } +} + +void guswave_dma_irq(void) +{ + unsigned char status; + + status = gus_look8(0x41); /* Get DMA IRQ Status */ + if (status & 0x40) /* DMA interrupt pending */ + switch (active_device) + { + case GUS_DEV_WAVE: + wake_up(&dram_sleeper); + break; + + case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + gus_transfer_output_block(pcm_current_dev, pcm_current_buf, + pcm_current_count, + pcm_current_intrflag, 1); + break; + + case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + if (pcm_qlen < pcm_nblk) + { + dma_active = 0; + if (gus_busy) + { + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + } + break; + + default: + break; + } + status = gus_look8(0x49); /* + * Get Sampling IRQ Status + */ + if (status & 0x40) /* + * Sampling Irq pending + */ + { + DMAbuf_inputintr(gus_devnum); + } +} + +/* + * Timer stuff + */ + +static volatile int select_addr, data_addr; +static volatile int curr_timer; + +void gus_timer_command(unsigned int addr, unsigned int val) +{ + int i; + + outb(((unsigned char) (addr & 0xff)), select_addr); + + for (i = 0; i < 2; i++) + inb(select_addr); + + outb(((unsigned char) (val & 0xff)), data_addr); + + for (i = 0; i < 2; i++) + inb(select_addr); +} + +static void arm_timer(int timer, unsigned int interval) +{ + curr_timer = timer; + + if (timer == 1) + { + gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */ + gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */ + gus_timer_command(0x04, 0x01); /* Start timer 1 */ + } + else + { + gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */ + gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */ + gus_timer_command(0x04, 0x02); /* Start timer 2 */ + } + + gus_timer_enabled = 1; +} + +static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick) +{ + int timer_no, resolution; + int divisor; + + if (usecs_per_tick > (256 * 80)) + { + timer_no = 2; + resolution = 320; /* usec */ + } + else + { + timer_no = 1; + resolution = 80; /* usec */ + } + divisor = (usecs_per_tick + (resolution / 2)) / resolution; + arm_timer(timer_no, divisor); + + return divisor * resolution; +} + +static void gus_tmr_disable(int dev) +{ + gus_write8(0x45, 0); /* Disable both timers */ + gus_timer_enabled = 0; +} + +static void gus_tmr_restart(int dev) +{ + if (curr_timer == 1) + gus_write8(0x45, 0x04); /* Start timer 1 again */ + else + gus_write8(0x45, 0x08); /* Start timer 2 again */ + gus_timer_enabled = 1; +} + +static struct sound_lowlev_timer gus_tmr = +{ + 0, + 1, + gus_tmr_start, + gus_tmr_disable, + gus_tmr_restart +}; + +static void gus_tmr_install(int io_base) +{ + struct sound_lowlev_timer *tmr; + + select_addr = io_base; + data_addr = io_base + 1; + + tmr = &gus_tmr; + +#ifdef THIS_GETS_FIXED + sound_timer_init(&gus_tmr, "GUS"); +#endif +} diff --git a/trunk/sound/oss/harmony.c b/trunk/sound/oss/harmony.c new file mode 100644 index 000000000000..6601b284f03a --- /dev/null +++ b/trunk/sound/oss/harmony.c @@ -0,0 +1,1330 @@ +/* + sound/oss/harmony.c + + This is a sound driver for ASP's and Lasi's Harmony sound chip + and is unlikely to be used for anything other than on a HP PA-RISC. + + Harmony is found in HP 712s, 715/new and many other GSC based machines. + On older 715 machines you'll find the technically identical chip + called 'Vivace'. Both Harmony and Vicace are supported by this driver. + + Copyright 2000 (c) Linuxcare Canada, Alex deVries + Copyright 2000-2003 (c) Helge Deller + Copyright 2001 (c) Matthieu Delahaye + Copyright 2001 (c) Jean-Christophe Vaugeois + Copyright 2004 (c) Stuart Brady + + +TODO: + - fix SNDCTL_DSP_GETOSPACE and SNDCTL_DSP_GETISPACE ioctls to + return the real values + - add private ioctl for selecting line- or microphone input + (only one of them is available at the same time) + - add module parameters + - implement mmap functionality + - implement gain meter ? + - ... +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sound_config.h" + + +#define PFX "harmony: " +#define HARMONY_VERSION "V0.9a" + +#undef DEBUG +#ifdef DEBUG +# define DPRINTK printk +#else +# define DPRINTK(x,...) +#endif + + +#define MAX_BUFS 10 /* maximum number of rotating buffers */ +#define HARMONY_BUF_SIZE 4096 /* needs to be a multiple of PAGE_SIZE (4096)! */ + +#define CNTL_C 0x80000000 +#define CNTL_ST 0x00000020 +#define CNTL_44100 0x00000015 /* HARMONY_SR_44KHZ */ +#define CNTL_8000 0x00000008 /* HARMONY_SR_8KHZ */ + +#define GAINCTL_HE 0x08000000 +#define GAINCTL_LE 0x04000000 +#define GAINCTL_SE 0x02000000 + +#define DSTATUS_PN 0x00000200 +#define DSTATUS_RN 0x00000002 + +#define DSTATUS_IE 0x80000000 + +#define HARMONY_DF_16BIT_LINEAR 0 +#define HARMONY_DF_8BIT_ULAW 1 +#define HARMONY_DF_8BIT_ALAW 2 + +#define HARMONY_SS_MONO 0 +#define HARMONY_SS_STEREO 1 + +#define HARMONY_SR_8KHZ 0x08 +#define HARMONY_SR_16KHZ 0x09 +#define HARMONY_SR_27KHZ 0x0A +#define HARMONY_SR_32KHZ 0x0B +#define HARMONY_SR_48KHZ 0x0E +#define HARMONY_SR_9KHZ 0x0F +#define HARMONY_SR_5KHZ 0x10 +#define HARMONY_SR_11KHZ 0x11 +#define HARMONY_SR_18KHZ 0x12 +#define HARMONY_SR_22KHZ 0x13 +#define HARMONY_SR_37KHZ 0x14 +#define HARMONY_SR_44KHZ 0x15 +#define HARMONY_SR_33KHZ 0x16 +#define HARMONY_SR_6KHZ 0x17 + +/* + * Some magics numbers used to auto-detect file formats + */ + +#define HARMONY_MAGIC_8B_ULAW 1 +#define HARMONY_MAGIC_8B_ALAW 27 +#define HARMONY_MAGIC_16B_LINEAR 3 +#define HARMONY_MAGIC_MONO 1 +#define HARMONY_MAGIC_STEREO 2 + +/* + * Channels Positions in mixer register + */ + +#define GAIN_HE_SHIFT 27 +#define GAIN_HE_MASK ( 1 << GAIN_HE_SHIFT) +#define GAIN_LE_SHIFT 26 +#define GAIN_LE_MASK ( 1 << GAIN_LE_SHIFT) +#define GAIN_SE_SHIFT 25 +#define GAIN_SE_MASK ( 1 << GAIN_SE_SHIFT) +#define GAIN_IS_SHIFT 24 +#define GAIN_IS_MASK ( 1 << GAIN_IS_SHIFT) +#define GAIN_MA_SHIFT 20 +#define GAIN_MA_MASK ( 0x0f << GAIN_MA_SHIFT) +#define GAIN_LI_SHIFT 16 +#define GAIN_LI_MASK ( 0x0f << GAIN_LI_SHIFT) +#define GAIN_RI_SHIFT 12 +#define GAIN_RI_MASK ( 0x0f << GAIN_RI_SHIFT) +#define GAIN_LO_SHIFT 6 +#define GAIN_LO_MASK ( 0x3f << GAIN_LO_SHIFT) +#define GAIN_RO_SHIFT 0 +#define GAIN_RO_MASK ( 0x3f << GAIN_RO_SHIFT) + + +#define MAX_OUTPUT_LEVEL (GAIN_RO_MASK >> GAIN_RO_SHIFT) +#define MAX_INPUT_LEVEL (GAIN_RI_MASK >> GAIN_RI_SHIFT) +#define MAX_MONITOR_LEVEL (GAIN_MA_MASK >> GAIN_MA_SHIFT) + +#define MIXER_INTERNAL SOUND_MIXER_LINE1 +#define MIXER_LINEOUT SOUND_MIXER_LINE2 +#define MIXER_HEADPHONES SOUND_MIXER_LINE3 + +#define MASK_INTERNAL SOUND_MASK_LINE1 +#define MASK_LINEOUT SOUND_MASK_LINE2 +#define MASK_HEADPHONES SOUND_MASK_LINE3 + +/* + * Channels Mask in mixer register + */ + +#define GAIN_TOTAL_SILENCE 0x00F00FFF +#define GAIN_DEFAULT 0x0FF00000 + + +struct harmony_hpa { + u8 unused000; + u8 id; + u8 teleshare_id; + u8 unused003; + u32 reset; + u32 cntl; + u32 gainctl; + u32 pnxtadd; + u32 pcuradd; + u32 rnxtadd; + u32 rcuradd; + u32 dstatus; + u32 ov; + u32 pio; + u32 unused02c; + u32 unused030[3]; + u32 diag; +}; + +struct harmony_dev { + struct harmony_hpa *hpa; + struct parisc_device *dev; + u32 current_gain; + u32 dac_rate; /* 8000 ... 48000 (Hz) */ + u8 data_format; /* HARMONY_DF_xx_BIT_xxx */ + u8 sample_rate; /* HARMONY_SR_xx_KHZ */ + u8 stereo_select; /* HARMONY_SS_MONO or HARMONY_SS_STEREO */ + int format_initialized :1; + int suspended_playing :1; + int suspended_recording :1; + + int blocked_playing :1; + int blocked_recording :1; + int audio_open :1; + int mixer_open :1; + + wait_queue_head_t wq_play, wq_record; + int first_filled_play; /* first buffer containing data (next to play) */ + int nb_filled_play; + int play_offset; + int first_filled_record; + int nb_filled_record; + + int dsp_unit, mixer_unit; +}; + + +static struct harmony_dev harmony; + + +/* + * Dynamic sound buffer allocation and DMA memory + */ + +struct harmony_buffer { + unsigned char *addr; + dma_addr_t dma_handle; + int dma_coherent; /* Zero if dma_alloc_coherent() fails */ + unsigned int len; +}; + +/* + * Harmony memory buffers + */ + +static struct harmony_buffer played_buf, recorded_buf, silent, graveyard; + + +#define CHECK_WBACK_INV_OFFSET(b,offset,len) \ + do { if (!b.dma_coherent) \ + dma_cache_wback_inv((unsigned long)b.addr+offset,len); \ + } while (0) + + +static int __init harmony_alloc_buffer(struct harmony_buffer *b, + unsigned int buffer_count) +{ + b->len = buffer_count * HARMONY_BUF_SIZE; + b->addr = dma_alloc_coherent(&harmony.dev->dev, + b->len, &b->dma_handle, GFP_KERNEL|GFP_DMA); + if (b->addr && b->dma_handle) { + b->dma_coherent = 1; + DPRINTK(KERN_INFO PFX "coherent memory: 0x%lx, played_buf: 0x%lx\n", + (unsigned long)b->dma_handle, (unsigned long)b->addr); + } else { + b->dma_coherent = 0; + /* kmalloc()ed memory will HPMC on ccio machines ! */ + b->addr = kmalloc(b->len, GFP_KERNEL); + if (!b->addr) { + printk(KERN_ERR PFX "couldn't allocate memory\n"); + return -EBUSY; + } + b->dma_handle = __pa(b->addr); + } + return 0; +} + +static void __exit harmony_free_buffer(struct harmony_buffer *b) +{ + if (!b->addr) + return; + + if (b->dma_coherent) + dma_free_coherent(&harmony.dev->dev, + b->len, b->addr, b->dma_handle); + else + kfree(b->addr); + + memset(b, 0, sizeof(*b)); +} + + + +/* + * Low-Level sound-chip programming + */ + +static void __inline__ harmony_wait_CNTL(void) +{ + /* Wait until we're out of control mode */ + while (gsc_readl(&harmony.hpa->cntl) & CNTL_C) + /* wait */ ; +} + + +static void harmony_update_control(void) +{ + u32 default_cntl; + + /* Set CNTL */ + default_cntl = (CNTL_C | /* The C bit */ + (harmony.data_format << 6) | /* Set the data format */ + (harmony.stereo_select << 5) | /* Stereo select */ + (harmony.sample_rate)); /* Set sample rate */ + harmony.format_initialized = 1; + + /* initialize CNTL */ + gsc_writel(default_cntl, &harmony.hpa->cntl); +} + +static void harmony_set_control(u8 data_format, u8 sample_rate, u8 stereo_select) +{ + harmony.sample_rate = sample_rate; + harmony.data_format = data_format; + harmony.stereo_select = stereo_select; + harmony_update_control(); +} + +static void harmony_set_rate(u8 data_rate) +{ + harmony.sample_rate = data_rate; + harmony_update_control(); +} + +static int harmony_detect_rate(int *freq) +{ + int newrate; + switch (*freq) { + case 8000: newrate = HARMONY_SR_8KHZ; break; + case 16000: newrate = HARMONY_SR_16KHZ; break; + case 27428: newrate = HARMONY_SR_27KHZ; break; + case 32000: newrate = HARMONY_SR_32KHZ; break; + case 48000: newrate = HARMONY_SR_48KHZ; break; + case 9600: newrate = HARMONY_SR_9KHZ; break; + case 5512: newrate = HARMONY_SR_5KHZ; break; + case 11025: newrate = HARMONY_SR_11KHZ; break; + case 18900: newrate = HARMONY_SR_18KHZ; break; + case 22050: newrate = HARMONY_SR_22KHZ; break; + case 37800: newrate = HARMONY_SR_37KHZ; break; + case 44100: newrate = HARMONY_SR_44KHZ; break; + case 33075: newrate = HARMONY_SR_33KHZ; break; + case 6615: newrate = HARMONY_SR_6KHZ; break; + default: newrate = HARMONY_SR_8KHZ; + *freq = 8000; break; + } + return newrate; +} + +static void harmony_set_format(u8 data_format) +{ + harmony.data_format = data_format; + harmony_update_control(); +} + +static void harmony_set_stereo(u8 stereo_select) +{ + harmony.stereo_select = stereo_select; + harmony_update_control(); +} + +static void harmony_disable_interrupts(void) +{ + harmony_wait_CNTL(); + gsc_writel(0, &harmony.hpa->dstatus); +} + +static void harmony_enable_interrupts(void) +{ + harmony_wait_CNTL(); + gsc_writel(DSTATUS_IE, &harmony.hpa->dstatus); +} + +/* + * harmony_silence() + * + * This subroutine fills in a buffer starting at location start and + * silences for length bytes. This references the current + * configuration of the audio format. + * + */ + +static void harmony_silence(struct harmony_buffer *buffer, int start, int length) +{ + u8 silence_char; + + /* Despite what you hear, silence is different in + different audio formats. */ + switch (harmony.data_format) { + case HARMONY_DF_8BIT_ULAW: silence_char = 0x55; break; + case HARMONY_DF_8BIT_ALAW: silence_char = 0xff; break; + case HARMONY_DF_16BIT_LINEAR: /* fall through */ + default: silence_char = 0; + } + + memset(buffer->addr+start, silence_char, length); +} + + +static int harmony_audio_open(struct inode *inode, struct file *file) +{ + if (harmony.audio_open) + return -EBUSY; + + harmony.audio_open = 1; + harmony.suspended_playing = harmony.suspended_recording = 1; + harmony.blocked_playing = harmony.blocked_recording = 0; + harmony.first_filled_play = harmony.first_filled_record = 0; + harmony.nb_filled_play = harmony.nb_filled_record = 0; + harmony.play_offset = 0; + init_waitqueue_head(&harmony.wq_play); + init_waitqueue_head(&harmony.wq_record); + + /* Start off in a balanced mode. */ + harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO); + harmony_update_control(); + harmony.format_initialized = 0; + + /* Clear out all the buffers and flush to cache */ + harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + + return 0; +} + +/* + * Release (close) the audio device. + */ + +static int harmony_audio_release(struct inode *inode, struct file *file) +{ + if (!harmony.audio_open) + return -EBUSY; + + harmony.audio_open = 0; + + return 0; +} + +/* + * Read recorded data off the audio device. + */ + +static ssize_t harmony_audio_read(struct file *file, + char *buffer, + size_t size_count, + loff_t *ppos) +{ + int total_count = (int) size_count; + int count = 0; + int buf_to_read; + + while (count24) { + if (copy_from_user(file_header, buffer, sizeof(file_header))) + ret = -EFAULT; + + start_string = four_bytes_to_u32(0); + + if ((file_header[4]==0) && (start_string==0x2E736E64)) { + u32 format; + u32 nb_voices; + u32 speed; + + format = four_bytes_to_u32(12); + nb_voices = four_bytes_to_u32(20); + speed = four_bytes_to_u32(16); + + switch (format) { + case HARMONY_MAGIC_8B_ULAW: + harmony.data_format = HARMONY_DF_8BIT_ULAW; + break; + case HARMONY_MAGIC_8B_ALAW: + harmony.data_format = HARMONY_DF_8BIT_ALAW; + break; + case HARMONY_MAGIC_16B_LINEAR: + harmony.data_format = HARMONY_DF_16BIT_LINEAR; + break; + default: + harmony_set_control(HARMONY_DF_16BIT_LINEAR, + HARMONY_SR_44KHZ, HARMONY_SS_STEREO); + goto out; + } + switch (nb_voices) { + case HARMONY_MAGIC_MONO: + harmony.stereo_select = HARMONY_SS_MONO; + break; + case HARMONY_MAGIC_STEREO: + harmony.stereo_select = HARMONY_SS_STEREO; + break; + default: + harmony.stereo_select = HARMONY_SS_MONO; + break; + } + harmony_set_rate(harmony_detect_rate(&speed)); + harmony.dac_rate = speed; + goto out; + } + } + harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO); +out: + return ret; +} +#undef four_bytes_to_u32 + + +static ssize_t harmony_audio_write(struct file *file, + const char *buffer, + size_t size_count, + loff_t *ppos) +{ + int total_count = (int) size_count; + int count = 0; + int frame_size; + int buf_to_fill; + int fresh_buffer; + + if (!harmony.format_initialized) { + if (harmony_format_auto_detect(buffer, total_count)) + return -EFAULT; + } + + while (count= MAX_BUFS && !harmony.play_offset) { + harmony.blocked_playing = 1; + interruptible_sleep_on(&harmony.wq_play); + harmony.blocked_playing = 0; + } + if (harmony.nb_filled_play+2 >= MAX_BUFS && !harmony.play_offset) + return -EBUSY; + + + buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play); + if (harmony.play_offset) { + buf_to_fill--; + buf_to_fill += MAX_BUFS; + } + buf_to_fill %= MAX_BUFS; + + fresh_buffer = (harmony.play_offset == 0); + + /* Figure out the size of the frame */ + if ((total_count-count) >= HARMONY_BUF_SIZE - harmony.play_offset) { + frame_size = HARMONY_BUF_SIZE - harmony.play_offset; + } else { + frame_size = total_count - count; + /* Clear out the buffer, since there we'll only be + overlaying part of the old buffer with the new one */ + harmony_silence(&played_buf, + HARMONY_BUF_SIZE*buf_to_fill+frame_size+harmony.play_offset, + HARMONY_BUF_SIZE-frame_size-harmony.play_offset); + } + + /* Copy the page to an aligned buffer */ + if (copy_from_user(played_buf.addr +(HARMONY_BUF_SIZE*buf_to_fill) + harmony.play_offset, + buffer+count, frame_size)) + return -EFAULT; + CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset), + frame_size); + + if (fresh_buffer) + harmony.nb_filled_play++; + + count += frame_size; + harmony.play_offset += frame_size; + harmony.play_offset %= HARMONY_BUF_SIZE; + if (harmony.suspended_playing && (harmony.nb_filled_play>=4)) + harmony_enable_interrupts(); + } + + return count; +} + +static unsigned int harmony_audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + + if (file->f_mode & FMODE_READ) { + if (!harmony.suspended_recording) + poll_wait(file, &harmony.wq_record, wait); + if (harmony.nb_filled_record) + mask |= POLLIN | POLLRDNORM; + } + + if (file->f_mode & FMODE_WRITE) { + if (!harmony.suspended_playing) + poll_wait(file, &harmony.wq_play, wait); + if (harmony.nb_filled_play) + mask |= POLLOUT | POLLWRNORM; + } + + return mask; +} + +static int harmony_audio_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ival, new_format; + int frag_size, frag_buf; + struct audio_buf_info info; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_GETCAPS: + ival = DSP_CAP_DUPLEX; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: + ival = (AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW ); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETFMT: + if (get_user(ival, (int *) arg)) + return -EFAULT; + if (ival != AFMT_QUERY) { + switch (ival) { + case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break; + case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break; + case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break; + default: { + DPRINTK(KERN_WARNING PFX + "unsupported sound format 0x%04x requested.\n", + ival); + ival = AFMT_S16_BE; + return put_user(ival, (int *) arg); + } + } + harmony_set_format(new_format); + return 0; + } else { + switch (harmony.data_format) { + case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break; + case HARMONY_DF_8BIT_ALAW: ival = AFMT_A_LAW; break; + case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break; + default: ival = 0; + } + return put_user(ival, (int *) arg); + } + + case SOUND_PCM_READ_RATE: + ival = harmony.dac_rate; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SPEED: + if (get_user(ival, (int *) arg)) + return -EFAULT; + harmony_set_rate(harmony_detect_rate(&ival)); + harmony.dac_rate = ival; + return put_user(ival, (int*) arg); + + case SNDCTL_DSP_STEREO: + if (get_user(ival, (int *) arg)) + return -EFAULT; + if (ival != 0 && ival != 1) + return -EINVAL; + harmony_set_stereo(ival); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(ival, (int *) arg)) + return -EFAULT; + if (ival != 1 && ival != 2) { + ival = harmony.stereo_select == HARMONY_SS_MONO ? 1 : 2; + return put_user(ival, (int *) arg); + } + harmony_set_stereo(ival-1); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + ival = HARMONY_BUF_SIZE; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (!harmony.suspended_recording) { + /* TODO: stop_recording() */ + } + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(ival, (int *)arg)) + return -EFAULT; + frag_size = ival & 0xffff; + frag_buf = (ival>>16) & 0xffff; + /* TODO: We use hardcoded fragment sizes and numbers for now */ + frag_size = 12; /* 4096 == 2^12 */ + frag_buf = MAX_BUFS; + ival = (frag_buf << 16) + frag_size; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + info.fragstotal = MAX_BUFS; + info.fragments = MAX_BUFS - harmony.nb_filled_play; + info.fragsize = HARMONY_BUF_SIZE; + info.bytes = info.fragments * info.fragsize; + return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + info.fragstotal = MAX_BUFS; + info.fragments = /*MAX_BUFS-*/ harmony.nb_filled_record; + info.fragsize = HARMONY_BUF_SIZE; + info.bytes = info.fragments * info.fragsize; + return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; + + case SNDCTL_DSP_SYNC: + return 0; + } + + return -EINVAL; +} + + +/* + * harmony_interrupt() + * + * harmony interruption service routine + * + */ + +static irqreturn_t harmony_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + u32 dstatus; + struct harmony_hpa *hpa; + + /* Setup the hpa */ + hpa = ((struct harmony_dev *)dev)->hpa; + harmony_wait_CNTL(); + + /* Read dstatus and pcuradd (the current address) */ + dstatus = gsc_readl(&hpa->dstatus); + + /* Turn off interrupts */ + harmony_disable_interrupts(); + + /* Check if this is a request to get the next play buffer */ + if (dstatus & DSTATUS_PN) { + if (!harmony.nb_filled_play) { + harmony.suspended_playing = 1; + gsc_writel((unsigned long)silent.dma_handle, &hpa->pnxtadd); + + if (!harmony.suspended_recording) + harmony_enable_interrupts(); + } else { + harmony.suspended_playing = 0; + gsc_writel((unsigned long)played_buf.dma_handle + + (HARMONY_BUF_SIZE*harmony.first_filled_play), + &hpa->pnxtadd); + harmony.first_filled_play++; + harmony.first_filled_play %= MAX_BUFS; + harmony.nb_filled_play--; + + harmony_enable_interrupts(); + } + + if (harmony.blocked_playing) + wake_up_interruptible(&harmony.wq_play); + } + + /* Check if we're being asked to fill in a recording buffer */ + if (dstatus & DSTATUS_RN) { + if((harmony.nb_filled_record+2>=MAX_BUFS) || harmony.suspended_recording) + { + harmony.nb_filled_record = 0; + harmony.first_filled_record = 0; + harmony.suspended_recording = 1; + gsc_writel((unsigned long)graveyard.dma_handle, &hpa->rnxtadd); + if (!harmony.suspended_playing) + harmony_enable_interrupts(); + } else { + int buf_to_fill; + buf_to_fill = (harmony.first_filled_record+harmony.nb_filled_record) % MAX_BUFS; + CHECK_WBACK_INV_OFFSET(recorded_buf, HARMONY_BUF_SIZE*buf_to_fill, HARMONY_BUF_SIZE); + gsc_writel((unsigned long)recorded_buf.dma_handle + + HARMONY_BUF_SIZE*buf_to_fill, + &hpa->rnxtadd); + harmony.nb_filled_record++; + harmony_enable_interrupts(); + } + + if (harmony.blocked_recording && harmony.nb_filled_record>3) + wake_up_interruptible(&harmony.wq_record); + } + return IRQ_HANDLED; +} + +/* + * Sound playing functions + */ + +static struct file_operations harmony_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = harmony_audio_read, + .write = harmony_audio_write, + .poll = harmony_audio_poll, + .ioctl = harmony_audio_ioctl, + .open = harmony_audio_open, + .release = harmony_audio_release, +}; + +static int harmony_audio_init(void) +{ + /* Request that IRQ */ + if (request_irq(harmony.dev->irq, harmony_interrupt, 0 ,"harmony", &harmony)) { + printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony.dev->irq); + return -EFAULT; + } + + harmony.dsp_unit = register_sound_dsp(&harmony_audio_fops, -1); + if (harmony.dsp_unit < 0) { + printk(KERN_ERR PFX "Error registering dsp\n"); + free_irq(harmony.dev->irq, &harmony); + return -EFAULT; + } + + /* Clear the buffers so you don't end up with crap in the buffers. */ + harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + + /* Make sure this makes it to cache */ + CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); + + /* Clear out the silent buffer and flush to cache */ + harmony_silence(&silent, 0, HARMONY_BUF_SIZE); + CHECK_WBACK_INV_OFFSET(silent, 0, HARMONY_BUF_SIZE); + + harmony.audio_open = 0; + + return 0; +} + + +/* + * mixer functions + */ + +static void harmony_mixer_set_gain(void) +{ + harmony_wait_CNTL(); + gsc_writel(harmony.current_gain, &harmony.hpa->gainctl); +} + +/* + * Read gain of selected channel. + * The OSS rate is from 0 (silent) to 100 -> need some conversions + * + * The harmony gain are attenuation for output and monitor gain. + * is amplifaction for input gain + */ +#define to_harmony_level(level,max) ((level)*max/100) +#define to_oss_level(level,max) ((level)*100/max) + +static int harmony_mixer_get_level(int channel) +{ + int left_level; + int right_level; + + switch (channel) { + case SOUND_MIXER_VOLUME: + left_level = (harmony.current_gain & GAIN_LO_MASK) >> GAIN_LO_SHIFT; + right_level = (harmony.current_gain & GAIN_RO_MASK) >> GAIN_RO_SHIFT; + left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); + right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); + return (right_level << 8)+left_level; + + case SOUND_MIXER_IGAIN: + left_level = (harmony.current_gain & GAIN_LI_MASK) >> GAIN_LI_SHIFT; + right_level= (harmony.current_gain & GAIN_RI_MASK) >> GAIN_RI_SHIFT; + left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); + right_level= to_oss_level(right_level, MAX_INPUT_LEVEL); + return (right_level << 8)+left_level; + + case SOUND_MIXER_MONITOR: + left_level = (harmony.current_gain & GAIN_MA_MASK) >> GAIN_MA_SHIFT; + left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL); + return (left_level << 8)+left_level; + } + return -EINVAL; +} + + + +/* + * Some conversions for the same reasons. + * We give back the new real value(s) due to + * the rescale. + */ + +static int harmony_mixer_set_level(int channel, int value) +{ + int left_level; + int right_level; + int new_left_level; + int new_right_level; + + right_level = (value & 0x0000ff00) >> 8; + left_level = value & 0x000000ff; + if (right_level > 100) right_level = 100; + if (left_level > 100) left_level = 100; + + switch (channel) { + case SOUND_MIXER_VOLUME: + right_level = to_harmony_level(100-right_level, MAX_OUTPUT_LEVEL); + left_level = to_harmony_level(100-left_level, MAX_OUTPUT_LEVEL); + new_right_level = to_oss_level(MAX_OUTPUT_LEVEL - right_level, MAX_OUTPUT_LEVEL); + new_left_level = to_oss_level(MAX_OUTPUT_LEVEL - left_level, MAX_OUTPUT_LEVEL); + harmony.current_gain = (harmony.current_gain & ~(GAIN_LO_MASK | GAIN_RO_MASK)) + | (left_level << GAIN_LO_SHIFT) | (right_level << GAIN_RO_SHIFT); + harmony_mixer_set_gain(); + return (new_right_level << 8) + new_left_level; + + case SOUND_MIXER_IGAIN: + right_level = to_harmony_level(right_level, MAX_INPUT_LEVEL); + left_level = to_harmony_level(left_level, MAX_INPUT_LEVEL); + new_right_level = to_oss_level(right_level, MAX_INPUT_LEVEL); + new_left_level = to_oss_level(left_level, MAX_INPUT_LEVEL); + harmony.current_gain = (harmony.current_gain & ~(GAIN_LI_MASK | GAIN_RI_MASK)) + | (left_level << GAIN_LI_SHIFT) | (right_level << GAIN_RI_SHIFT); + harmony_mixer_set_gain(); + return (new_right_level << 8) + new_left_level; + + case SOUND_MIXER_MONITOR: + left_level = to_harmony_level(100-left_level, MAX_MONITOR_LEVEL); + new_left_level = to_oss_level(MAX_MONITOR_LEVEL-left_level, MAX_MONITOR_LEVEL); + harmony.current_gain = (harmony.current_gain & ~GAIN_MA_MASK) | (left_level << GAIN_MA_SHIFT); + harmony_mixer_set_gain(); + return (new_left_level << 8) + new_left_level; + } + + return -EINVAL; +} + +#undef to_harmony_level +#undef to_oss_level + +/* + * Return the selected input device (mic or line) + */ + +static int harmony_mixer_get_recmask(void) +{ + int current_input_line; + + current_input_line = (harmony.current_gain & GAIN_IS_MASK) + >> GAIN_IS_SHIFT; + if (current_input_line) + return SOUND_MASK_MIC; + + return SOUND_MASK_LINE; +} + +/* + * Set the input (only one at time, arbitrary priority to line in) + */ + +static int harmony_mixer_set_recmask(int recmask) +{ + int new_input_line; + int new_input_mask; + int current_input_line; + + current_input_line = (harmony.current_gain & GAIN_IS_MASK) + >> GAIN_IS_SHIFT; + if ((current_input_line && ((recmask & SOUND_MASK_LINE) || !(recmask & SOUND_MASK_MIC))) || + (!current_input_line && ((recmask & SOUND_MASK_LINE) && !(recmask & SOUND_MASK_MIC)))) { + new_input_line = 0; + new_input_mask = SOUND_MASK_LINE; + } else { + new_input_line = 1; + new_input_mask = SOUND_MASK_MIC; + } + harmony.current_gain = ((harmony.current_gain & ~GAIN_IS_MASK) | + (new_input_line << GAIN_IS_SHIFT )); + harmony_mixer_set_gain(); + return new_input_mask; +} + + +/* + * give the active outlines + */ + +static int harmony_mixer_get_outmask(void) +{ + int outmask = 0; + + if (harmony.current_gain & GAIN_SE_MASK) outmask |= MASK_INTERNAL; + if (harmony.current_gain & GAIN_LE_MASK) outmask |= MASK_LINEOUT; + if (harmony.current_gain & GAIN_HE_MASK) outmask |= MASK_HEADPHONES; + + return outmask; +} + + +static int harmony_mixer_set_outmask(int outmask) +{ + if (outmask & MASK_INTERNAL) + harmony.current_gain |= GAIN_SE_MASK; + else + harmony.current_gain &= ~GAIN_SE_MASK; + + if (outmask & MASK_LINEOUT) + harmony.current_gain |= GAIN_LE_MASK; + else + harmony.current_gain &= ~GAIN_LE_MASK; + + if (outmask & MASK_HEADPHONES) + harmony.current_gain |= GAIN_HE_MASK; + else + harmony.current_gain &= ~GAIN_HE_MASK; + + harmony_mixer_set_gain(); + + return (outmask & (MASK_INTERNAL | MASK_LINEOUT | MASK_HEADPHONES)); +} + +/* + * This code is inspired from sb_mixer.c + */ + +static int harmony_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int val; + int ret; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info, 0, sizeof(info)); + strncpy(info.id, "harmony", sizeof(info.id)-1); + strncpy(info.name, "Harmony audio", sizeof(info.name)-1); + info.modify_counter = 1; /* ? */ + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + /* read */ + val = 0; + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (cmd) { + case MIXER_READ(SOUND_MIXER_CAPS): + ret = SOUND_CAP_EXCL_INPUT; + break; + case MIXER_READ(SOUND_MIXER_STEREODEVS): + ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN; + break; + + case MIXER_READ(SOUND_MIXER_RECMASK): + ret = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + case MIXER_READ(SOUND_MIXER_DEVMASK): + ret = SOUND_MASK_VOLUME | SOUND_MASK_IGAIN | + SOUND_MASK_MONITOR; + break; + case MIXER_READ(SOUND_MIXER_OUTMASK): + ret = MASK_INTERNAL | MASK_LINEOUT | + MASK_HEADPHONES; + break; + + case MIXER_WRITE(SOUND_MIXER_RECSRC): + ret = harmony_mixer_set_recmask(val); + break; + case MIXER_READ(SOUND_MIXER_RECSRC): + ret = harmony_mixer_get_recmask(); + break; + + case MIXER_WRITE(SOUND_MIXER_OUTSRC): + ret = harmony_mixer_set_outmask(val); + break; + case MIXER_READ(SOUND_MIXER_OUTSRC): + ret = harmony_mixer_get_outmask(); + break; + + case MIXER_WRITE(SOUND_MIXER_VOLUME): + case MIXER_WRITE(SOUND_MIXER_IGAIN): + case MIXER_WRITE(SOUND_MIXER_MONITOR): + ret = harmony_mixer_set_level(cmd & 0xff, val); + break; + + case MIXER_READ(SOUND_MIXER_VOLUME): + case MIXER_READ(SOUND_MIXER_IGAIN): + case MIXER_READ(SOUND_MIXER_MONITOR): + ret = harmony_mixer_get_level(cmd & 0xff); + break; + + default: + return -EINVAL; + } + + if (put_user(ret, (int *)arg)) + return -EFAULT; + return 0; +} + + +static int harmony_mixer_open(struct inode *inode, struct file *file) +{ + if (harmony.mixer_open) + return -EBUSY; + harmony.mixer_open = 1; + return 0; +} + +static int harmony_mixer_release(struct inode *inode, struct file *file) +{ + if (!harmony.mixer_open) + return -EBUSY; + harmony.mixer_open = 0; + return 0; +} + +static struct file_operations harmony_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = harmony_mixer_open, + .release = harmony_mixer_release, + .ioctl = harmony_mixer_ioctl, +}; + + +/* + * Mute all the output and reset Harmony. + */ + +static void __init harmony_mixer_reset(void) +{ + harmony.current_gain = GAIN_TOTAL_SILENCE; + harmony_mixer_set_gain(); + harmony_wait_CNTL(); + gsc_writel(1, &harmony.hpa->reset); + mdelay(50); /* wait 50 ms */ + gsc_writel(0, &harmony.hpa->reset); + harmony.current_gain = GAIN_DEFAULT; + harmony_mixer_set_gain(); +} + +static int __init harmony_mixer_init(void) +{ + /* Register the device file operations */ + harmony.mixer_unit = register_sound_mixer(&harmony_mixer_fops, -1); + if (harmony.mixer_unit < 0) { + printk(KERN_WARNING PFX "Error Registering Mixer Driver\n"); + return -EFAULT; + } + + harmony_mixer_reset(); + harmony.mixer_open = 0; + + return 0; +} + + + +/* + * This is the callback that's called by the inventory hardware code + * if it finds a match to the registered driver. + */ +static int __devinit +harmony_driver_probe(struct parisc_device *dev) +{ + u8 id; + u8 rev; + u32 cntl; + int ret; + + if (harmony.hpa) { + /* We only support one Harmony at this time */ + printk(KERN_ERR PFX "driver already registered\n"); + return -EBUSY; + } + + if (!dev->irq) { + printk(KERN_ERR PFX "no irq found\n"); + return -ENODEV; + } + + /* Set the HPA of harmony */ + harmony.hpa = (struct harmony_hpa *)dev->hpa.start; + harmony.dev = dev; + + /* Grab the ID and revision from the device */ + id = gsc_readb(&harmony.hpa->id); + if ((id | 1) != 0x15) { + printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", id); + return -EBUSY; + } + cntl = gsc_readl(&harmony.hpa->cntl); + rev = (cntl>>20) & 0xff; + + printk(KERN_INFO "Lasi Harmony Audio driver " HARMONY_VERSION ", " + "h/w id %i, rev. %i at 0x%lx, IRQ %i\n", + id, rev, dev->hpa.start, harmony.dev->irq); + + /* Make sure the control bit isn't set, although I don't think it + ever is. */ + if (cntl & CNTL_C) { + printk(KERN_WARNING PFX "CNTL busy\n"); + harmony.hpa = 0; + return -EBUSY; + } + + /* Initialize the memory buffers */ + if (harmony_alloc_buffer(&played_buf, MAX_BUFS) || + harmony_alloc_buffer(&recorded_buf, MAX_BUFS) || + harmony_alloc_buffer(&graveyard, 1) || + harmony_alloc_buffer(&silent, 1)) { + ret = -EBUSY; + goto out_err; + } + + /* Initialize /dev/mixer and /dev/audio */ + if ((ret=harmony_mixer_init())) + goto out_err; + if ((ret=harmony_audio_init())) + goto out_err; + + return 0; + +out_err: + harmony.hpa = 0; + harmony_free_buffer(&played_buf); + harmony_free_buffer(&recorded_buf); + harmony_free_buffer(&graveyard); + harmony_free_buffer(&silent); + return ret; +} + + +static struct parisc_device_id harmony_tbl[] = { + /* { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, Bushmaster/Flounder */ + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, /* 712/715 Audio */ + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, /* Pace Audio */ + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, /* Outfield / Coral II */ + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, harmony_tbl); + +static struct parisc_driver harmony_driver = { + .name = "Lasi Harmony", + .id_table = harmony_tbl, + .probe = harmony_driver_probe, +}; + +static int __init init_harmony(void) +{ + return register_parisc_driver(&harmony_driver); +} + +static void __exit cleanup_harmony(void) +{ + free_irq(harmony.dev->irq, &harmony); + unregister_sound_mixer(harmony.mixer_unit); + unregister_sound_dsp(harmony.dsp_unit); + harmony_free_buffer(&played_buf); + harmony_free_buffer(&recorded_buf); + harmony_free_buffer(&graveyard); + harmony_free_buffer(&silent); + unregister_parisc_driver(&harmony_driver); +} + + +MODULE_AUTHOR("Alex DeVries "); +MODULE_DESCRIPTION("Harmony sound driver"); +MODULE_LICENSE("GPL"); + +module_init(init_harmony); +module_exit(cleanup_harmony); + diff --git a/trunk/sound/oss/ics2101.c b/trunk/sound/oss/ics2101.c new file mode 100644 index 000000000000..45918df150b3 --- /dev/null +++ b/trunk/sound/oss/ics2101.c @@ -0,0 +1,247 @@ +/* + * sound/oss/ics2101.c + * + * Driver for the ICS2101 mixer of GUS v3.7. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init() + */ +#include +#include +#include "sound_config.h" + +#include + +#include "gus.h" +#include "gus_hw.h" + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH| \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +extern int *gus_osp; +extern int gus_base; +extern spinlock_t gus_lock; +static int volumes[ICS_MIXDEVS]; +static int left_fix[ICS_MIXDEVS] = +{1, 1, 1, 2, 1, 2}; +static int right_fix[ICS_MIXDEVS] = +{2, 2, 2, 1, 2, 1}; + +static int scale_vol(int vol) +{ + /* + * Experimental volume scaling by Risto Kankkunen. + * This should give smoother volume response than just + * a plain multiplication. + */ + + int e; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) + { + while (vol < 16) + { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; + } + return ((e << 4) + vol); +} + +static void write_mix(int dev, int chn, int vol) +{ + int *selector; + unsigned long flags; + int ctrl_addr = dev << 3; + int attn_addr = dev << 3; + + vol = scale_vol(vol); + + if (chn == CHN_LEFT) + { + selector = left_fix; + ctrl_addr |= 0x00; + attn_addr |= 0x02; + } + else + { + selector = right_fix; + ctrl_addr |= 0x01; + attn_addr |= 0x03; + } + + spin_lock_irqsave(&gus_lock, flags); + outb((ctrl_addr), u_MixSelect); + outb((selector[dev]), u_MixData); + outb((attn_addr), u_MixSelect); + outb(((unsigned char) vol), u_MixData); + spin_unlock_irqrestore(&gus_lock,flags); +} + +static int set_volumes(int dev, int vol) +{ + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + if (left < 0) + left = 0; + if (left > 100) + left = 100; + if (right < 0) + right = 0; + if (right > 100) + right = 100; + + write_mix(dev, CHN_LEFT, left); + write_mix(dev, CHN_RIGHT, right); + + vol = left + (right << 8); + volumes[dev] = vol; + return vol; +} + +static int ics2101_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) +{ + int val; + + if (((cmd >> 8) & 0xff) == 'M') { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + if (get_user(val, (int __user *)arg)) + return -EFAULT; + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_MIC: + val = set_volumes(DEV_MIC, val); + break; + + case SOUND_MIXER_CD: + val = set_volumes(DEV_CD, val); + break; + + case SOUND_MIXER_LINE: + val = set_volumes(DEV_LINE, val); + break; + + case SOUND_MIXER_SYNTH: + val = set_volumes(DEV_GF1, val); + break; + + case SOUND_MIXER_VOLUME: + val = set_volumes(DEV_VOL, val); + break; + + default: + return -EINVAL; + } + return put_user(val, (int __user *)arg); + } else { + switch (cmd & 0xff) { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = volumes[DEV_MIC]; + break; + + case SOUND_MIXER_LINE: + val = volumes[DEV_LINE]; + break; + + case SOUND_MIXER_CD: + val = volumes[DEV_CD]; + break; + + case SOUND_MIXER_VOLUME: + val = volumes[DEV_VOL]; + break; + + case SOUND_MIXER_SYNTH: + val = volumes[DEV_GF1]; + break; + + default: + return -EINVAL; + } + return put_user(val, (int __user *)arg); + } + } + return -EINVAL; +} + +static struct mixer_operations ics2101_mixer_operations = +{ + .owner = THIS_MODULE, + .id = "ICS2101", + .name = "ICS2101 Multimedia Mixer", + .ioctl = ics2101_mixer_ioctl +}; + +int __init ics2101_mixer_init(void) +{ + int i; + int n; + + if ((n = sound_alloc_mixerdev()) != -1) + { + mixer_devs[n] = &ics2101_mixer_operations; + + /* + * Some GUS v3.7 cards had some channels flipped. Disable + * the flipping feature if the model id is other than 5. + */ + + if (inb(u_MixSelect) != 5) + { + for (i = 0; i < ICS_MIXDEVS; i++) + left_fix[i] = 1; + for (i = 0; i < ICS_MIXDEVS; i++) + right_fix[i] = 2; + } + set_volumes(DEV_GF1, 0x5a5a); + set_volumes(DEV_CD, 0x5a5a); + set_volumes(DEV_MIC, 0x0000); + set_volumes(DEV_LINE, 0x5a5a); + set_volumes(DEV_VOL, 0x5a5a); + set_volumes(DEV_UNUSED, 0x0000); + } + return n; +} diff --git a/trunk/sound/oss/iwmem.h b/trunk/sound/oss/iwmem.h new file mode 100644 index 000000000000..48d333c7302b --- /dev/null +++ b/trunk/sound/oss/iwmem.h @@ -0,0 +1,36 @@ +/* + * sound/oss/iwmem.h + * + * DRAM size encoding table for AMD Interwave chip. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Bartlomiej Zolnierkiewicz : added __initdata to mem_decode + */ + + +#define K 1024 +#define M (1024*K) +static int mem_decode[][4] __initdata = +{ +/* Bank0 Bank1 Bank2 Bank3 Encoding bits */ + {256*K, 0, 0, 0}, /* 0 */ + {256*K, 256*K, 0, 0}, /* 1 */ + {256*K, 256*K, 256*K, 256*K}, /* 2 */ + {256*K, 1*M, 0, 0}, /* 3 */ + {256*K, 1*M, 1*M, 1*M}, /* 4 */ + {256*K, 256*K, 1*M, 0}, /* 5 */ + {256*K, 256*K, 1*M, 1*M}, /* 6 */ + {1*M, 0, 0, 0}, /* 7 */ + {1*M, 1*M, 0, 0}, /* 8 */ + {1*M, 1*M, 1*M, 1*M}, /* 9 */ + {4*M, 0, 0, 0}, /* 10 */ + {4*M, 4*M, 0, 0}, /* 11 */ + {4*M, 4*M, 4*M, 4*M} /* 12 */ +}; diff --git a/trunk/sound/oss/mad16.c b/trunk/sound/oss/mad16.c new file mode 100644 index 000000000000..954647f41dff --- /dev/null +++ b/trunk/sound/oss/mad16.c @@ -0,0 +1,1112 @@ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * mad16.c + * + * Initialization code for OPTi MAD16 compatible audio chips. Including + * + * OPTi 82C928 MAD16 (replaced by C929) + * OAK OTI-601D Mozart + * OAK OTI-605 Mozart (later version with MPU401 Midi) + * OPTi 82C929 MAD16 Pro + * OPTi 82C930 + * OPTi 82C924 + * + * These audio interface chips don't produce sound themselves. They just + * connect some other components (OPL-[234] and a WSS compatible codec) + * to the PC bus and perform I/O, DMA and IRQ address decoding. There is + * also a UART for the MPU-401 mode (not 82C928/Mozart). + * The Mozart chip appears to be compatible with the 82C928, although later + * issues of the card, using the OTI-605 chip, have an MPU-401 compatible Midi + * port. This port is configured differently to that of the OPTi audio chips. + * + * Changes + * + * Alan Cox Clean up, added module selections. + * + * A. Wik Added support for Opti924 PnP. + * Improved debugging support. 16-May-1998 + * Fixed bug. 16-Jun-1998 + * + * Torsten Duwe Made Opti924 PnP support non-destructive + * 23-Dec-1998 + * + * Paul Grayson Added support for Midi on later Mozart cards. + * 25-Nov-1999 + * Christoph Hellwig Adapted to module_init/module_exit. + * Arnaldo C. de Melo got rid of attach_uart401 21-Sep-2000 + * + * Pavel Rabel Clean up Nov-2000 + */ + +#include +#include +#include +#include +#include "sound_config.h" + +#include "ad1848.h" +#include "sb.h" +#include "mpu401.h" + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + +static int mad16_conf; +static int mad16_cdsel; +static DEFINE_SPINLOCK(lock); + +#define C928 1 +#define MOZART 2 +#define C929 3 +#define C930 4 +#define C924 5 + +/* + * Registers + * + * The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations). + * All ports are inactive by default. They can be activated by + * writing 0xE2 or 0xE3 to the password register. The password is valid + * only until the next I/O read or write. + * + * 82C930 uses 0xE4 as the password and indirect addressing to access + * the config registers. + */ + +#define MC0_PORT 0xf8c /* Dummy port */ +#define MC1_PORT 0xf8d /* SB address, CD-ROM interface type, joystick */ +#define MC2_PORT 0xf8e /* CD-ROM address, IRQ, DMA, plus OPL4 bit */ +#define MC3_PORT 0xf8f +#define PASSWD_REG 0xf8f +#define MC4_PORT 0xf90 +#define MC5_PORT 0xf91 +#define MC6_PORT 0xf92 +#define MC7_PORT 0xf93 +#define MC8_PORT 0xf94 +#define MC9_PORT 0xf95 +#define MC10_PORT 0xf96 +#define MC11_PORT 0xf97 +#define MC12_PORT 0xf98 + +static int board_type = C928; + +static int *mad16_osp; +static int c931_detected; /* minor differences from C930 */ +static char c924pnp; /* " " " C924 */ +static int debug; /* debugging output */ + +#ifdef DDB +#undef DDB +#endif +#define DDB(x) do {if (debug) x;} while (0) + +static unsigned char mad_read(int port) +{ + unsigned long flags; + unsigned char tmp; + + spin_lock_irqsave(&lock,flags); + + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + /* the c924 has its ports relocated by -128 if + PnP is enabled -aw */ + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + tmp = inb(0xe0f); /* Read from data reg */ + } + else + if (!c924pnp) + tmp = inb(port); else + tmp = inb(port-0x80); + spin_unlock_irqrestore(&lock,flags); + + return tmp; +} + +static void mad_write(int port, int value) +{ + unsigned long flags; + + spin_lock_irqsave(&lock,flags); + + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + if (!c924pnp) + outb((0xE5), PASSWD_REG); else + outb((0xE5), PASSWD_REG - 0x80); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + outb(((unsigned char) (value & 0xff)), 0xe0f); + } + else + if (!c924pnp) + outb(((unsigned char) (value & 0xff)), port); else + outb(((unsigned char) (value & 0xff)), port-0x80); + spin_unlock_irqrestore(&lock,flags); +} + +static int __init detect_c930(void) +{ + unsigned char tmp = mad_read(MC1_PORT); + + if ((tmp & 0x06) != 0x06) + { + DDB(printk("Wrong C930 signature (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, 0); + + if (mad_read(MC1_PORT) != 0x06) + { + DDB(printk("Wrong C930 signature2 (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, tmp); /* Restore bits */ + + mad_write(MC7_PORT, 0); + if ((tmp = mad_read(MC7_PORT)) != 0) + { + DDB(printk("MC7 not writable (%x)\n", tmp)); + return 0; + } + mad_write(MC7_PORT, 0xcb); + if ((tmp = mad_read(MC7_PORT)) != 0xcb) + { + DDB(printk("MC7 not writable2 (%x)\n", tmp)); + return 0; + } + + tmp = mad_read(MC0_PORT+18); + if (tmp == 0xff || tmp == 0x00) + return 1; + /* We probably have a C931 */ + DDB(printk("Detected C931 config=0x%02x\n", tmp)); + c931_detected = 1; + + /* + * We cannot configure the chip if it is in PnP mode. + * If we have a CSN assigned (bit 8 in MC13) we first try + * a software reset, then a software power off, finally + * Clearing PnP mode. The last option is not + * Bit 8 in MC13 + */ + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Software reset */ + mad_write(MC9_PORT, 0x02); + mad_write(MC9_PORT, 0x00); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Power off, and on again */ + mad_write(MC9_PORT, 0xc2); + mad_write(MC9_PORT, 0xc0); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + +#if 0 + /* Force off PnP mode. This is not recommended because + * the PnP bios will not recognize the chip on the next + * warm boot and may assignd different resources to other + * PnP/PCI cards. + */ + mad_write(MC0_PORT+17, 0x04); +#endif + return 1; +} + +static int __init detect_mad16(void) +{ + unsigned char tmp, tmp2, bit; + int i, port; + + /* + * Check that reading a register doesn't return bus float (0xff) + * when the card is accessed using password. This may fail in case + * the card is in low power mode. Normally at least the power saving + * mode bit should be 0. + */ + + if ((tmp = mad_read(MC1_PORT)) == 0xff) + { + DDB(printk("MC1_PORT returned 0xff\n")); + return 0; + } + for (i = 0xf8d; i <= 0xf98; i++) + if (!c924pnp) + DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))); + else + DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i))); + + if (board_type == C930) + return detect_c930(); + + /* + * Now check that the gate is closed on first I/O after writing + * the password. (This is how a MAD16 compatible card works). + */ + + if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */ + { + DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); + return 0; + } + + bit = (c924pnp) ? 0x20 : 0x80; + port = (c924pnp) ? MC2_PORT : MC1_PORT; + + tmp = mad_read(port); + mad_write(port, tmp ^ bit); /* Toggle a bit */ + if ((tmp2 = mad_read(port)) != (tmp ^ bit)) /* Compare the bit */ + { + mad_write(port, tmp); /* Restore */ + DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); + return 0; + } + mad_write(port, tmp); /* Restore */ + return 1; /* Bingo */ +} + +static int __init wss_init(struct address_info *hw_config) +{ + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00. + */ + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 && + (inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 9 && inb(hw_config->io_base + 3) & 0x80) + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 1; +} + +static void __init init_c930(struct address_info *hw_config, int base) +{ + unsigned char cfg = 0; + + cfg |= (0x0f & mad16_conf); + + if(c931_detected) + { + /* Bit 0 has reversd meaning. Bits 1 and 2 sese + reversed on write. + Support only IDE cdrom. IDE port programmed + somewhere else. */ + cfg = (cfg & 0x09) ^ 0x07; + } + cfg |= base << 4; + mad_write(MC1_PORT, cfg); + + /* MC2 is CD configuration. Don't touch it. */ + + mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ + + /* bit 2 of MC4 reverses it's meaning between the C930 + and the C931. */ + cfg = c931_detected ? 0x04 : 0x00; + + if(mad16_cdsel & 0x20) + mad_write(MC4_PORT, 0x62|cfg); /* opl4 */ + else + mad_write(MC4_PORT, 0x52|cfg); /* opl3 */ + + mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ + mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ + mad_write(MC7_PORT, 0xCB); + mad_write(MC10_PORT, 0x11); +} + +static int __init chip_detect(void) +{ + int i; + + /* + * Then try to detect with the old password + */ + board_type = C924; + + DDB(printk("Detect using password = 0xE5\n")); + + if (detect_mad16()) { + return 1; + } + + board_type = C928; + + DDB(printk("Detect using password = 0xE2\n")); + + if (detect_mad16()) + { + unsigned char model; + + if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { + DDB(printk("mad16.c: Mozart detected\n")); + board_type = MOZART; + } else { + DDB(printk("mad16.c: 82C928 detected???\n")); + board_type = C928; + } + return 1; + } + + board_type = C929; + + DDB(printk("Detect using password = 0xE3\n")); + + if (detect_mad16()) + { + DDB(printk("mad16.c: 82C929 detected\n")); + return 1; + } + + if (inb(PASSWD_REG) != 0xff) + return 0; + + /* + * First relocate MC# registers to 0xe0e/0xe0f, disable password + */ + + outb((0xE4), PASSWD_REG); + outb((0x80), PASSWD_REG); + + board_type = C930; + + DDB(printk("Detect using password = 0xE4\n")); + + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printk("port %03x = %02x\n", i, mad_read(i))); + + if(detect_mad16()) { + DDB(printk("mad16.c: 82C930 detected\n")); + return 1; + } + + /* The C931 has the password reg at F8D */ + outb((0xE4), 0xF8D); + outb((0x80), 0xF8D); + DDB(printk("Detect using password = 0xE4 for C931\n")); + + if (detect_mad16()) { + return 1; + } + + board_type = C924; + c924pnp++; + DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n")); + if (detect_mad16()) { + DDB(printk("mad16.c: 82C924 PnP detected\n")); + return 1; + } + + c924pnp=0; + + return 0; +} + +static int __init probe_mad16(struct address_info *hw_config) +{ + int i; + unsigned char tmp; + unsigned char cs4231_mode = 0; + + int ad_flags = 0; + + signed char bits; + + static char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; + int dma = hw_config->dma, dma2 = hw_config->dma2; + unsigned char dma2_bit = 0; + int base; + struct resource *ports; + + mad16_osp = hw_config->osp; + + switch (hw_config->io_base) { + case 0x530: + base = 0; + break; + case 0xe80: + base = 1; + break; + case 0xf40: + base = 2; + break; + case 0x604: + base = 3; + break; + default: + printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); + return 0; + } + + if (dma != 0 && dma != 1 && dma != 3) { + printk(KERN_ERR "MSS: Bad DMA %d\n", dma); + return 0; + } + + /* + * Check that all ports return 0xff (bus float) when no password + * is written to the password register. + */ + + DDB(printk("--- Detecting MAD16 / Mozart ---\n")); + if (!chip_detect()) + return 0; + + switch (hw_config->irq) { + case 7: + bits = 8; + break; + case 9: + bits = 0x10; + break; + case 10: + bits = 0x18; + break; + case 12: + bits = 0x20; + break; + case 5: /* Also IRQ5 is possible on C930 */ + if (board_type == C930 || c924pnp) { + bits = 0x28; + break; + } + default: + printk(KERN_ERR "MAD16/Mozart: Bad IRQ %d\n", hw_config->irq); + return 0; + } + + ports = request_region(hw_config->io_base + 4, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (!request_region(hw_config->io_base, 4, "mad16 WSS config")) { + release_region(hw_config->io_base + 4, 4); + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + + if (board_type == C930) { + init_c930(hw_config, base); + goto got_it; + } + + for (i = 0xf8d; i <= 0xf93; i++) { + if (!c924pnp) + DDB(printk("port %03x = %02x\n", i, mad_read(i))); + else + DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i))); + } + +/* + * Set the WSS address + */ + + tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ + tmp |= base << 4; /* WSS port select bits */ + + /* + * Set optional CD-ROM and joystick settings. + */ + + tmp &= ~0x0f; + tmp |= (mad16_conf & 0x0f); /* CD-ROM and joystick bits */ + mad_write(MC1_PORT, tmp); + + tmp = mad16_cdsel; + mad_write(MC2_PORT, tmp); + mad_write(MC3_PORT, 0xf0); /* Disable SB */ + + if (board_type == C924) /* Specific C924 init values */ + { + mad_write(MC4_PORT, 0xA0); + mad_write(MC5_PORT, 0x05); + mad_write(MC6_PORT, 0x03); + } + if (!ad1848_detect(ports, &ad_flags, mad16_osp)) + goto fail; + + if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) + cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ + + if (board_type == C929) + { + mad_write(MC4_PORT, 0xa2); + mad_write(MC5_PORT, 0xA5 | cs4231_mode); + mad_write(MC6_PORT, 0x03); /* Disable MPU401 */ + } + else + { + mad_write(MC4_PORT, 0x02); + mad_write(MC5_PORT, 0x30 | cs4231_mode); + } + + for (i = 0xf8d; i <= 0xf93; i++) { + if (!c924pnp) + DDB(printk("port %03x after init = %02x\n", i, mad_read(i))); + else + DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); + } + +got_it: + ad_flags = 0; + if (!ad1848_detect(ports, &ad_flags, mad16_osp)) + goto fail; + + if (!wss_init(hw_config)) + goto fail; + + /* + * Set the IRQ and DMA addresses. + */ + + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[IRQ Conflict?]\n"); + + /* + * Handle the capture DMA channel + */ + + if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) + { + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk("MAD16: Invalid capture DMA\n"); + dma2 = dma; + } + } + else dma2 = dma; + + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ + + hw_config->slots[0] = ad1848_init("mad16 WSS", ports, + hw_config->irq, + dma, + dma2, 0, + hw_config->osp, + THIS_MODULE); + return 1; + +fail: + release_region(hw_config->io_base + 4, 4); + release_region(hw_config->io_base, 4); + return 0; +} + +static int __init probe_mad16_mpu(struct address_info *hw_config) +{ + unsigned char tmp; + + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + +#ifdef CONFIG_MAD16_OLDCARD + + tmp = mad_read(MC3_PORT); + + /* + * MAD16 SB base is defined by the WSS base. It cannot be changed + * alone. + * Ignore configured I/O base. Use the active setting. + */ + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + switch (hw_config->irq) + { + case 5: + tmp = (tmp & 0x3f) | 0x80; + break; + case 7: + tmp = (tmp & 0x3f); + break; + case 11: + tmp = (tmp & 0x3f) | 0x40; + break; + default: + printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n"); + return 0; + } + + mad_write(MC3_PORT, tmp | 0x04); + hw_config->driver_use_1 = SB_MIDI_ONLY; + if (!request_region(hw_config->io_base, 16, "soundblaster")) + return 0; + if (!sb_dsp_detect(hw_config, 0, 0, NULL)) { + release_region(hw_config->io_base, 16); + return 0; + } + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + hw_config->name = "Mad16/Mozart"; + sb_dsp_init(hw_config, THIS_MODULE); + return 1; +#else + /* assuming all later Mozart cards are identified as + * either 82C928 or Mozart. If so, following code attempts + * to set MPU register. TODO - add probing + */ + + tmp = mad_read(MC8_PORT); + + switch (hw_config->irq) + { + case 5: + tmp |= 0x08; + break; + case 7: + tmp |= 0x10; + break; + case 9: + tmp |= 0x18; + break; + case 10: + tmp |= 0x20; + break; + case 11: + tmp |= 0x28; + break; + default: + printk(KERN_ERR "mad16/MOZART: invalid mpu_irq\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x300: + tmp |= 0x01; + break; + case 0x310: + tmp |= 0x03; + break; + case 0x320: + tmp |= 0x05; + break; + case 0x330: + tmp |= 0x07; + break; + default: + printk(KERN_ERR "mad16/MOZART: invalid mpu_io\n"); + return 0; + } + + mad_write(MC8_PORT, tmp); /* write MPU port parameters */ + goto probe_401; +#endif + } + tmp = mad_read(MC6_PORT) & 0x83; + tmp |= 0x80; /* MPU-401 enable */ + + /* Set the MPU base bits */ + + switch (hw_config->io_base) + { + case 0x300: + tmp |= 0x60; + break; + case 0x310: + tmp |= 0x40; + break; + case 0x320: + tmp |= 0x20; + break; + case 0x330: + tmp |= 0x00; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base); + return 0; + } + + /* Set the MPU IRQ bits */ + + switch (hw_config->irq) + { + case 5: + tmp |= 0x10; + break; + case 7: + tmp |= 0x18; + break; + case 9: + tmp |= 0x00; + break; + case 10: + tmp |= 0x08; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq); + break; + } + + mad_write(MC6_PORT, tmp); /* Write MPU401 config */ + +#ifndef CONFIG_MAD16_OLDCARD +probe_401: +#endif + hw_config->driver_use_1 = SB_MIDI_ONLY; + hw_config->name = "Mad16/Mozart"; + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_mad16(struct address_info *hw_config) +{ + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0); + release_region(hw_config->io_base, 4); + sound_unload_audiodev(hw_config->slots[0]); +} + +static void __exit unload_mad16_mpu(struct address_info *hw_config) +{ +#ifdef CONFIG_MAD16_OLDCARD + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + sb_dsp_unload(hw_config, 0); + return; + } +#endif + + unload_uart401(hw_config); +} + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int found_mpu; + +static int __initdata mpu_io = 0; +static int __initdata mpu_irq = 0; +static int __initdata io = -1; +static int __initdata dma = -1; +static int __initdata dma16 = -1; /* Set this for modules that need it */ +static int __initdata irq = -1; +static int __initdata cdtype = 0; +static int __initdata cdirq = 0; +static int __initdata cdport = 0x340; +static int __initdata cddma = -1; +static int __initdata opl4 = 0; +static int __initdata joystick = 0; + +module_param(mpu_io, int, 0); +module_param(mpu_irq, int, 0); +module_param(io, int, 0); +module_param(dma, int, 0); +module_param(dma16, int, 0); +module_param(irq, int, 0); +module_param(cdtype, int, 0); +module_param(cdirq, int, 0); +module_param(cdport, int, 0); +module_param(cddma, int, 0); +module_param(opl4, int, 0); +module_param(joystick, bool, 0); +module_param(debug, bool, 0644); + +static int __initdata dma_map[2][8] = +{ + {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02}, + {0x03, -1, 0x01, 0x00, -1, -1, -1, -1} +}; + +static int __initdata irq_map[16] = +{ + 0x00, -1, -1, 0x0A, + -1, 0x04, -1, 0x08, + -1, 0x10, 0x14, 0x18, + -1, -1, -1, -1 +}; + +#ifdef SUPPORT_JOYSTICK + +static struct gameport *gameport; + +static int __devinit mad16_register_gameport(int io_port) +{ + if (!request_region(io_port, 1, "mad16 gameport")) { + printk(KERN_ERR "mad16: gameport address 0x%#x already in use\n", io_port); + return -EBUSY; + } + + gameport = gameport_allocate_port(); + if (!gameport) { + printk(KERN_ERR "mad16: can not allocate memory for gameport\n"); + release_region(io_port, 1); + return -ENOMEM; + } + + gameport_set_name(gameport, "MAD16 Gameport"); + gameport_set_phys(gameport, "isa%04x/gameport0", io_port); + gameport->io = io_port; + + gameport_register_port(gameport); + + return 0; +} + +static inline void mad16_unregister_gameport(void) +{ + if (gameport) { + /* the gameport was initialized so we must free it up */ + gameport_unregister_port(gameport); + gameport = NULL; + release_region(0x201, 1); + } +} +#else +static inline int mad16_register_gameport(int io_port) { return -ENOSYS; } +static inline void mad16_unregister_gameport(void) { } +#endif + +static int __devinit init_mad16(void) +{ + int dmatype = 0; + + printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + printk(KERN_INFO "CDROM "); + switch (cdtype) + { + case 0x00: + printk("Disabled"); + cdirq = 0; + break; + case 0x02: + printk("Sony CDU31A"); + dmatype = 1; + if(cddma == -1) cddma = 3; + break; + case 0x04: + printk("Mitsumi"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + case 0x06: + printk("Panasonic Lasermate"); + dmatype = 1; + if(cddma == -1) cddma = 3; + break; + case 0x08: + printk("Secondary IDE"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + case 0x0A: + printk("Primary IDE"); + dmatype = 0; + if(cddma == -1) cddma = 5; + break; + default: + printk("\n"); + printk(KERN_ERR "Invalid CDROM type\n"); + return -EINVAL; + } + + /* + * Build the config words + */ + + mad16_conf = (joystick ^ 1) | cdtype; + mad16_cdsel = 0; + if (opl4) + mad16_cdsel |= 0x20; + + if(cdtype){ + if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) + { + printk("\n"); + printk(KERN_ERR "Invalid CDROM DMA\n"); + return -EINVAL; + } + if (cddma) + printk(", DMA %d", cddma); + else + printk(", no DMA"); + + if (!cdirq) + printk(", no IRQ"); + else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) + { + printk(", invalid IRQ (disabling)"); + cdirq = 0; + } + else printk(", IRQ %d", cdirq); + + mad16_cdsel |= dma_map[dmatype][cddma]; + + if (cdtype < 0x08) + { + switch (cdport) + { + case 0x340: + mad16_cdsel |= 0x00; + break; + case 0x330: + mad16_cdsel |= 0x40; + break; + case 0x360: + mad16_cdsel |= 0x80; + break; + case 0x320: + mad16_cdsel |= 0xC0; + break; + default: + printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); + return -EINVAL; + } + } + mad16_cdsel |= irq_map[cdirq]; + } + + printk(".\n"); + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma16; + + if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { + printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); + return -EINVAL; + } + + if (!request_region(MC0_PORT, 12, "mad16")) + return -EBUSY; + + if (!probe_mad16(&cfg)) { + release_region(MC0_PORT, 12); + return -ENODEV; + } + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + found_mpu = probe_mad16_mpu(&cfg_mpu); + + if (joystick) + mad16_register_gameport(0x201); + + return 0; +} + +static void __exit cleanup_mad16(void) +{ + if (found_mpu) + unload_mad16_mpu(&cfg_mpu); + mad16_unregister_gameport(); + unload_mad16(&cfg); + release_region(MC0_PORT, 12); +} + +module_init(init_mad16); +module_exit(cleanup_mad16); + +#ifndef MODULE +static int __init setup_mad16(char *str) +{ + /* io, irq */ + int ints[8]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma16 = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + joystick = ints[7]; + + return 1; +} + +__setup("mad16=", setup_mad16); +#endif +MODULE_LICENSE("GPL"); diff --git a/trunk/sound/oss/maestro.c b/trunk/sound/oss/maestro.c new file mode 100644 index 000000000000..1d98d100d739 --- /dev/null +++ b/trunk/sound/oss/maestro.c @@ -0,0 +1,3686 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.[23].x + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * (c) Copyright 1999 Alan Cox + * + * Based heavily on SonicVibes.c: + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * Heavily modified by Zach Brown based on lunch + * with ESS engineers. Many thanks to Howard Kim for providing + * contacts and hardware. Honorable mention goes to Eric + * Brombaugh for all sorts of things. Best regards to the + * proprietors of Hack Central for fine lodging. + * + * Supported devices: + * /dev/dsp0-3 standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASSP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASSP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASSP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * Each APU can do a number of things, but we only really use + * 3 basic functions. For playback we use them to convert PCM + * data fetched over PCI by the wavecahche into analog data that + * is handed to the codec. One APU for mono, and a pair for stereo. + * When in stereo, the combination of smarts in the APU and Wavecache + * decide which wavecache gets the left or right channel. + * + * For record we still use the old overly mono system. For each in + * coming channel the data comes in from the codec, through a 'input' + * APU, through another rate converter APU, and then into memory via + * the wavecache and PCI. If its stereo, we mash it back into LRLR in + * software. The pass between the 2 APUs is supposedly what requires us + * to have a 512 byte buffer sitting around in wavecache/memory. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + * + * This driver has brute force APM suspend support. We catch suspend + * notifications and stop all work being done on the chip. Any people + * that try between this shutdown and the real suspend operation will + * be put to sleep. When we resume we restore our software state on + * the chip and wake up the people that were using it. The code thats + * being used now is quite dirty and assumes we're on a uni-processor + * machine. Much of it will need to be cleaned up for SMP ACPI or + * similar. + * + * We also pay attention to PCI power management now. The driver + * will power down units of the chip that it knows aren't needed. + * The WaveProcessor and company are only powered on when people + * have /dev/dsp*s open. On removal the driver will + * power down the maestro entirely. There could still be + * trouble with BIOSen that magically change power states + * themselves, but we'll see. + * + * History + * v0.15 - May 21 2001 - Marcus Meissner + * Ported to Linux 2.4 PCI API. Some clean ups, global devs list + * removed (now using pci device driver data). + * PM needs to be polished still. Bumped version. + * (still kind of v0.14) May 13 2001 - Ben Pfaff + * Add support for 978 docking and basic hardware volume control + * (still kind of v0.14) Nov 23 - Alan Cox + * Add clocking= for people with seriously warped hardware + * (still v0.14) Nov 10 2000 - Bartlomiej Zolnierkiewicz + * add __init to maestro_ac97_init() and maestro_install() + * (still based on v0.14) Mar 29 2000 - Zach Brown + * move to 2.3 power management interface, which + * required hacking some suspend/resume/check paths + * make static compilation work + * v0.14 - Jan 28 2000 - Zach Brown + * add PCI power management through ACPI regs. + * we now shut down on machine reboot/halt + * leave scary PCI config items alone (isa stuff, mostly) + * enable 1921s, it seems only mine was broke. + * fix swapped left/right pcm dac. har har. + * up bob freq, increase buffers, fix pointers at underflow + * silly compilation problems + * v0.13 - Nov 18 1999 - Zach Brown + * fix nec Versas? man would that be cool. + * v0.12 - Nov 12 1999 - Zach Brown + * brown bag volume max fix.. + * v0.11 - Nov 11 1999 - Zach Brown + * use proper stereo apu decoding, mmap/write should work. + * make volume sliders more useful, tweak rate calculation. + * fix lame 8bit format reporting bug. duh. apm apu saving buglet also + * fix maestro 1 clock freq "bug", remove pt101 support + * v0.10 - Oct 28 1999 - Zach Brown + * aha, so, sometimes the WP writes a status word to offset 0 + * from one of the PCMBARs. rearrange allocation accordingly.. + * cheers again to Eric for being a good hacker in investigating this. + * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) + * v0.09 - Oct 23 1999 - Zach Brown + * added APM support. + * re-order something such that some 2Es now work. Magic! + * new codec reset routine. made some codecs come to life. + * fix clear_advance, sync some control with ESS. + * now write to all base regs to be paranoid. + * v0.08 - Oct 20 1999 - Zach Brown + * Fix initial buflen bug. I am so smart. also smp compiling.. + * I owe Eric yet another beer: fixed recmask, igain, + * muting, and adc sync consistency. Go Team. + * v0.07 - Oct 4 1999 - Zach Brown + * tweak adc/dac, formating, and stuff to allow full duplex + * allocate dsps memory at open() so we can fit in the wavecache window + * fix wavecache braindamage. again. no more scribbling? + * fix ess 1921 codec bug on some laptops. + * fix dumb pci scanning bug + * started 2.3 cleanup, redid spinlocks, little cleanups + * v0.06 - Sep 20 1999 - Zach Brown + * fix wavecache thinkos. limit to 1 /dev/dsp. + * eric is wearing his thinking toque this week. + * spotted apu mode bugs and gain ramping problem + * don't touch weird mixer regs, make recmask optional + * fixed igain inversion, defaults for mixers, clean up rec_start + * make mono recording work. + * report subsystem stuff, please send reports. + * littles: parallel out, amp now + * v0.05 - Sep 17 1999 - Zach Brown + * merged and fixed up Eric's initial recording code + * munged format handling to catch misuse, needs rewrite. + * revert ring bus init, fixup shared int, add pci busmaster setting + * fix mixer oss interface, fix mic mute and recmask + * mask off unsupported mixers, reset with all 1s, modularize defaults + * make sure bob is running while we need it + * got rid of device limit, initial minimal apm hooks + * pull out dead code/includes, only allow multimedia/audio maestros + * v0.04 - Sep 01 1999 - Zach Brown + * copied memory leak fix from sonicvibes driver + * different ac97 reset, play with 2.0 ac97, simplify ring bus setup + * bob freq code, region sanity, jitter sync fix; all from Eric + * + * TODO + * fix bob frequency + * endianness + * do smart things with ac97 2.0 bits. + * dual codecs + * leave 54->61 open + * + * it also would be fun to have a mode that would not use pci dma at all + * but would copy into the wavecache on board memory and use that + * on architectures that don't like the maestro's pci dma ickiness. + */ + +/*****************************************************************************/ + +#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 "maestro.h" + +static struct pci_driver maestro_pci_driver; + +/* --------------------------------------------------------------------- */ + +#define M_DEBUG 1 + +#ifdef M_DEBUG +static int debug; +#define M_printk(args...) {if (debug) printk(args);} +#else +#define M_printk(x) +#endif + +/* we try to setup 2^(dsps_order) /dev/dsp devices */ +static int dsps_order; +/* whether or not we mess around with power management */ +static int use_pm=2; /* set to 1 for force */ +/* clocking for broken hardware - a few laptops seem to use a 50Khz clock + ie insmod with clocking=50000 or so */ + +static int clocking=48000; + +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +module_param(debug, bool, 0644); +#endif +module_param(dsps_order, int, 0); +module_param(use_pm, int, 0); +module_param(clocking, int, 0); + +/* --------------------------------------------------------------------- */ +#define DRIVER_VERSION "0.15" + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, + the people the maestro + was bought from */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ +#endif /* PCI_VENDOR_ESS */ + +#define ESS_CHAN_HARD 0x100 + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + + +/* changed so that I could actually find all the + references and fix them up. it's a little more readable now. */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define ESS_STATE_MAGIC 0x125D1968 +#define ESS_CARD_MAGIC 0x19283746 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define MAX_DSP_ORDER 2 +#define MAX_DSPS (1<src buffer page */ + void *mixbuf; + +}; + +struct ess_card { + unsigned int magic; + + /* We keep maestro cards in a linked list */ + struct ess_card *next; + + int dev_mixer; + + int card_type; + + /* as most of this is static, + perhaps it should be a pointer to a global struct */ + struct mixer_goo { + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + /* the caller must guarantee arg sanity before calling these */ +/* int (*read_mixer)(struct ess_card *card, int index);*/ + void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); + int (*recmask_io)(struct ess_card *card,int rw,int mask); + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + } mix; + + int power_regs; + + int in_suspend; + wait_queue_head_t suspend_queue; + + struct ess_state channels[MAX_DSPS]; + u16 maestro_map[NR_IDRS]; /* Register map */ + /* we have to store this junk so that we can come back from a + suspend */ + u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* memory for this card.. wavecache limited :(*/ + void *dmapages; + int dmaorder; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int bob_freq; + char dsps_open; + + int dock_mute_vol; +}; + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ); + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +/* --------------------------------------------------------------------- */ + +static void check_suspend(struct ess_card *card); + +/* --------------------------------------------------------------------- */ + + +/* + * ESS Maestro AC97 codec programming interface. + */ + +static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) +{ + int io = card->iobase; + int i; + /* + * Wait for the codec bus to be free + */ + + check_suspend(card); + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + /* + * Write the bus + */ + outw(val, io+ESS_AC97_DATA); + mdelay(1); + outb(cmd, io+ESS_AC97_INDEX); + mdelay(1); +} + +static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) +{ + int io = card->iobase; + int sanity=10000; + u16 data; + int i; + + check_suspend(card); + /* + * Wait for the codec bus to be free + */ + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + + outb(cmd|0x80, io+ESS_AC97_INDEX); + mdelay(1); + + while(inb(io+ESS_AC97_INDEX)&1) + { + sanity--; + if(!sanity) + { + printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); + return 0; + } + } + data=inw(io+ESS_AC97_DATA); + mdelay(1); + return data; +} + +/* OSS interface to the ac97s.. */ + +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ + SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ + SOUND_MASK_SPEAKER) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1<offset); + + if(AC97_STEREO_MASK & (1<> 8) & 0x7f; + right = val & 0x7f; + + if (mixer == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (mixer == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (mixer == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + + M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); + + return ret; +} +#endif + +/* write the OSS encoded volume to the given OSS encoded mixer, + again caller's job to make sure all is well in arg land, + call with spinlock held */ + +/* linear scale -> log */ +static unsigned char lin2log[101] = +{ +0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , +50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , +63 , 65 , 66 , 67 , 68 , 69 , 69 , 70 , 71 , +72 , 73 , 73 , 74 , 75 , 75 , 76 , 77 , 77 , +78 , 78 , 79 , 80 , 80 , 81 , 81 , 82 , 82 , +83 , 83 , 84 , 84 , 84 , 85 , 85 , 86 , 86 , +87 , 87 , 87 , 88 , 88 , 88 , 89 , 89 , 89 , +90 , 90 , 90 , 91 , 91 , 91 , 92 , 92 , 92 , +93 , 93 , 93 , 94 , 94 , 94 , 94 , 95 , 95 , +95 , 95 , 96 , 96 , 96 , 96 , 97 , 97 , 97 , +97 , 98 , 98 , 98 , 98 , 99 , 99 , 99 , 99 , 99 +}; + +static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, unsigned int right) +{ + u16 val=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); + + if(AC97_STEREO_MASK & (1<scale) / 100; + left = (left * mh->scale) / 100; + if ((left == 0) && (right == 0)) + val |= 0x8000; + } else if (mixer == SOUND_MIXER_PCM || mixer == SOUND_MIXER_CD) { + /* log conversion seems bad for them */ + if ((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + } else { + /* log conversion for the stereo controls */ + if((left == 0) && (right == 0)) + val = 0x8000; + right = ((100 - lin2log[right]) * mh->scale) / 100; + left = ((100 - lin2log[left]) * mh->scale) / 100; + } + + val |= (left << 8) | right; + + } else if (mixer == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (mixer == SOUND_MIXER_MIC) { + val = maestro_ac97_get(card, mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + val = maestro_ac97_get(card , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (mixer == SOUND_MIXER_TREBLE) { + val = maestro_ac97_get(card , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + + maestro_ac97_set(card , mh->offset, val); + + M_printk(" -> %x\n",val); +} + +/* the following tables allow us to go from + OSS <-> ac97 quickly. */ + +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_oss_mask[] = { + [AC97_REC_MIC] = SOUND_MASK_MIC, + [AC97_REC_CD] = SOUND_MASK_CD, + [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, + [AC97_REC_AUX] = SOUND_MASK_LINE1, + [AC97_REC_LINE] = SOUND_MASK_LINE, + [AC97_REC_PHONE] = SOUND_MASK_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* read or write the recmask + the ac97 can really have left and right recording + inputs independently set, but OSS doesn't seem to + want us to express that to the user. + the caller guarantees that we have a supported bit set, + and they must be holding the card's spinlock */ +static int +ac97_recmask_io(struct ess_card *card, int read, int mask) +{ + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; + + if (read) return val; + + /* oss can have many inputs, maestro can't. try + to pick the 'new' one */ + + if (mask != val) mask &= ~val; + + val = ffs(mask) - 1; + val = ac97_oss_rm[val]; + val |= val << 8; /* set both channels */ + + M_printk("maestro: setting ac97 recmask to 0x%x\n",val); + + maestro_ac97_set(card,0x1a,val); + + return 0; +}; + +/* + * The Maestro can be wired to a standard AC97 compliant codec + * (see www.intel.com for the pdf's on this), or to a PT101 codec + * which appears to be the ES1918 (data sheet on the esstech.com.tw site) + * + * The PT101 setup is untested. + */ + +static u16 __init maestro_ac97_init(struct ess_card *card) +{ + u16 vend1, vend2, caps; + + card->mix.supported_mixers = AC97_SUPPORTED_MASK; + card->mix.stereo_mixers = AC97_STEREO_MASK; + card->mix.record_sources = AC97_RECORD_MASK; +/* card->mix.read_mixer = ac97_read_mixer;*/ + card->mix.write_mixer = ac97_write_mixer; + card->mix.recmask_io = ac97_recmask_io; + + vend1 = maestro_ac97_get(card, 0x7c); + vend2 = maestro_ac97_get(card, 0x7e); + + caps = maestro_ac97_get(card, 0x00); + + printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", + vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); + + if (! (caps & 0x4) ) { + /* no bass/treble nobs */ + card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + + /* XXX endianness, dork head. */ + /* vendor specifc bits.. */ + switch ((long)(vend1 << 16) | vend2) { + case 0x545200ff: /* TriTech */ + /* no idea what this does */ + maestro_ac97_set(card,0x2a,0x0001); + maestro_ac97_set(card,0x2c,0x0000); + maestro_ac97_set(card,0x2c,0xffff); + break; +#if 0 /* i thought the problems I was seeing were with + the 1921, but apparently they were with the pci board + it was on, so this code is commented out. + lets see if this holds true. */ + case 0x83847609: /* ESS 1921 */ + /* writing to 0xe (mic) or 0x1a (recmask) seems + to hang this codec */ + card->mix.supported_mixers &= ~(SOUND_MASK_MIC); + card->mix.record_sources = 0; + card->mix.recmask_io = NULL; +#if 0 /* don't ask. I have yet to see what these actually do. */ + maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + udelay(20); + maestro_ac97_set(card,0x78,0x3002); + udelay(20); + maestro_ac97_set(card,0x78,0x3802); + udelay(20); +#endif + break; +#endif + default: break; + } + + maestro_ac97_set(card, 0x1E, 0x0404); + /* null misc stuff */ + maestro_ac97_set(card, 0x20, 0x0000); + + return 0; +} + +#if 0 /* there has been 1 person on the planet with a pt101 that we + know of. If they care, they can put this back in :) */ +static u16 maestro_pt101_init(struct ess_card *card,int iobase) +{ + printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + maestro_ac97_set(iobase, 0x2A, 0x0001); + maestro_ac97_set(iobase, 0x2C, 0x0000); + maestro_ac97_set(iobase, 0x2C, 0xFFFF); + maestro_ac97_set(iobase, 0x10, 0x9F1F); + maestro_ac97_set(iobase, 0x12, 0x0808); + maestro_ac97_set(iobase, 0x14, 0x9F1F); + maestro_ac97_set(iobase, 0x16, 0x9F1F); + maestro_ac97_set(iobase, 0x18, 0x0404); + maestro_ac97_set(iobase, 0x1A, 0x0000); + maestro_ac97_set(iobase, 0x1C, 0x0000); + maestro_ac97_set(iobase, 0x02, 0x0404); + maestro_ac97_set(iobase, 0x04, 0x0808); + maestro_ac97_set(iobase, 0x0C, 0x801F); + maestro_ac97_set(iobase, 0x0E, 0x801F); + return 0; +} +#endif + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + u32 vend; + + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* reset the first codec */ + outw(0x0000, ioaddr+0x36); + save_68 = inw(ioaddr+0x68); + pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &vend); + if( w & 0x1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ + outw(0x0001, ioaddr + 0x68); + outw(0x0000, ioaddr + 0x60); + udelay(20); + outw(0x0001, ioaddr + 0x60); + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); + outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); + outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); + + /* now the second codec */ + outw(0x0000, ioaddr+0x36); + outw(0xfff7, ioaddr + 0x64); + save_68 = inw(ioaddr+0x68); + outw(0x0009, ioaddr + 0x68); + outw(0x0001, ioaddr + 0x60); + udelay(20); + outw(0x0009, ioaddr + 0x60); + mdelay(500); /* .. ouch.. */ + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + M_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80|0x7c, ioaddr + 0x30); + for (w=0; ; w++) { + if ((inw(ioaddr+ 0x30) & 1) == 0) { + if(inb(ioaddr + 0x32) !=0) break; + + outb(0x80|0x7d, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + outb(0x80|0x7f, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + } + + if( w > 10000) { + outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); + udelay(1); + outw( 0x80, ioaddr+0x30); + for(w = 0 ; w < 10000; w++) { + if((inw(ioaddr + 0x30) & 1) ==0) break; + } + } + } +#endif + if ( vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) { + /* turn on external amp? */ + outw(0xf9ff, ioaddr + 0x64); + outw(inw(ioaddr+0x68) | 0x600, ioaddr + 0x68); + outw(0x0209, ioaddr + 0x60); + } + + /* Turn on the 978 docking chip. + First frob the "master output enable" bit, + then set most of the playback volume control registers to max. */ + outb(inb(ioaddr+0xc0)|(1<<5), ioaddr+0xc0); + outb(0xff, ioaddr+0xc3); + outb(0xff, ioaddr+0xc4); + outb(0xff, ioaddr+0xc6); + outb(0xff, ioaddr+0xc8); + outb(0x3f, ioaddr+0xcf); + outb(0x3f, ioaddr+0xd0); +} +/* + * Indirect register access. Not all registers are readable so we + * need to keep register state ourselves + */ + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* + * The Maestro engineers were a little indirection happy. These indirected + * registers themselves include indirect registers at another layer + */ + +static void __maestro_write(struct ess_card *card, u16 reg, u16 data) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + outw(data, ioaddr+0x00); + if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); + else card->maestro_map[reg]=data; + +} + +static void maestro_write(struct ess_state *s, u16 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_write(s->card,reg,data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 __maestro_read(struct ess_card *card, u16 reg) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + return card->maestro_map[reg]=inw(ioaddr+0x00); +} + +static u16 maestro_read(struct ess_state *s, u16 reg) +{ + if(READABLE_MAP & (1<card); + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_read(s->card,reg); + + spin_unlock_irqrestore(&s->card->lock,flags); + } + return s->card->maestro_map[reg]; +} + +/* + * These routines handle accessing the second level indirections to the + * wave ram. + */ + +/* + * The register names are the ones ESS uses (see 104T31.ZIP) + */ + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +static void apu_index_set(struct ess_card *card, u16 index) +{ + int i; + __maestro_write(card, IDR1_CRAM_POINTER, index); + for(i=0;i<1000;i++) + if(__maestro_read(card, IDR1_CRAM_POINTER)==index) + return; + printk(KERN_WARNING "maestro: APU register select failed.\n"); +} + +static void apu_data_set(struct ess_card *card, u16 data) +{ + int i; + for(i=0;i<1000;i++) + { + if(__maestro_read(card, IDR0_DATA_PORT)==data) + return; + __maestro_write(card, IDR0_DATA_PORT, data); + } +} + +/* + * This is the public interface for APU manipulation. It handles the + * interlock to avoid two APU writes in parallel etc. Don't diddle + * directly with the stuff above. + */ + +static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + + check_suspend(s->card); + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + { + if(channel>5) + printk("BAD CHANNEL %d.\n",channel); + else + channel = s->apu[channel]; + /* store based on real hardware apu/reg */ + s->card->apu_map[channel][reg]=data; + } + reg|=(channel<<4); + + /* hooray for double indirection!! */ + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + apu_data_set(s->card, data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + + check_suspend(s->card); + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + channel = s->apu[channel]; + + reg|=(channel<<4); + + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + v=__maestro_read(s->card, IDR0_DATA_PORT); + + spin_unlock_irqrestore(&s->card->lock,flags); + return v; +} + + +/* + * The wavecache buffers between the APUs and + * pci bus mastering + */ + +static void wave_set_register(struct ess_state *s, u16 reg, u16 value) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + check_suspend(s->card); + + spin_lock_irqsave(&s->card->lock,flags); + + outw(reg, ioaddr+0x10); + outw(value, ioaddr+0x12); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 wave_get_register(struct ess_state *s, u16 reg) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + u16 value; + check_suspend(s->card); + + spin_lock_irqsave(&s->card->lock,flags); + outw(reg, ioaddr+0x10); + value=inw(ioaddr+0x12); + spin_unlock_irqrestore(&s->card->lock,flags); + + return value; +} + +static void sound_reset(int ioaddr) +{ + outw(0x2000, 0x18+ioaddr); + udelay(1); + outw(0x0000, 0x18+ioaddr); + udelay(1); +} + +/* sets the play formats of these apus, should be passed the already shifted format */ +static void set_apu_fmt(struct ess_state *s, int apu, int mode) +{ + int apu_fmt = 0x10; + + if(!(mode&ESS_FMT_16BIT)) apu_fmt+=0x20; + if((mode&ESS_FMT_STEREO)) apu_fmt+=0x10; + s->apu_mode[apu] = apu_fmt; + s->apu_mode[apu+1] = apu_fmt; +} + +/* this only fixes the output apu mode to be later set by start_dac and + company. output apu modes are set in ess_rec_setup */ +static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) +{ + s->fmt = (s->fmt & mask) | data; + set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); +} + +/* this is off by a little bit.. */ +static u32 compute_rate(struct ess_state *s, u32 freq) +{ + u32 clock = clock_freq[s->card->card_type]; + + freq = (freq * clocking)/48000; + + if (freq == 48000) + return 0x10000; + + return ((freq / clock) <<16 )+ + (((freq % clock) << 16) / clock); +} + +static void set_dac_rate(struct ess_state *s, unsigned int rate) +{ + u32 freq; + int fmt = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->ratedac = rate; + + if(! (fmt & ESS_FMT_16BIT) && !(fmt & ESS_FMT_STEREO)) + rate >>= 1; + +/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 0, 3, freq>>8); + apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 1, 3, freq>>8); +} + +static void set_adc_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + /* Sample Rate conversion APUs don't like 0x10000 for their rate */ + if (rate > 47999) + rate = 47999; + if (rate < 4000) + rate = 4000; + + s->rateadc = rate; + + freq = compute_rate(s, rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 2, 3, freq>>8); + apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 3, 3, freq>>8); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + + apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 4, 3, freq>>8); + apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 5, 3, freq>>8); +} + +/* Stop our host of recording apus */ +static inline void stop_adc(struct ess_state *s) +{ + /* XXX lets hope we don't have to lock around this */ + if (! (s->enable & ADC_RUNNING)) return; + + s->enable &= ~ADC_RUNNING; + apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); + apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); +} + +/* stop output apus */ +static void stop_dac(struct ess_state *s) +{ + /* XXX have to lock around this? */ + if (! (s->enable & DAC_RUNNING)) return; + + s->enable &= ~DAC_RUNNING; + apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); + apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); +} + +static void start_dac(struct ess_state *s) +{ + /* XXX locks? */ + if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && + s->dma_dac.ready && + (! (s->enable & DAC_RUNNING)) ) { + + s->enable |= DAC_RUNNING; + + apu_set_register(s, 0, 0, + (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) + apu_set_register(s, 1, 0, + (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); + } +} + +static void start_adc(struct ess_state *s) +{ + /* XXX locks? */ + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready && (! (s->enable & ADC_RUNNING)) ) { + + s->enable |= ADC_RUNNING; + apu_set_register(s, 2, 0, + (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); + apu_set_register(s, 4, 0, + (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); + + if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + apu_set_register(s, 3, 0, + (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); + apu_set_register(s, 5, 0, + (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); + } + + } +} + + +/* + * Native play back driver + */ + +/* the mode passed should be already shifted and masked */ +static void +ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + u32 pa; + u32 tmpval; + int high_apu = 0; + int channel; + + M_printk("mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + if(mode&ESS_FMT_STEREO) { + high_apu++; + /* only 16/stereo gets size divided */ + if(mode&ESS_FMT_16BIT) + size>>=1; + } + + for(channel=0; channel <= high_apu; channel++) + { + pa = virt_to_bus(buffer); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + if(!(mode & ESS_FMT_16BIT)) tmpval |= 4; + if(mode & ESS_FMT_STEREO) tmpval |= 2; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on the left one */ + if(!channel) ess->dma_dac.base = pa&0xFFFF; + + pa|=0x00400000; /* System RAM */ + + /* XXX the 16bit here might not be needed.. */ + if((mode & ESS_FMT_STEREO) && (mode & ESS_FMT_16BIT)) { + if(channel) + pa|=0x00800000; /* Stereo */ + pa>>=1; + } + +/* XXX think about endianess when writing these registers */ + M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); + /* start of sample */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + apu_set_register(ess, channel, 5, pa&0xFFFF); + /* sample end */ + apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); + /* setting loop len == sample len */ + apu_set_register(ess, channel, 7, size); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(ess, channel, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(ess, channel, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + apu_set_register(ess, channel, 0, 0x400F); + + if(mode&ESS_FMT_16BIT) + ess->apu_mode[channel]=0x10; + else + ess->apu_mode[channel]=0x30; + + if(mode&ESS_FMT_STEREO) { + /* set panning: left or right */ + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); + ess->apu_mode[channel] += 0x10; + } else + apu_set_register(ess, channel, 10, 0x8F08); + } + + /* clear WP interrupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* go team! */ + set_dac_rate(ess,rate); + start_dac(ess); +} + +/* + * Native record driver + */ + +/* again, passed mode is alrady shifted/masked */ +static void +ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + int apu_step = 2; + int channel; + + M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + apu_step = 1; + } + + /* APU assignments: 2 = mono/left SRC + 3 = right SRC + 4 = mono/left Input Mixer + 5 = right Input Mixer */ + for(channel=2;channel<6;channel+=apu_step) + { + int i; + int bsize, route; + u32 pa; + u32 tmpval; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if(channel & 0x04) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if(!(channel & 0x01)) { + pa = virt_to_bus(ess->mixbuf); + } else { + pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); + } + + /* we source from a 'magic' apu */ + bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ + route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ + ess->apu_mode[channel] = 0x90; /* Input Mixer */ + + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if(!(channel & 0x01)) { + pa = virt_to_bus(buffer); + } else { + /* right channel records its split half. + *2 accommodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + } + + ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = channel + 2; + } + + M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + if(channel==2) ess->dma_adc.base = pa&0xFFFF; + + pa|=0x00400000; /* bit 22 -> System RAM */ + + M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", + ess->apu[channel], pa, bsize, route); + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + + apu_set_register(ess, channel, 0, 0x400F); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(ess, channel, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + /* XXX reg is little endian.. */ + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); + apu_set_register(ess, channel, 7, bsize); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x00F0); + + /* amplitude now? sure. why not. */ + apu_set_register(ess, channel, 9, 0x0000); + + /* set filter tune, radius, polar pan */ + apu_set_register(ess, channel, 10, 0x8F08); + + /* route input */ + apu_set_register(ess, channel, 11, route); + } + + /* clear WP interrupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* let 'er rip */ + set_adc_rate(ess,rate); + start_adc(ess); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmaa??\n"); +} + +static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmac??\n"); +} + +/* Playback pointer */ +static inline unsigned get_dmaa(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,0,5); + +/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ + + offset-=s->dma_dac.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* Record pointer */ +static inline unsigned get_dmac(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,2,5); + +/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ + + /* The offset is an address not a position relative to base */ + offset-=s->dma_adc.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* + * Meet Bob, the timer... + */ + +static irqreturn_t ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void stop_bob(struct ess_state *s) +{ + /* Mask IDR 11,17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); + maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); +} + +/* eventually we could be clever and limit bob ints + to the frequency at which our smallest duration + chunks may expire */ +#define ESS_SYSCLK 50000000 +static void start_bob(struct ess_state *s) +{ + int prescale; + int divide; + + /* XXX make freq selector much smarter, see calc_bob_rate */ + int freq = 200; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for(prescale=5;prescale<12;prescale++) + if(freq > (ESS_SYSCLK>>(prescale+9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide=1; + while((prescale > 5) && (divide<32)) + { + prescale--; + divide <<=1; + } + divide>>=1; + + /* now fine-tune the divider for best match */ + for(;divide<31;divide++) + if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) + break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if(divide == 0) + { + divide++; + if(prescale>5) + prescale--; + } + + maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)|1); + maestro_write(s, 0x17, maestro_read(s, 0x17)|1); +} +/* --------------------------------------------------------------------- */ + +/* this quickly calculates the frequency needed for bob + and sets it if its different than what bob is + currently running at. its called often so + needs to be fairly quick. */ +#define BOB_MIN 50 +#define BOB_MAX 400 +static void calc_bob_rate(struct ess_state *s) { +#if 0 /* this thing tries to set the frequency of bob such that + there are 2 interrupts / buffer walked by the dac/adc. That + is probably very wrong for people who actually care about + mid buffer positioning. it should be calculated as bytes/interrupt + and that needs to be decided :) so for now just use the static 150 + in start_bob.*/ + + unsigned int dac_rate=2,adc_rate=1,newrate; + static int israte=-1; + + if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; + else { + dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_dac.fragsize) ; + } + + if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; + else { + adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_adc.fragsize) ; + } + + if(dac_rate > adc_rate) newrate = adc_rate; + else newrate=dac_rate; + + if(newrate > BOB_MAX) newrate = BOB_MAX; + else { + if(newrate < BOB_MIN) + newrate = BOB_MIN; + } + + if( israte != newrate) { + printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); + israte=newrate; + } +#endif + +} + +static int +prog_dmabuf(struct ess_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + spin_unlock_irqrestore(&s->lock, flags); + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + /* this algorithm is a little nuts.. where did /1000 come from? */ + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + + M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + spin_lock_irqsave(&s->lock, flags); + if (rec) + ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + + return 0; +} + +static __inline__ void +clear_advance(struct ess_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void +ess_update_ptr(struct ess_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + /* oh boy should this all be re-written. everything in the current code paths think + that the various counters/pointers are expressed in bytes to the user but we have + two apus doing stereo stuff so we fix it up here.. it propagates to all the various + counters from here. */ + if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; + } else { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + } + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + /* the apu only reports the length it has seen, not the + length of the memory that has been used (the WP + knows that) */ + if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) + hwptr<<=1; + + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; +/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + s->dma_dac.count -= diff; +/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ + if (s->dma_dac.count <= 0) { + M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, + hwptr, s->dma_dac.swptr); + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + /* XXX how on earth can calling this with the lock held work.. */ + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = hwptr; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); +/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, + hwptr);*/ + } + } + } +} + +static irqreturn_t +ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ess_state *s; + struct ess_card *c = (struct ess_card *)dev_id; + int i; + u32 event; + + if ( ! (event = inb(c->iobase+0x1A)) ) + return IRQ_NONE; + + outw(inw(c->iobase+4)&1, c->iobase+4); + +/* M_printk("maestro int: %x\n",event);*/ + if(event&(1<<6)) + { + int x; + enum {UP_EVT, DOWN_EVT, MUTE_EVT} vol_evt; + int volume; + + /* Figure out which volume control button was pushed, + based on differences from the default register + values. */ + x = inb(c->iobase+0x1c); + if (x&1) vol_evt = MUTE_EVT; + else if (((x>>1)&7) > 4) vol_evt = UP_EVT; + else vol_evt = DOWN_EVT; + + /* Reset the volume control registers. */ + outb(0x88, c->iobase+0x1c); + outb(0x88, c->iobase+0x1d); + outb(0x88, c->iobase+0x1e); + outb(0x88, c->iobase+0x1f); + + /* Deal with the button press in a hammer-handed + manner by adjusting the master mixer volume. */ + volume = c->mix.mixer_state[0] & 0xff; + if (vol_evt == UP_EVT) { + volume += 5; + if (volume > 100) + volume = 100; + } + else if (vol_evt == DOWN_EVT) { + volume -= 5; + if (volume < 0) + volume = 0; + } else { + /* vol_evt == MUTE_EVT */ + if (volume == 0) + volume = c->dock_mute_vol; + else { + c->dock_mute_vol = volume; + volume = 0; + } + } + set_mixer (c, 0, (volume << 8) | volume); + } + + /* Ack all the interrupts. */ + outb(0xFF, c->iobase+0x1A); + + /* + * Update the pointers for all APU's we are running. + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + break; + spin_lock(&s->lock); + ess_update_ptr(s); + spin_unlock(&s->lock); + } + return IRQ_HANDLED; +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) + +static void set_mixer(struct ess_card *card,unsigned int mixer, unsigned int val ) +{ + unsigned int left,right; + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if(right > 100) right = 100; + if(left > 100) left = 100; + + card->mix.mixer_state[mixer]=(right << 8) | left; + card->mix.write_mixer(card,mixer,left,right); +} + +static void +mixer_push_state(struct ess_card *card) +{ + int i; + for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { + if( ! supported_mixer(card,i)) continue; + + set_mixer(card,i,card->mix.mixer_state[i]); + } +} + +static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) +{ + int i, val=0; + unsigned long flags; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_CARD(card); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); + strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); + info.modify_counter = card->mix.modcnt; + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.id, card_names[card->card_type], sizeof(info.id)); + strlcpy(info.name, card_names[card->card_type], sizeof(info.name)); + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, p); + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + + if(!card->mix.recmask_io) { + val = 0; + } else { + spin_lock_irqsave(&card->lock, flags); + val = card->mix.recmask_io(card,1,0); + spin_unlock_irqrestore(&card->lock, flags); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = card->mix.supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = card->mix.record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = card->mix.stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ +/* spin_lock_irqsave(&card->lock, flags); + val = card->mix.read_mixer(card,i); + spin_unlock_irqrestore(&card->lock, flags);*/ + + val = card->mix.mixer_state[i]; +/* M_printk("returned 0x%x for mixer %d\n",val,i);*/ + + break; + } + return put_user(val, p); + } + + if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) + return -EINVAL; + + card->mix.modcnt++; + + if (get_user(val, p)) + return -EFAULT; + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + + if (!card->mix.recmask_io) return -EINVAL; + if(!val) return 0; + if(! (val &= card->mix.record_sources)) return -EINVAL; + + spin_lock_irqsave(&card->lock, flags); + card->mix.recmask_io(card,0,val); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + spin_lock_irqsave(&card->lock, flags); + set_mixer(card,i,val); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; + } +} + +/* --------------------------------------------------------------------- */ +static int ess_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct ess_card *card = NULL; + struct pci_dev *pdev = NULL; + struct pci_driver *drvr; + + while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + card = (struct ess_card*)pci_get_drvdata (pdev); + if (!card) + continue; + if (card->dev_mixer == minor) + break; + } + } + if (!card) + return -ENODEV; + file->private_data = card; + return nonseekable_open(inode, file); +} + +static int ess_release_mixdev(struct inode *inode, struct file *file) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return 0; +} + +static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return mixer_ioctl(card, cmd, arg); +} + +static /*const*/ struct file_operations ess_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = ess_ioctl_mixdev, + .open = ess_open_mixdev, + .release = ess_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct ess_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + /* XXX uhm.. questionable locking*/ + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* Zach sez: "god this is gross.." */ +static int +comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize) +{ + /* No such thing as stereo recording, so we + use dual input mixers. which means we have to + combine mono to stereo buffer. yuck. + + but we don't have to be able to work a byte at a time..*/ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + +/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ + + for(i=count/4; i ; i--) { + (*(so+2)) = *(right++); + (*(so+3)) = *(right++); + (*so) = *(left++); + (*(so+1)) = *(left++); + so+=4; + } + + return 0; +} + +/* in this loop, dma_adc.count signifies the amount of data thats waiting + to be copied to the user's buffer. it is filled by the interrupt + handler and drained by this loop. */ +static ssize_t +ess_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned char *combbuf = NULL; + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if(!(combbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + /* remember, all these things are expressed in bytes to be + sent to the user.. hence the evil / 2 down below */ + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if ( cnt > 0 ) cnt &= ~3; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto rec_return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + /* FILL ME */ +/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto rec_return_free; + } + continue; + } + + if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + /* swptr/2 so that we know the real offset in each apu's buffer */ + comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); + if (copy_to_user(buffer, combbuf, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } else { + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +rec_return_free: + kfree(combbuf); + return ret; +} + +static ssize_t +ess_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ +/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ + /* FILL ME */ + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto return_free; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } +/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/ + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +return_free: + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + +/* In 0.14 prog_dmabuf always returns success anyway ... */ + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 0)) + return 0; + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 1)) + return 0; + } + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int ess_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_dac; + } else +#if 0 + /* if we can have the wp/wc do the combining + we can turn this back on. */ + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_adc; + } else +#endif + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + void __user *argp = (void __user *)arg; + int __user *p = argp; + +/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->card->pcidev->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->card->pcidev->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + /* fixed at 16bit for now */ + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; +#if 0 + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); +#endif + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + M_printk("maestro: SETFRAGMENT: %0x\n",val); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static void +set_base_registers(struct ess_state *s,void *vaddr) +{ + unsigned long packed_phys = virt_to_bus(vaddr)>>12; + wave_set_register(s, 0x01FC , packed_phys); + wave_set_register(s, 0x01FD , packed_phys); + wave_set_register(s, 0x01FE , packed_phys); + wave_set_register(s, 0x01FF , packed_phys); +} + +/* + * this guy makes sure we're in the right power + * state for what we want to be doing + */ +static void maestro_power(struct ess_card *card, int tostate) +{ + u16 active_mask = acpi_state_mask[tostate]; + u8 state; + + if(!use_pm) return; + + pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state); + state&=3; + + /* make sure we're in the right state */ + if(state != tostate) { + M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n", + card->pcidev->bus->number, + PCI_SLOT(card->pcidev->devfn), + PCI_FUNC(card->pcidev->devfn), + state,tostate); + pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate); + } + + /* and make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(card->pcidev, 0x54, ~ active_mask); + pci_write_config_word(card->pcidev, 0x56, ~ active_mask); +} + +/* we allocate a large power of two for all our memory. + this is cut up into (not to scale :): + |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | +*/ +static int +allocate_buffers(struct ess_state *s) +{ + void *rawbuf=NULL; + int order,i; + struct page *page, *pend; + + /* alloc as big a chunk as we can */ + for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) + if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) + break; + + if (!rawbuf) + return 1; + + M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; + s->card->dmaorder = order; + + for(i=0;icard->channels[i]; + + if(ess->dev_audio == -1) + continue; + + ess->dma_dac.ready = s->dma_dac.mapped = 0; + ess->dma_adc.ready = s->dma_adc.mapped = 0; + ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; + + /* offset dac and adc buffers starting half way through and then at each [da][ad]c's + order's intervals.. */ + ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); + ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); + /* offset mixbuf by a mixbuf so that the lame status fifo can + happily scribble away.. */ + ess->mixbuf = rawbuf + (512 * (i+1)); + + M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf, + ess->dma_adc.rawbuf, ess->mixbuf); + + } + + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(rawbuf); page <= pend; page++) + SetPageReserved(page); + + return 0; +} +static void +free_buffers(struct ess_state *s) +{ + struct page *page, *pend; + + s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; + s->dma_dac.mapped = s->dma_adc.mapped = 0; + s->dma_dac.ready = s->dma_adc.ready = 0; + + M_printk("maestro: freeing %p\n",s->card->dmapages); + /* undo marking the pages as reserved */ + + pend = virt_to_page(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); + for (page = virt_to_page(s->card->dmapages); page <= pend; page++) + ClearPageReserved(page); + + free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); + s->card->dmapages = NULL; +} + +static int +ess_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct ess_state *s = NULL; + unsigned char fmtm = ~0, fmts = 0; + struct pci_dev *pdev = NULL; + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + + while ((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { + struct ess_card *c; + struct pci_driver *drvr; + + drvr = pci_dev_driver (pdev); + if (drvr == &maestro_pci_driver) { + int i; + struct ess_state *sp; + + c = (struct ess_card*)pci_get_drvdata (pdev); + if (!c) + continue; + for(i=0;ichannels[i]; + if(sp->dev_audio < 0) + continue; + if((sp->dev_audio ^ minor) & ~0xf) + continue; + s=sp; + } + } + } + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EWOULDBLOCK; + } + mutex_unlock(&s->open_mutex); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + + /* under semaphore.. */ + if ((s->card->dmapages==NULL) && allocate_buffers(s)) { + mutex_unlock(&s->open_mutex); + return -ENOMEM; + } + + /* we're covered by the open_mutex */ + if( ! s->card->dsps_open ) { + maestro_power(s->card,ACPI_D0); + start_bob(s); + } + s->card->dsps_open++; + M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); + + /* ok, lets write WC base regs now that we've + powered up the chip */ + M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages), + ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12); + set_base_registers(s,s->card->dmapages); + + if (file->f_mode & FMODE_READ) { +/* + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ + + fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); + fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int +ess_release(struct inode *inode, struct file *file) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_mutex); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + /* we're covered by the open_mutex */ + M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); + if( --s->card->dsps_open <= 0) { + s->card->dsps_open = 0; + stop_bob(s); + free_buffers(s); + maestro_power(s->card,ACPI_D2); + } + mutex_unlock(&s->open_mutex); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static struct file_operations ess_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ess_read, + .write = ess_write, + .poll = ess_poll, + .ioctl = ess_ioctl, + .mmap = ess_mmap, + .open = ess_open, + .release = ess_release, +}; + +static int +maestro_config(struct ess_card *card) +{ + struct pci_dev *pcidev = card->pcidev; + struct ess_state *ess = &card->channels[0]; + int apu,iobase = card->iobase; + u16 w; + u32 n; + + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. + */ + + /* do config work at full power */ + maestro_power(card,ACPI_D0); + + pci_read_config_word(pcidev, 0x50, &w); + + w&=~(1<<5); /* Don't swap left/right (undoc)*/ + + pci_write_config_word(pcidev, 0x50, w); + + pci_read_config_word(pcidev, 0x52, &w); + w&=~(1<<15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w&=~(1<<14); /* External clock */ + + w|= (1<<7); /* Hardware volume control on */ + w|= (1<<6); /* Debounce off: easier to push the HWV buttons. */ + w&=~(1<<5); /* GPIO 4:5 */ + w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ + w&=~(1<<2); /* MIDI fix off (undoc) */ + w&=~(1<<1); /* reserved, always write 0 */ + pci_write_config_word(pcidev, 0x52, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pcidev, 0x40, &w); + w|=(1<<15); /* legacy decode off */ + w&=~(1<<14); /* Disable SIRQ */ + w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pcidev, 0x40, w); + + /* Set up 978 docking control chip. */ + pci_read_config_word(pcidev, 0x58, &w); + w|=1<<2; /* Enable 978. */ + w|=1<<3; /* Turn on 978 hardware volume control. */ + w&=~(1<<11); /* Turn on 978 mixer volume control. */ + pci_write_config_word(pcidev, 0x58, w); + + sound_reset(iobase); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase+0x34); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase+0x36); /* direct sound, stereo */ + udelay(20); + + + /* + * Reset the CODEC + */ + + maestro_ac97_reset(iobase,pcidev); + + /* + * Ring Bus Setup + */ + + n=inl(iobase+0x34); + n&=~0xF000; + n|=12<<12; /* Direct Sound, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x0F00; /* Modem off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F0; + n|=9<<4; /* DAC, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F; /* ASSP off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<29); /* Enable ring bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<28); /* Enable serial bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F00000; /* MIC off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F0000; /* I2S off */ + outl(n, iobase+0x34); + + + w=inw(iobase+0x18); + w&=~(1<<7); /* ClkRun off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<6); /* Hardware volume control interrupt off... for now. */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<4); /* ASSP irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<3); /* ISDN irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<2); /* Direct Sound IRQ on */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<1); /* MPU401 IRQ off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<0); /* SB IRQ on */ + outw(w, iobase+0x18); + + /* Set hardware volume control registers to midpoints. + We can tell which button was pushed based on how they change. */ + outb(0x88, iobase+0x1c); + outb(0x88, iobase+0x1d); + outb(0x88, iobase+0x1e); + outb(0x88, iobase+0x1f); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of whether we use the assp or not. */ + + outb(0, iobase+0xA4); + outb(3, iobase+0xA2); + outb(0, iobase+0xA6); + + for(apu=0;apu<16;apu++) + { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + + /* + * The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too. + */ + outw(0x01D0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + } + +#if 1 + wave_set_register(ess, IDR7_WAVE_ROMRAM, + (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); +#else + maestro_write(ess, IDR7_WAVE_ROMRAM, + (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); +#endif + + maestro_write(ess, IDR2_CRAM_DATA, 0x0000); + maestro_write(ess, 0x08, 0xB004); + /* Now back to the DirectSound stuff */ + maestro_write(ess, 0x09, 0x001B); + maestro_write(ess, 0x0A, 0x8000); + maestro_write(ess, 0x0B, 0x3F37); + maestro_write(ess, 0x0C, 0x0098); + + /* parallel out ?? */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0xF000)|0x8000); + /* parallel in, has something to do with recording :) */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); + + maestro_write(ess, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + outw(inw(0x14+iobase)|(1<<8),0x14+iobase); + outw(inw(0x14+iobase)&0xFE03,0x14+iobase); + outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); + outw(inw(0x14+iobase)|(1<<7),0x14+iobase); + + outw(0xA1A0, 0x14+iobase); /* 0300 ? */ + + /* Now clear the APU control ram */ + for(apu=0;apupower_regs = 0; + + /* check to see if we have a capabilities list in + the config register */ + pci_read_config_word(pcidev, PCI_STATUS, &w); + if(!(w & PCI_STATUS_CAP_LIST)) return 0; + + /* walk the list, starting at the head. */ + pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next); + + while(next && max--) { + pci_read_config_dword(pcidev, next & ~3, &n); + if((n & 0xff) == PCI_CAP_ID_PM) { + card->power_regs = next; + break; + } + next = ((n>>8) & 0xff); + } + + return card->power_regs ? 1 : 0; +} + +static int __init +maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid) +{ + int card_type = pdid->driver_data; + u32 n; + int iobase; + int i, ret; + struct ess_card *card; + struct ess_state *ess; + int num = 0; + +/* when built into the kernel, we only print version if device is found */ +#ifndef MODULE + static int printed_version; + if (!printed_version++) + printk(version); +#endif + + /* don't pick up weird modem maestros */ + if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return -ENODEV; + + + if ((ret=pci_enable_device(pcidev))) + return ret; + + iobase = pci_resource_start(pcidev,0); + if (!iobase || !(pci_resource_flags(pcidev, 0 ) & IORESOURCE_IO)) + return -ENODEV; + + if(pcidev->irq == 0) + return -ENODEV; + + /* stake our claim on the iospace */ + if( request_region(iobase, 256, card_names[card_type]) == NULL ) + { + printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); + return -EBUSY; + } + + /* just to be sure */ + pci_set_master(pcidev); + + card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); + if(card == NULL) + { + printk(KERN_WARNING "maestro: out of memory\n"); + release_region(iobase, 256); + return -ENOMEM; + } + + memset(card, 0, sizeof(*card)); + card->pcidev = pcidev; + + card->iobase = iobase; + card->card_type = card_type; + card->irq = pcidev->irq; + card->magic = ESS_CARD_MAGIC; + spin_lock_init(&card->lock); + init_waitqueue_head(&card->suspend_queue); + + card->dock_mute_vol = 50; + + /* init our groups of 6 apus */ + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + mutex_init(&s->open_mutex); + s->magic = ESS_STATE_MAGIC; + + s->apu[0] = 6*i; + s->apu[1] = (6*i)+1; + s->apu[2] = (6*i)+2; + s->apu[3] = (6*i)+3; + s->apu[4] = (6*i)+4; + s->apu[5] = (6*i)+5; + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk("maestro: BOTCH!\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) + break; + } + + num = i; + + /* clear the rest if we ran out of slots to register */ + for(;ichannels[i]; + s->dev_audio = -1; + } + + ess = &card->channels[0]; + + /* + * Ok card ready. Begin setup proper + */ + + printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", + card_names[card_type],iobase,card->irq); + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); + + /* turn off power management unless: + * - the user explicitly asks for it + * or + * - we're not a 2e, lesser chipps seem to have problems. + * - we're not on our _very_ small whitelist. some implemenetations + * really don't like the pm code, others require it. + * feel free to expand this as required. + */ +#define SUBSYSTEM_VENDOR(x) (x&0xffff) + if( (use_pm != 1) && + ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028))) + use_pm = 0; + + if(!use_pm) + printk(KERN_INFO "maestro: not attempting power management.\n"); + else { + if(!parse_power(card,pcidev)) + printk(KERN_INFO "maestro: no PCI power management interface found.\n"); + else { + pci_read_config_dword(pcidev, card->power_regs, &n); + printk(KERN_INFO "maestro: PCI power management capability: 0x%x\n",n>>16); + } + } + + maestro_config(card); + + if(maestro_ac97_get(card, 0x00)==0x0080) { + printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" + "\tyou should tell someone about this.\n"); + } else { + maestro_ac97_init(card); + } + + if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { + printk("maestro: couldn't register mixer!\n"); + } else { + memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); + mixer_push_state(card); + } + + if((ret=request_irq(card->irq, ess_interrupt, IRQF_SHARED, card_names[card_type], card))) + { + printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + release_region(card->iobase, 256); + unregister_reboot_notifier(&maestro_nb); + kfree(card); + return ret; + } + + /* Turn on hardware volume control interrupt. + This has to come after we grab the IRQ above, + or a crash will result on installation if a button has been pressed, + because in that case we'll get an immediate interrupt. */ + n = inw(iobase+0x18); + n|=(1<<6); + outw(n, iobase+0x18); + + pci_set_drvdata(pcidev,card); + /* now go to sleep 'till something interesting happens */ + maestro_power(card,ACPI_D2); + + printk(KERN_INFO "maestro: %d channels configured.\n", num); + return 0; +} + +static void maestro_remove(struct pci_dev *pcidev) { + struct ess_card *card = pci_get_drvdata(pcidev); + int i; + u32 n; + + /* XXX maybe should force stop bob, but should be all + stopped by _release by now */ + + /* Turn off hardware volume control interrupt. + This has to come before we leave the IRQ below, + or a crash results if a button is pressed ! */ + n = inw(card->iobase+0x18); + n&=~(1<<6); + outw(n, card->iobase+0x18); + + free_irq(card->irq, card); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(ess->dev_audio != -1) + unregister_sound_dsp(ess->dev_audio); + } + /* Goodbye, Mr. Bond. */ + maestro_power(card,ACPI_D3); + release_region(card->iobase, 256); + kfree(card); + pci_set_drvdata(pcidev,NULL); +} + +static struct pci_device_id maestro_pci_tbl[] = { + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2}, + {PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO2E}, + {PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_MAESTRO}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, maestro_pci_tbl); + +static struct pci_driver maestro_pci_driver = { + .name = "maestro", + .id_table = maestro_pci_tbl, + .probe = maestro_probe, + .remove = maestro_remove, +}; + +static int __init init_maestro(void) +{ + int rc; + + rc = pci_register_driver(&maestro_pci_driver); + if (rc < 0) + return rc; + + if (register_reboot_notifier(&maestro_nb)) + printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); +#ifdef MODULE + printk(version); +#endif + if (dsps_order < 0) { + dsps_order = 1; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + else if (dsps_order > MAX_DSP_ORDER) { + dsps_order = MAX_DSP_ORDER; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + return 0; +} + +static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + /* this notifier is called when the kernel is really shut down. */ + M_printk("maestro: shutting down\n"); + /* this will remove all card instances too */ + pci_unregister_driver(&maestro_pci_driver); + /* XXX dunno about power management */ + return NOTIFY_OK; +} + +/* --------------------------------------------------------------------- */ + + +static void cleanup_maestro(void) { + M_printk("maestro: unloading\n"); + pci_unregister_driver(&maestro_pci_driver); + unregister_reboot_notifier(&maestro_nb); +} + +/* --------------------------------------------------------------------- */ + +void +check_suspend(struct ess_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!card->in_suspend) return; + + card->in_suspend++; + add_wait_queue(&(card->suspend_queue), &wait); + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + remove_wait_queue(&(card->suspend_queue), &wait); + current->state = TASK_RUNNING; +} + +module_init(init_maestro); +module_exit(cleanup_maestro); diff --git a/trunk/sound/oss/maestro.h b/trunk/sound/oss/maestro.h new file mode 100644 index 000000000000..023ec7f968f9 --- /dev/null +++ b/trunk/sound/oss/maestro.h @@ -0,0 +1,60 @@ +/* + * Registers for the ESS PCI cards + */ + +/* + * Memory access + */ + +#define ESS_MEM_DATA 0x00 +#define ESS_MEM_INDEX 0x02 + +/* + * AC-97 Codec port. Delay 1uS after each write. This is used to + * talk AC-97 (see intel.com). Write data then register. + */ + +#define ESS_AC97_INDEX 0x30 /* byte wide */ +#define ESS_AC97_DATA 0x32 + +/* + * Reading is a bit different. You write register|0x80 to ubdex + * delay 1uS poll the low bit of index, when it clears read the + * data value. + */ + +/* + * Control port. Not yet fully understood + * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 + * to the data port. Then after 4uS the value 0x300 is written + */ + +#define RING_BUS_CTRL_L 0x34 +#define RING_BUS_CTRL_H 0x36 + +/* + * This is also used during setup. The value 0x17 is written to it + */ + +#define ESS_SETUP_18 0x18 + +/* + * And this one gets 0x000b + */ + +#define ESS_SETUP_A2 0xA2 + +/* + * And this 0x0000 + */ + +#define ESS_SETUP_A4 0xA4 +#define ESS_SETUP_A6 0xA6 + +/* + * Stuff to do with Harpo - the wave stuff + */ + +#define ESS_WAVETABLE_SIZE 0x14 +#define ESS_WAVETABLE_2M 0xA180 + diff --git a/trunk/sound/oss/maestro3.c b/trunk/sound/oss/maestro3.c new file mode 100644 index 000000000000..5ef6e617911b --- /dev/null +++ b/trunk/sound/oss/maestro3.c @@ -0,0 +1,2968 @@ +/***************************************************************************** + * + * ESS Maestro3/Allegro driver for Linux 2.4.x + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * (c) Copyright 2000 Zach Brown + * + * I need to thank many people for helping make this driver happen. + * As always, Eric Brombaugh was a hacking machine and killed many bugs + * that I was too dumb to notice. Howard Kim at ESS provided reference boards + * and as much docs as he could. Todd and Mick at Dell tested snapshots on + * an army of laptops. msw and deviant at Red Hat also humoured me by hanging + * their laptops every few hours in the name of science. + * + * Shouts go out to Mike "DJ XPCom" Ang. + * + * History + * v1.23 - Jun 5 2002 - Michael Olson + * added a module option to allow selection of GPIO pin number + * for external amp + * v1.22 - Feb 28 2001 - Zach Brown + * allocate mem at insmod/setup, rather than open + * limit pci dma addresses to 28bit, thanks guys. + * v1.21 - Feb 04 2001 - Zach Brown + * fix up really dumb notifier -> suspend oops + * v1.20 - Jan 30 2001 - Zach Brown + * get rid of pm callback and use pci_dev suspend/resume instead + * m3_probe cleanups, including pm oops think-o + * v1.10 - Jan 6 2001 - Zach Brown + * revert to lame remap_page_range mmap() just to make it work + * record mmap fixed. + * fix up incredibly broken open/release resource management + * duh. fix record format setting. + * add SMP locking and cleanup formatting here and there + * v1.00 - Dec 16 2000 - Zach Brown + * port to sexy 2.4 interfaces + * properly align instance allocations so recording works + * clean up function namespace a little :/ + * update PCI IDs based on mail from ESS + * arbitrarily bump version number to show its 2.4 now, + * 2.2 will stay 0., oss_audio port gets 2. + * v0.03 - Nov 05 2000 - Zach Brown + * disable recording but allow dsp to be opened read + * pull out most silly compat defines + * v0.02 - Nov 04 2000 - Zach Brown + * changed clocking setup for m3, slowdown fixed. + * codec reset is hopefully reliable now + * rudimentary apm/power management makes suspend/resume work + * v0.01 - Oct 31 2000 - Zach Brown + * first release + * v0.00 - Sep 09 2000 - Zach Brown + * first pass derivation from maestro.c + * + * TODO + * in/out allocated contiguously so fullduplex mmap will work? + * no beep on init (mute) + * resetup msrc data memory if freq changes? + * + * -- + * + * Allow me to ramble a bit about the m3 architecture. The core of the + * chip is the 'assp', the custom ESS dsp that runs the show. It has + * a small amount of code and data ram. ESS drops binary dsp code images + * on our heads, but we don't get to see specs on the dsp. + * + * The constant piece of code on the dsp is the 'kernel'. It also has a + * chunk of the dsp memory that is statically set aside for its control + * info. This is the KDATA defines in maestro3.h. Part of its core + * data is a list of code addresses that point to the pieces of DSP code + * that it should walk through in its loop. These other pieces of code + * do the real work. The kernel presumably jumps into each of them in turn. + * These code images tend to have their own data area, and one can have + * multiple data areas representing different states for each of the 'client + * instance' code portions. There is generally a list in the kernel data + * that points to the data instances for a given piece of code. + * + * We've only been given the binary image for the 'minisrc', mini sample + * rate converter. This is rather annoying because it limits the work + * we can do on the dsp, but it also greatly simplifies the job of managing + * dsp data memory for the code and data for our playing streams :). We + * statically allocate the minisrc code into a region we 'know' to be free + * based on the map of the binary kernel image we're loading. We also + * statically allocate the data areas for the maximum number of pcm streams + * we can be dealing with. This max is set by the length of the static list + * in the kernel data that records the number of minisrc data regions we + * can have. Thats right, all software dsp mixing with static code list + * limits. Rock. + * + * How sound goes in and out is still a relative mystery. It appears + * that the dsp has the ability to get input and output through various + * 'connections'. To do IO from or to a connection, you put the address + * of the minisrc client area in the static kernel data lists for that + * input or output. so for pcm -> dsp -> mixer, we put the minisrc data + * instance in the DMA list and also in the list for the mixer. I guess + * it Just Knows which is in/out, and we give some dma control info that + * helps. There are all sorts of cool inputs/outputs that it seems we can't + * use without dsp code images that know how to use them. + * + * So at init time we preload all the memory allocation stuff and set some + * system wide parameters. When we really get a sound to play we build + * up its minisrc header (stream parameters, buffer addresses, input/output + * settings). Then we throw its header on the various lists. We also + * tickle some KDATA settings that ask the assp to raise clock interrupts + * and do some amount of software mixing before handing data to the ac97. + * + * Sorry for the vague details. Feel free to ask Eric or myself if you + * happen to be trying to use this driver elsewhere. Please accept my + * apologies for the quality of the OSS support code, its passed through + * too many hands now and desperately wants to be rethought. + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include "maestro3.h" + +#define M_DEBUG 1 + +#define DRIVER_VERSION "1.23" +#define M3_MODULE_NAME "maestro3" +#define PFX M3_MODULE_NAME ": " + +#define M3_STATE_MAGIC 0x734d724d +#define M3_CARD_MAGIC 0x646e6f50 + +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define SND_DEV_DSP16 5 + +#ifdef M_DEBUG +static int debug; +#define DPMOD 1 /* per module load */ +#define DPSTR 2 /* per 'stream' */ +#define DPSYS 3 /* per syscall */ +#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */ +#define DPINT 5 /* per interrupt, LOTS */ +#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);} +#else +#define DPRINTK(x) +#endif + +struct m3_list { + int curlen; + u16 mem_addr; + int max; +}; + +static int external_amp = 1; +static int gpio_pin = -1; + +struct m3_state { + unsigned int magic; + struct m3_card *card; + unsigned char fmt, enable; + + int index; + + /* this locks around the oss state in the driver */ + /* no, this lock is removed - only use card->lock */ + /* otherwise: against what are you protecting on SMP + when irqhandler uses s->lock + and m3_assp_read uses card->lock ? + */ + struct mutex open_mutex; + wait_queue_head_t open_wait; + mode_t open_mode; + + int dev_audio; + + struct assp_instance { + u16 code, data; + } dac_inst, adc_inst; + + /* should be in dmabuf */ + unsigned int rateadc, ratedac; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + /* new in m3 */ + int mixer_index, dma_index, msrc_index, adc1_index; + int in_lists; + /* 2.4.. */ + dma_addr_t handle; + + } dma_dac, dma_adc; +}; + +struct m3_card { + unsigned int magic; + + struct m3_card *next; + + struct ac97_codec *ac97; + spinlock_t ac97_lock; + + int card_type; + +#define NR_DSPS 1 +#define MAX_DSPS NR_DSPS + struct m3_state channels[MAX_DSPS]; + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* hardware resources */ + struct pci_dev *pcidev; + u32 iobase; + u32 irq; + + int dacs_active; + + int timer_users; + + struct m3_list msrc_list, + mixer_list, + adc1_list, + dma_list; + + /* for storing reset state..*/ + u8 reset_state; + + u16 *suspend_mem; + int in_suspend; + wait_queue_head_t suspend_queue; +}; + +/* + * an arbitrary volume we set the internal + * volume settings to so that the ac97 volume + * range is a little less insane. 0x7fff is + * max. + */ +#define ARB_VOLUME ( 0x6800 ) + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +enum { + ESS_ALLEGRO, + ESS_MAESTRO3, + /* + * a maestro3 with 'hardware strapping', only + * found inside ESS? + */ + ESS_MAESTRO3HW, +}; + +static char *card_names[] = { + [ESS_ALLEGRO] = "Allegro", + [ESS_MAESTRO3] = "Maestro3(i)", + [ESS_MAESTRO3HW] = "Maestro3(i)hw" +}; + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#endif + +#define M3_DEVICE(DEV, TYPE) \ +{ \ +.vendor = PCI_VENDOR_ESS, \ +.device = DEV, \ +.subvendor = PCI_ANY_ID, \ +.subdevice = PCI_ANY_ID, \ +.class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, \ +.class_mask = 0xffff << 8, \ +.driver_data = TYPE, \ +} + +static struct pci_device_id m3_id_table[] = { + M3_DEVICE(0x1988, ESS_ALLEGRO), + M3_DEVICE(0x1998, ESS_MAESTRO3), + M3_DEVICE(0x199a, ESS_MAESTRO3HW), + {0,} +}; + +MODULE_DEVICE_TABLE (pci, m3_id_table); + +/* + * reports seem to indicate that the m3 is limited + * to 28bit bus addresses. aaaargggh... + */ +#define M3_PCI_DMA_MASK 0x0fffffff + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +static struct m3_card *devs; + +/* + * I'm not very good at laying out functions in a file :) + */ +static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf); +static int m3_suspend(struct pci_dev *pci_dev, pm_message_t state); +static void check_suspend(struct m3_card *card); + +static struct notifier_block m3_reboot_nb = { + .notifier_call = m3_notifier, +}; + +static void m3_outw(struct m3_card *card, + u16 value, unsigned long reg) +{ + check_suspend(card); + outw(value, card->iobase + reg); +} + +static u16 m3_inw(struct m3_card *card, unsigned long reg) +{ + check_suspend(card); + return inw(card->iobase + reg); +} +static void m3_outb(struct m3_card *card, + u8 value, unsigned long reg) +{ + check_suspend(card); + outb(value, card->iobase + reg); +} +static u8 m3_inb(struct m3_card *card, unsigned long reg) +{ + check_suspend(card); + return inb(card->iobase + reg); +} + +/* + * access 16bit words to the code or data regions of the dsp's memory. + * index addresses 16bit words. + */ +static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index) +{ + m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + m3_outw(card, index, DSP_PORT_MEMORY_INDEX); + return m3_inw(card, DSP_PORT_MEMORY_DATA); +} +static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index) +{ + unsigned long flags; + u16 ret; + + spin_lock_irqsave(&(card->lock), flags); + ret = __m3_assp_read(card, region, index); + spin_unlock_irqrestore(&(card->lock), flags); + + return ret; +} + +static void __m3_assp_write(struct m3_card *card, + u16 region, u16 index, u16 data) +{ + m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); + m3_outw(card, index, DSP_PORT_MEMORY_INDEX); + m3_outw(card, data, DSP_PORT_MEMORY_DATA); +} +static void m3_assp_write(struct m3_card *card, + u16 region, u16 index, u16 data) +{ + unsigned long flags; + + spin_lock_irqsave(&(card->lock), flags); + __m3_assp_write(card, region, index, data); + spin_unlock_irqrestore(&(card->lock), flags); +} + +static void m3_assp_halt(struct m3_card *card) +{ + card->reset_state = m3_inb(card, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; + mdelay(10); + m3_outb(card, card->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +static void m3_assp_continue(struct m3_card *card) +{ + m3_outb(card, card->reset_state | REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); +} + +/* + * This makes me sad. the maestro3 has lists + * internally that must be packed.. 0 terminates, + * apparently, or maybe all unused entries have + * to be 0, the lists have static lengths set + * by the binary code images. + */ + +static int m3_add_list(struct m3_card *card, + struct m3_list *list, u16 val) +{ + DPRINTK(DPSTR, "adding val 0x%x to list 0x%p at pos %d\n", + val, list, list->curlen); + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + list->curlen, + val); + + return list->curlen++; + +} + +static void m3_remove_list(struct m3_card *card, + struct m3_list *list, int index) +{ + u16 val; + int lastindex = list->curlen - 1; + + DPRINTK(DPSTR, "removing ind %d from list 0x%p\n", + index, list); + + if(index != lastindex) { + val = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex); + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + index, + val); + } + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + list->mem_addr + lastindex, + 0); + + list->curlen--; +} + +static void set_fmt(struct m3_state *s, unsigned char mask, unsigned char data) +{ + int tmp; + + s->fmt = (s->fmt & mask) | data; + + tmp = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + /* write to 'mono' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 1, + (tmp & ESS_FMT_STEREO) ? 0 : 1); + /* write to '8bit' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 2, + (tmp & ESS_FMT_16BIT) ? 0 : 1); + + tmp = (s->fmt >> ESS_ADC_SHIFT) & ESS_FMT_MASK; + + /* write to 'mono' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 1, + (tmp & ESS_FMT_STEREO) ? 0 : 1); + /* write to '8bit' word */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 2, + (tmp & ESS_FMT_16BIT) ? 0 : 1); +} + +static void set_dac_rate(struct m3_state *s, unsigned int rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + s->ratedac = rate; + + freq = ((rate << 15) + 24000 ) / 48000; + if(freq) + freq--; + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_FREQUENCY, + freq); +} + +static void set_adc_rate(struct m3_state *s, unsigned int rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 8000) + rate = 8000; + + s->rateadc = rate; + + freq = ((rate << 15) + 24000 ) / 48000; + if(freq) + freq--; + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_FREQUENCY, + freq); +} + +static void inc_timer_users(struct m3_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + card->timer_users++; + DPRINTK(DPSYS, "inc timer users now %d\n", + card->timer_users); + if(card->timer_users != 1) + goto out; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 240 ) ; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 240 ) ; + + m3_outw(card, + m3_inw(card, HOST_INT_CTRL) | CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +out: + spin_unlock_irqrestore(&card->lock, flags); +} + +static void dec_timer_users(struct m3_card *card) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + card->timer_users--; + DPRINTK(DPSYS, "dec timer users now %d\n", + card->timer_users); + if(card->timer_users > 0 ) + goto out; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_RELOAD, + 0 ) ; + + __m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TIMER_COUNT_CURRENT, + 0 ) ; + + m3_outw(card, m3_inw(card, HOST_INT_CTRL) & ~CLKRUN_GEN_ENABLE, + HOST_INT_CTRL); +out: + spin_unlock_irqrestore(&card->lock, flags); +} + +/* + * {start,stop}_{adc,dac} should be called + * while holding the 'state' lock and they + * will try to grab the 'card' lock.. + */ +static void stop_adc(struct m3_state *s) +{ + if (! (s->enable & ADC_RUNNING)) + return; + + s->enable &= ~ADC_RUNNING; + dec_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_INSTANCE_READY, 0); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 0); +} + +static void stop_dac(struct m3_state *s) +{ + if (! (s->enable & DAC_RUNNING)) + return; + + DPRINTK(DPSYS, "stop_dac()\n"); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_INSTANCE_READY, 0); + + s->enable &= ~DAC_RUNNING; + s->card->dacs_active--; + dec_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + s->card->dacs_active ) ; +} + +static void start_dac(struct m3_state *s) +{ + if( (!s->dma_dac.mapped && s->dma_dac.count < 1) || + !s->dma_dac.ready || + (s->enable & DAC_RUNNING)) + return; + + DPRINTK(DPSYS, "start_dac()\n"); + + s->enable |= DAC_RUNNING; + s->card->dacs_active++; + inc_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_INSTANCE_READY, 1); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER, + s->card->dacs_active ) ; +} + +static void start_adc(struct m3_state *s) +{ + if ((! s->dma_adc.mapped && + s->dma_adc.count >= (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + || !s->dma_adc.ready + || (s->enable & ADC_RUNNING) ) + return; + + DPRINTK(DPSYS, "start_adc()\n"); + + s->enable |= ADC_RUNNING; + inc_timer_users(s->card); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + KDATA_ADC1_REQUEST, 1); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_INSTANCE_READY, 1); +} + +static struct play_vals { + u16 addr, val; +} pv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 0} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 8}, /* numin */ + {SRC3_DIRECTION_OFFSET + 17, 50*2}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, MINISRC_BIQUAD_STAGE - 1}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0} /* booster */ +}; + + +/* the mode passed should be already shifted and masked */ +static void m3_play_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) +{ + int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + int dsp_in_buffer = s->dac_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + struct dmabuf *db = &s->dma_dac; + int i; + + DPRINTK(DPSTR, "mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_ADDRL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_ADDRH, + HI(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_CURRENTL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_HOST_SRC_CURRENTH, + HI(virt_to_bus(buffer))); +#undef LO +#undef HI + + /* dsp buffers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); + + /* + * some per client initializers + */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 12, + s->dac_inst.data + 40 + 8); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 19, + s->dac_inst.code + MINISRC_COEF_LOC); + + /* enable or disable low pass filter? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + SRC3_DIRECTION_OFFSET + 22, + s->ratedac > 45000 ? 0xff : 0 ); + + /* tell it which way dma is going? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + CDATA_DMA_CONTROL, + DMACONTROL_AUTOREPEAT + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->dac_inst.data + pv[i].addr, pv[i].val); + + /* + * put us in the lists if we're not already there + */ + + if(db->in_lists == 0) { + + db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->dma_index = m3_add_list(s->card, &s->card->dma_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->mixer_index = m3_add_list(s->card, &s->card->mixer_list, + s->dac_inst.data >> DP_SHIFT_COUNT); + + db->in_lists = 1; + } + + set_dac_rate(s,rate); + start_dac(s); +} + +/* + * Native record driver + */ +static struct rec_vals { + u16 addr, val; +} rv[] = { + {CDATA_LEFT_VOLUME, ARB_VOLUME}, + {CDATA_RIGHT_VOLUME, ARB_VOLUME}, + {SRC3_DIRECTION_OFFSET, 1} , + /* +1, +2 are stereo/16 bit */ + {SRC3_DIRECTION_OFFSET + 3, 0x0000}, /* fraction? */ + {SRC3_DIRECTION_OFFSET + 4, 0}, /* first l */ + {SRC3_DIRECTION_OFFSET + 5, 0}, /* first r */ + {SRC3_DIRECTION_OFFSET + 6, 0}, /* second l */ + {SRC3_DIRECTION_OFFSET + 7, 0}, /* second r */ + {SRC3_DIRECTION_OFFSET + 8, 0}, /* delta l */ + {SRC3_DIRECTION_OFFSET + 9, 0}, /* delta r */ + {SRC3_DIRECTION_OFFSET + 10, 0x8000}, /* round */ + {SRC3_DIRECTION_OFFSET + 11, 0xFF00}, /* higher bute mark */ + {SRC3_DIRECTION_OFFSET + 13, 0}, /* temp0 */ + {SRC3_DIRECTION_OFFSET + 14, 0}, /* c fraction */ + {SRC3_DIRECTION_OFFSET + 15, 0}, /* counter */ + {SRC3_DIRECTION_OFFSET + 16, 50},/* numin */ + {SRC3_DIRECTION_OFFSET + 17, 8}, /* numout */ + {SRC3_DIRECTION_OFFSET + 18, 0}, /* numstage */ + {SRC3_DIRECTION_OFFSET + 19, 0}, /* coef */ + {SRC3_DIRECTION_OFFSET + 20, 0}, /* filtertap */ + {SRC3_DIRECTION_OFFSET + 21, 0}, /* booster */ + {SRC3_DIRECTION_OFFSET + 22, 0xff} /* skip lpf */ +}; + +/* again, passed mode is alrady shifted/masked */ +static void m3_rec_setup(struct m3_state *s, int mode, u32 rate, void *buffer, int size) +{ + int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); + int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + int dsp_in_buffer = s->adc_inst.data + (MINISRC_TMP_BUFFER_SIZE / 2); + int dsp_out_buffer = dsp_in_buffer + (dsp_in_size / 2) + 1; + struct dmabuf *db = &s->dma_adc; + int i; + + DPRINTK(DPSTR, "rec_setup mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + +#define LO(x) ((x) & 0xffff) +#define HI(x) LO((x) >> 16) + + /* host dma buffer pointers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_ADDRL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_ADDRH, + HI(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1L, + LO(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_END_PLUS_1H, + HI(virt_to_bus(buffer) + size)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_CURRENTL, + LO(virt_to_bus(buffer))); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_HOST_SRC_CURRENTH, + HI(virt_to_bus(buffer))); +#undef LO +#undef HI + + /* dsp buffers */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_BEGIN, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_END_PLUS_1, + dsp_in_buffer + (dsp_in_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_HEAD, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_IN_BUF_TAIL, + dsp_in_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_BEGIN, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_END_PLUS_1, + dsp_out_buffer + (dsp_out_size / 2)); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_HEAD, + dsp_out_buffer); + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_OUT_BUF_TAIL, + dsp_out_buffer); + + /* + * some per client initializers + */ + + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + SRC3_DIRECTION_OFFSET + 12, + s->adc_inst.data + 40 + 8); + + /* tell it which way dma is going? */ + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + CDATA_DMA_CONTROL, + DMACONTROL_DIRECTION + DMACONTROL_AUTOREPEAT + + DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR); + + /* + * set an armload of static initializers + */ + for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) + m3_assp_write(s->card, MEMTYPE_INTERNAL_DATA, + s->adc_inst.data + rv[i].addr, rv[i].val); + + /* + * put us in the lists if we're not already there + */ + + if(db->in_lists == 0) { + + db->adc1_index = m3_add_list(s->card, &s->card->adc1_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->dma_index = m3_add_list(s->card, &s->card->dma_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->msrc_index = m3_add_list(s->card, &s->card->msrc_list, + s->adc_inst.data >> DP_SHIFT_COUNT); + + db->in_lists = 1; + } + + set_adc_rate(s,rate); + start_adc(s); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct m3_state *s, unsigned int addr, unsigned int count) +{ + DPRINTK(DPINT,"set_dmaa??\n"); +} + +static void set_dmac(struct m3_state *s, unsigned int addr, unsigned int count) +{ + DPRINTK(DPINT,"set_dmac??\n"); +} + +static u32 get_dma_pos(struct m3_card *card, + int instance_addr) +{ + u16 hi = 0, lo = 0; + int retry = 10; + + /* + * try and get a valid answer + */ + while(retry--) { + hi = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTH); + + lo = m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTL); + + if(hi == m3_assp_read(card, MEMTYPE_INTERNAL_DATA, + instance_addr + CDATA_HOST_SRC_CURRENTH)) + break; + } + return lo | (hi<<16); +} + +static u32 get_dmaa(struct m3_state *s) +{ + u32 offset; + + offset = get_dma_pos(s->card, s->dac_inst.data) - + virt_to_bus(s->dma_dac.rawbuf); + + DPRINTK(DPINT,"get_dmaa: 0x%08x\n",offset); + + return offset; +} + +static u32 get_dmac(struct m3_state *s) +{ + u32 offset; + + offset = get_dma_pos(s->card, s->adc_inst.data) - + virt_to_bus(s->dma_adc.rawbuf); + + DPRINTK(DPINT,"get_dmac: 0x%08x\n",offset); + + return offset; + +} + +static int +prog_dmabuf(struct m3_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->card->lock, flags); + + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + + DPRINTK(DPSTR,"prog_dmabuf: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + if (rec) + m3_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + m3_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + + db->ready = 1; + + spin_unlock_irqrestore(&s->card->lock, flags); + + return 0; +} + +static void clear_advance(struct m3_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void m3_update_ptr(struct m3_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = get_dmac(s) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = get_dmaa(s) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + + DPRINTK(DPINT,"updating dac: hwptr: %6d diff: %6d count: %6d\n", + hwptr,diff,s->dma_dac.count); + + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + + if (s->dma_dac.mapped) { + + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) { + wake_up(&s->dma_dac.wait); + } + } else { + + s->dma_dac.count -= diff; + + if (s->dma_dac.count <= 0) { + DPRINTK(DPCRAP,"underflow! diff: %d (0x%x) count: %d (0x%x) hw: %d (0x%x) sw: %d (0x%x)\n", + diff, diff, + s->dma_dac.count, + s->dma_dac.count, + hwptr, hwptr, + s->dma_dac.swptr, + s->dma_dac.swptr); + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = hwptr; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + DPRINTK(DPINT,"waking up DAC count: %d sw: %d hw: %d\n", + s->dma_dac.count, s->dma_dac.swptr, hwptr); + } + } + } +} + +static irqreturn_t m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct m3_card *c = (struct m3_card *)dev_id; + struct m3_state *s = &c->channels[0]; + u8 status; + + status = inb(c->iobase+0x1A); + + if(status == 0xff) + return IRQ_NONE; + + /* presumably acking the ints? */ + outw(status, c->iobase+0x1A); + + if(c->in_suspend) + return IRQ_HANDLED; + + /* + * ack an assp int if its running + * and has an int pending + */ + if( status & ASSP_INT_PENDING) { + u8 ctl = inb(c->iobase + ASSP_CONTROL_B); + if( !(ctl & STOP_ASSP_CLOCK)) { + ctl = inb(c->iobase + ASSP_HOST_INT_STATUS ); + if(ctl & DSP2HOST_REQ_TIMER) { + outb( DSP2HOST_REQ_TIMER, c->iobase + ASSP_HOST_INT_STATUS); + /* update adc/dac info if it was a timer int */ + spin_lock(&c->lock); + m3_update_ptr(s); + spin_unlock(&c->lock); + } + } + } + + /* XXX is this needed? */ + if(status & 0x40) + outb(0x40, c->iobase+0x1A); + return IRQ_HANDLED; +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,M3_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,M3_CARD_MAGIC) + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct m3_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int count; + signed long tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->card->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->card->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]; + /* XXX this is just broken. someone is waking us up alot, or schedule_timeout is broken. + or something. who cares. - zach */ + if (!schedule_timeout(tmo ? tmo : 1) && tmo) + DPRINTK(DPCRAP,"dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static ssize_t m3_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + spin_lock_irqsave(&s->card->lock, flags); + + while (count > 0) { + int timed_out; + + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto out; + } + + spin_unlock_irqrestore(&s->card->lock, flags); + timed_out = interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ) == 0; + spin_lock_irqsave(&s->card->lock, flags); + + if(timed_out) { + printk("read: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto out; + } + continue; + } + + spin_unlock_irqrestore(&s->card->lock, flags); + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + return ret; + } + spin_lock_irqsave(&s->card->lock, flags); + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +out: + spin_unlock_irqrestore(&s->card->lock, flags); + return ret; +} + +static ssize_t m3_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + spin_lock_irqsave(&s->card->lock, flags); + + while (count > 0) { + int timed_out; + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + cnt = s->dma_dac.dmasize-swptr; + + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + + if (cnt > count) + cnt = count; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto out; + } + spin_unlock_irqrestore(&s->card->lock, flags); + timed_out = interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ) == 0; + spin_lock_irqsave(&s->card->lock, flags); + if(timed_out) { + DPRINTK(DPCRAP,"write: chip lockup? dmasz %u fragsz %u count %u hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto out; + } + continue; + } + spin_unlock_irqrestore(&s->card->lock, flags); + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + return ret; + } + spin_lock_irqsave(&s->card->lock, flags); + + DPRINTK(DPSYS,"wrote %6d bytes at sw: %6d cnt: %6d while hw: %6d\n", + cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr); + + swptr = (swptr + cnt) % s->dma_dac.dmasize; + + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +out: + spin_unlock_irqrestore(&s->card->lock, flags); + return ret; +} + +static unsigned int m3_poll(struct file *file, struct poll_table_struct *wait) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + + spin_lock_irqsave(&s->card->lock, flags); + m3_update_ptr(s); + + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + + spin_unlock_irqrestore(&s->card->lock, flags); + return mask; +} + +static int m3_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + unsigned long max_size, size, start, offset; + struct dmabuf *db; + int ret = -EINVAL; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_dac; + } else + if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + + max_size = db->dmasize; + + start = vma->vm_start; + offset = (vma->vm_pgoff << PAGE_SHIFT); + size = vma->vm_end - vma->vm_start; + + if(size > max_size) + goto out; + if(offset > max_size - size) + goto out; + + /* + * this will be ->nopage() once I can + * ask Jeff what the hell I'm doing wrong. + */ + ret = -EAGAIN; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + + db->mapped = 1; + ret = 0; + +out: + return ret; +} + +/* + * this function is a disaster.. + */ +#define get_user_ret(x, ptr, ret) ({ if(get_user(x, ptr)) return ret; }) +static int m3_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + struct m3_card *card=s->card; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + + DPRINTK(DPSYS,"m3_ioctl: cmd %d\n", cmd); + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: + spin_lock_irqsave(&card->lock, flags); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->card->pcidev->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->card->pcidev->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + spin_unlock_irqrestore(&card->lock, flags); + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, p, -EFAULT); + spin_lock_irqsave(&card->lock, flags); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + spin_unlock_irqrestore(&card->lock, flags); + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, p, -EFAULT); + spin_lock_irqsave(&card->lock, flags); + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, p, -EFAULT); + spin_lock_irqsave(&card->lock, flags); + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + spin_unlock_irqrestore(&card->lock, flags); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8|AFMT_S16_LE, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, p, -EFAULT); + spin_lock_irqsave(&card->lock, flags); + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + spin_unlock_irqrestore(&card->lock, flags); + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_U8, + p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, p, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&card->lock, flags); + m3_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&card->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->enable & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&card->lock, flags); + m3_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&card->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&card->lock, flags); + m3_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&card->lock, flags); + return put_user(val, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&card->lock, flags); + m3_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&card->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&card->lock, flags); + m3_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&card->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, p, -EFAULT); + spin_lock_irqsave(&card->lock, flags); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + spin_unlock_irqrestore(&card->lock, flags); + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + get_user_ret(val, p, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static int +allocate_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) +{ + int order; + + DPRINTK(DPSTR,"allocating for dmabuf %p\n", db); + + /* + * alloc as big a chunk as we can, start with + * 64k 'cause we're insane. based on order cause + * the amazingly complicated prog_dmabuf wants it. + * + * pci_alloc_sonsistent guarantees that it won't cross a natural + * boundary; the m3 hardware can't have dma cross a 64k bus + * address boundary. + */ + for (order = 16-PAGE_SHIFT; order >= 1; order--) { + db->rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order, + &(db->handle)); + if(db->rawbuf) + break; + } + + if (!db->rawbuf) + return 1; + + DPRINTK(DPSTR,"allocated %ld (%d) bytes at %p\n", + PAGE_SIZE<rawbuf); + + { + struct page *page, *pend; + + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << order) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + SetPageReserved(page); + } + + + db->buforder = order; + db->ready = 0; + db->mapped = 0; + + return 0; +} + +static void +nuke_lists(struct m3_card *card, struct dmabuf *db) +{ + m3_remove_list(card, &(card->dma_list), db->dma_index); + m3_remove_list(card, &(card->msrc_list), db->msrc_index); + db->in_lists = 0; +} + +static void +free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db) +{ + if(db->rawbuf == NULL) + return; + + DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db); + + { + struct page *page, *pend; + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + ClearPageReserved(page); + } + + + pci_free_consistent(pci_dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->handle); + + db->rawbuf = NULL; + db->buforder = 0; + db->mapped = 0; + db->ready = 0; +} + +static int m3_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct m3_card *c; + struct m3_state *s = NULL; + int i; + unsigned char fmtm = ~0, fmts = 0; + unsigned long flags; + + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + for(c = devs ; c != NULL ; c = c->next) { + + for(i=0;ichannels[i].dev_audio < 0) + continue; + if((c->channels[i].dev_audio ^ minor) & ~0xf) + continue; + + s = &c->channels[i]; + break; + } + } + + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + + file->private_data = s; + + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EWOULDBLOCK; + } + mutex_unlock(&s->open_mutex); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + + spin_lock_irqsave(&c->lock, flags); + + if (file->f_mode & FMODE_READ) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + mutex_unlock(&s->open_mutex); + spin_unlock_irqrestore(&c->lock, flags); + return nonseekable_open(inode, file); +} + +static int m3_release(struct inode *inode, struct file *file) +{ + struct m3_state *s = (struct m3_state *)file->private_data; + struct m3_card *card=s->card; + unsigned long flags; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + + mutex_lock(&s->open_mutex); + spin_lock_irqsave(&card->lock, flags); + + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if(s->dma_dac.in_lists) { + m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index); + nuke_lists(s->card, &(s->dma_dac)); + } + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if(s->dma_adc.in_lists) { + m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index); + nuke_lists(s->card, &(s->dma_adc)); + } + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + spin_unlock_irqrestore(&card->lock, flags); + mutex_unlock(&s->open_mutex); + wake_up(&s->open_wait); + + return 0; +} + +/* + * Wait for the ac97 serial bus to be free. + * return nonzero if the bus is still busy. + */ +static int m3_ac97_wait(struct m3_card *card) +{ + int i = 10000; + + while( (m3_inb(card, 0x30) & 1) && i--) ; + + return i == 0; +} + +static u16 m3_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 ret = 0; + struct m3_card *card = codec->private_data; + + spin_lock(&card->ac97_lock); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy reading reg 0x%x\n",reg); + goto out; + } + + m3_outb(card, 0x80 | (reg & 0x7f), 0x30); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy finishing read reg 0x%x\n",reg); + goto out; + } + + ret = m3_inw(card, 0x32); + DPRINTK(DPCRAP,"reading 0x%04x from 0x%02x\n",ret, reg); + +out: + spin_unlock(&card->ac97_lock); + return ret; +} + +static void m3_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct m3_card *card = codec->private_data; + + spin_lock(&card->ac97_lock); + + if(m3_ac97_wait(card)) { + printk(KERN_ERR PFX "serial bus busy writing 0x%x to 0x%x\n",val, reg); + goto out; + } + DPRINTK(DPCRAP,"writing 0x%04x to 0x%02x\n", val, reg); + + m3_outw(card, val, 0x32); + m3_outb(card, reg & 0x7f, 0x30); +out: + spin_unlock(&card->ac97_lock); +} +/* OSS /dev/mixer file operation methods */ +static int m3_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct m3_card *card = devs; + + for (card = devs; card != NULL; card = card->next) { + if((card->ac97 != NULL) && (card->ac97->dev_mixer == minor)) + break; + } + + if (!card) { + return -ENODEV; + } + + file->private_data = card->ac97; + + return nonseekable_open(inode, file); +} + +static int m3_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static int m3_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static struct file_operations m3_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = m3_ioctl_mixdev, + .open = m3_open_mixdev, + .release = m3_release_mixdev, +}; + +static void remote_codec_config(int io, int isremote) +{ + isremote = isremote ? 1 : 0; + + outw( (inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote, + io + RING_BUS_CTRL_B); + outw( (inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote, + io + SDO_OUT_DEST_CTRL); + outw( (inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote, + io + SDO_IN_DEST_CTRL); +} + +/* + * hack, returns non zero on err + */ +static int try_read_vendor(struct m3_card *card) +{ + u16 ret; + + if(m3_ac97_wait(card)) + return 1; + + m3_outb(card, 0x80 | (AC97_VENDOR_ID1 & 0x7f), 0x30); + + if(m3_ac97_wait(card)) + return 1; + + ret = m3_inw(card, 0x32); + + return (ret == 0) || (ret == 0xffff); +} + +static void m3_codec_reset(struct m3_card *card, int busywait) +{ + u16 dir; + int delay1 = 0, delay2 = 0, i; + int io = card->iobase; + + switch (card->card_type) { + /* + * the onboard codec on the allegro seems + * to want to wait a very long time before + * coming back to life + */ + case ESS_ALLEGRO: + delay1 = 50; + delay2 = 800; + break; + case ESS_MAESTRO3: + case ESS_MAESTRO3HW: + delay1 = 20; + delay2 = 500; + break; + } + + for(i = 0; i < 5; i ++) { + dir = inw(io + GPIO_DIRECTION); + dir |= 0x10; /* assuming pci bus master? */ + + remote_codec_config(io, 0); + + outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A); + udelay(20); + + outw(dir & ~GPO_PRIMARY_AC97 , io + GPIO_DIRECTION); + outw(~GPO_PRIMARY_AC97 , io + GPIO_MASK); + outw(0, io + GPIO_DATA); + outw(dir | GPO_PRIMARY_AC97, io + GPIO_DIRECTION); + + if(busywait) { + mdelay(delay1); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay1 * HZ) / 1000); + } + + outw(GPO_PRIMARY_AC97, io + GPIO_DATA); + udelay(5); + /* ok, bring back the ac-link */ + outw(IO_SRAM_ENABLE | SERIAL_AC_LINK_ENABLE, io + RING_BUS_CTRL_A); + outw(~0, io + GPIO_MASK); + + if(busywait) { + mdelay(delay2); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((delay2 * HZ) / 1000); + } + if(! try_read_vendor(card)) + break; + + delay1 += 10; + delay2 += 100; + + DPRINTK(DPMOD, "retrying codec reset with delays of %d and %d ms\n", + delay1, delay2); + } + +#if 0 + /* more gung-ho reset that doesn't + * seem to work anywhere :) + */ + tmp = inw(io + RING_BUS_CTRL_A); + outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); + mdelay(20); + outw(tmp, io + RING_BUS_CTRL_A); + mdelay(50); +#endif +} + +static int __devinit m3_codec_install(struct m3_card *card) +{ + struct ac97_codec *codec; + + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + + codec->private_data = card; + codec->codec_read = m3_ac97_read; + codec->codec_write = m3_ac97_write; + /* someday we should support secondary codecs.. */ + codec->id = 0; + + if (ac97_probe_codec(codec) == 0) { + printk(KERN_ERR PFX "codec probe failed\n"); + ac97_release_codec(codec); + return -1; + } + + if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) { + printk(KERN_ERR PFX "couldn't register mixer!\n"); + ac97_release_codec(codec); + return -1; + } + + card->ac97 = codec; + + return 0; +} + + +#define MINISRC_LPF_LEN 10 +static u16 minisrc_lpf[MINISRC_LPF_LEN] = { + 0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C, + 0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F +}; +static void m3_assp_init(struct m3_card *card) +{ + int i; + + /* zero kernel data */ + for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR + i, 0); + + /* zero mixer data? */ + for(i = 0 ; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_BASE_ADDR2 + i, 0); + + /* init dma pointer */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_CURRENT_DMA, + KDATA_DMA_XFER0); + + /* write kernel into code memory.. */ + for(i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + REV_B_CODE_MEMORY_BEGIN + i, + assp_kernel_image[i]); + } + + /* + * We only have this one client and we know that 0x400 + * is free in our kernel's mem map, so lets just + * drop it there. It seems that the minisrc doesn't + * need vectors, so we won't bother with them.. + */ + for(i = 0 ; i < sizeof(assp_minisrc_image) / 2; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + i, + assp_minisrc_image[i]); + } + + /* + * write the coefficients for the low pass filter? + */ + for(i = 0; i < MINISRC_LPF_LEN ; i++) { + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + i, + minisrc_lpf[i]); + } + + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, + 0x400 + MINISRC_COEF_LOC + MINISRC_LPF_LEN, + 0x8000); + + /* + * the minisrc is the only thing on + * our task list.. + */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_TASK0, + 0x400); + + /* + * init the mixer number.. + */ + + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_MIXER_TASK_NUMBER,0); + + /* + * EXTREME KERNEL MASTER VOLUME + */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_LEFT_VOLUME, ARB_VOLUME); + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DAC_RIGHT_VOLUME, ARB_VOLUME); + + card->mixer_list.mem_addr = KDATA_MIXER_XFER0; + card->mixer_list.max = MAX_VIRTUAL_MIXER_CHANNELS; + card->adc1_list.mem_addr = KDATA_ADC1_XFER0; + card->adc1_list.max = MAX_VIRTUAL_ADC1_CHANNELS; + card->dma_list.mem_addr = KDATA_DMA_XFER0; + card->dma_list.max = MAX_VIRTUAL_DMA_CHANNELS; + card->msrc_list.mem_addr = KDATA_INSTANCE0_MINISRC; + card->msrc_list.max = MAX_INSTANCE_MINISRC; +} + +static int setup_msrc(struct m3_card *card, + struct assp_instance *inst, int index) +{ + int data_bytes = 2 * ( MINISRC_TMP_BUFFER_SIZE / 2 + + MINISRC_IN_BUFFER_SIZE / 2 + + 1 + MINISRC_OUT_BUFFER_SIZE / 2 + 1 ); + int address, i; + + /* + * the revb memory map has 0x1100 through 0x1c00 + * free. + */ + + /* + * align instance address to 256 bytes so that it's + * shifted list address is aligned. + * list address = (mem address >> 1) >> 7; + */ + data_bytes = (data_bytes + 255) & ~255; + address = 0x1100 + ((data_bytes/2) * index); + + if((address + (data_bytes/2)) >= 0x1c00) { + printk(KERN_ERR PFX "no memory for %d bytes at ind %d (addr 0x%x)\n", + data_bytes, index, address); + return -1; + } + + for(i = 0; i < data_bytes/2 ; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + address + i, 0); + + inst->code = 0x400; + inst->data = address; + + return 0; +} + +static int m3_assp_client_init(struct m3_state *s) +{ + setup_msrc(s->card, &(s->dac_inst), s->index * 2); + setup_msrc(s->card, &(s->adc_inst), (s->index * 2) + 1); + + return 0; +} + +static void m3_amp_enable(struct m3_card *card, int enable) +{ + /* + * this works for the reference board, have to find + * out about others + * + * this needs more magic for 4 speaker, but.. + */ + int io = card->iobase; + u16 gpo, polarity_port, polarity; + + if(!external_amp) + return; + + if (gpio_pin >= 0 && gpio_pin <= 15) { + polarity_port = 0x1000 + (0x100 * gpio_pin); + } else { + switch (card->card_type) { + case ESS_ALLEGRO: + polarity_port = 0x1800; + break; + default: + polarity_port = 0x1100; + /* Panasonic toughbook CF72 has to be different... */ + if(card->pcidev->subsystem_vendor == 0x10F7 && card->pcidev->subsystem_device == 0x833D) + polarity_port = 0x1D00; + break; + } + } + + gpo = (polarity_port >> 8) & 0x0F; + polarity = polarity_port >> 12; + if ( enable ) + polarity = !polarity; + polarity = polarity << gpo; + gpo = 1 << gpo; + + outw(~gpo , io + GPIO_MASK); + + outw( inw(io + GPIO_DIRECTION) | gpo , + io + GPIO_DIRECTION); + + outw( (GPO_SECONDARY_AC97 | GPO_PRIMARY_AC97 | polarity) , + io + GPIO_DATA); + + outw(0xffff , io + GPIO_MASK); +} + +static int +maestro_config(struct m3_card *card) +{ + struct pci_dev *pcidev = card->pcidev; + u32 n; + u8 t; /* makes as much sense as 'n', no? */ + + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= REDUCED_DEBOUNCE; + n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + outb(RESET_ASSP, card->iobase + ASSP_CONTROL_B); + pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); + n &= ~INT_CLK_SELECT; + if(card->card_type >= ESS_MAESTRO3) { + n &= ~INT_CLK_MULT_ENABLE; + n |= INT_CLK_SRC_NOT_PCI; + } + n &= ~( CLK_MULT_MODE_SELECT | CLK_MULT_MODE_SELECT_2 ); + pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); + + if(card->card_type <= ESS_ALLEGRO) { + pci_read_config_dword(pcidev, PCI_USER_CONFIG, &n); + n |= IN_CLK_12MHZ_SELECT; + pci_write_config_dword(pcidev, PCI_USER_CONFIG, n); + } + + t = inb(card->iobase + ASSP_CONTROL_A); + t &= ~( DSP_CLK_36MHZ_SELECT | ASSP_CLK_49MHZ_SELECT); + t |= ASSP_CLK_49MHZ_SELECT; + t |= ASSP_0_WS_ENABLE; + outb(t, card->iobase + ASSP_CONTROL_A); + + outb(RUN_ASSP, card->iobase + ASSP_CONTROL_B); + + return 0; +} + +static void m3_enable_ints(struct m3_card *card) +{ + unsigned long io = card->iobase; + + outw(ASSP_INT_ENABLE, io + HOST_INT_CTRL); + outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, + io + ASSP_CONTROL_C); +} + +static struct file_operations m3_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = m3_read, + .write = m3_write, + .poll = m3_poll, + .ioctl = m3_ioctl, + .mmap = m3_mmap, + .open = m3_open, + .release = m3_release, +}; + +#ifdef CONFIG_PM +static int alloc_dsp_suspendmem(struct m3_card *card) +{ + int len = sizeof(u16) * (REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); + + if( (card->suspend_mem = vmalloc(len)) == NULL) + return 1; + + return 0; +} + +#else +#define alloc_dsp_suspendmem(args...) 0 +#endif + +/* + * great day! this function is ugly as hell. + */ +static int __devinit m3_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + u32 n; + int i; + struct m3_card *card = NULL; + int ret = 0; + int card_type = pci_id->driver_data; + + DPRINTK(DPMOD, "in maestro_install\n"); + + if (pci_enable_device(pci_dev)) + return -EIO; + + if (pci_set_dma_mask(pci_dev, M3_PCI_DMA_MASK)) { + printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n"); + return -ENODEV; + } + + pci_set_master(pci_dev); + + if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING PFX "out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(struct m3_card)); + card->pcidev = pci_dev; + init_waitqueue_head(&card->suspend_queue); + + if ( ! request_region(pci_resource_start(pci_dev, 0), + pci_resource_len (pci_dev, 0), M3_MODULE_NAME)) { + + printk(KERN_WARNING PFX "unable to reserve I/O space.\n"); + ret = -EBUSY; + goto out; + } + + card->iobase = pci_resource_start(pci_dev, 0); + + if(alloc_dsp_suspendmem(card)) { + printk(KERN_WARNING PFX "couldn't alloc %d bytes for saving dsp state on suspend\n", + REV_B_CODE_MEMORY_LENGTH + REV_B_DATA_MEMORY_LENGTH); + ret = -ENOMEM; + goto out; + } + + card->card_type = card_type; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = M3_CARD_MAGIC; + spin_lock_init(&card->lock); + spin_lock_init(&card->ac97_lock); + devs = card; + for(i = 0; ichannels[i]); + s->dev_audio = -1; + } + + printk(KERN_INFO PFX "Configuring ESS %s found at IO 0x%04X IRQ %d\n", + card_names[card->card_type], card->iobase, card->irq); + + pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO PFX " subvendor id: 0x%08x\n",n); + + maestro_config(card); + m3_assp_halt(card); + + m3_codec_reset(card, 0); + + if(m3_codec_install(card)) { + ret = -EIO; + goto out; + } + + m3_assp_init(card); + m3_amp_enable(card, 1); + + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + mutex_init(&(s->open_mutex)); + s->magic = M3_STATE_MAGIC; + + m3_assp_client_init(s); + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) { + break; + } + + if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) || + allocate_dmabuf(card->pcidev, &(s->dma_dac))) { + ret = -ENOMEM; + goto out; + } + } + + if(request_irq(card->irq, m3_interrupt, IRQF_SHARED, card_names[card->card_type], card)) { + + printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq); + + ret = -EIO; + goto out; + } + + pci_set_drvdata(pci_dev, card); + + m3_enable_ints(card); + m3_assp_continue(card); + +out: + if(ret) { + if(card->iobase) + release_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + vfree(card->suspend_mem); + if(card->ac97) { + unregister_sound_mixer(card->ac97->dev_mixer); + kfree(card->ac97); + } + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + kfree(card); + } + + return ret; +} + +static void m3_remove(struct pci_dev *pci_dev) +{ + struct m3_card *card; + + unregister_reboot_notifier(&m3_reboot_nb); + + while ((card = devs)) { + int i; + devs = devs->next; + + free_irq(card->irq, card); + unregister_sound_mixer(card->ac97->dev_mixer); + kfree(card->ac97); + + for(i=0;ichannels[i]; + if(s->dev_audio < 0) + continue; + + unregister_sound_dsp(s->dev_audio); + free_dmabuf(card->pcidev, &s->dma_adc); + free_dmabuf(card->pcidev, &s->dma_dac); + } + + release_region(card->iobase, 256); + vfree(card->suspend_mem); + kfree(card); + } + devs = NULL; +} + +/* + * some bioses like the sound chip to be powered down + * at shutdown. We're just calling _suspend to + * achieve that.. + */ +static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + struct m3_card *card; + + DPRINTK(DPMOD, "notifier suspending all cards\n"); + + for(card = devs; card != NULL; card = card->next) { + if(!card->in_suspend) + m3_suspend(card->pcidev, PMSG_SUSPEND); /* XXX legal? */ + } + return 0; +} + +static int m3_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + unsigned long flags; + int i; + struct m3_card *card = pci_get_drvdata(pci_dev); + + /* must be a better way.. */ + spin_lock_irqsave(&card->lock, flags); + + DPRINTK(DPMOD, "pm in dev %p\n",card); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + DPRINTK(DPMOD, "stop_adc/dac() device %d\n",i); + stop_dac(s); + stop_adc(s); + } + + mdelay(10); /* give the assp a chance to idle.. */ + + m3_assp_halt(card); + + if(card->suspend_mem) { + int index = 0; + + DPRINTK(DPMOD, "saving code\n"); + for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) + card->suspend_mem[index++] = + m3_assp_read(card, MEMTYPE_INTERNAL_CODE, i); + DPRINTK(DPMOD, "saving data\n"); + for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + card->suspend_mem[index++] = + m3_assp_read(card, MEMTYPE_INTERNAL_DATA, i); + } + + DPRINTK(DPMOD, "powering down apci regs\n"); + m3_outw(card, 0xffff, 0x54); + m3_outw(card, 0xffff, 0x56); + + card->in_suspend = 1; + + spin_unlock_irqrestore(&card->lock, flags); + + return 0; +} + +static int m3_resume(struct pci_dev *pci_dev) +{ + unsigned long flags; + int index; + int i; + struct m3_card *card = pci_get_drvdata(pci_dev); + + spin_lock_irqsave(&card->lock, flags); + card->in_suspend = 0; + + DPRINTK(DPMOD, "resuming\n"); + + /* first lets just bring everything back. .*/ + + DPRINTK(DPMOD, "bringing power back on card 0x%p\n",card); + m3_outw(card, 0, 0x54); + m3_outw(card, 0, 0x56); + + DPRINTK(DPMOD, "restoring pci configs and reseting codec\n"); + maestro_config(card); + m3_assp_halt(card); + m3_codec_reset(card, 1); + + DPRINTK(DPMOD, "restoring dsp code card\n"); + index = 0; + for(i = REV_B_CODE_MEMORY_BEGIN ; i <= REV_B_CODE_MEMORY_END; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_CODE, i, + card->suspend_mem[index++]); + for(i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, i, + card->suspend_mem[index++]); + + /* tell the dma engine to restart itself */ + m3_assp_write(card, MEMTYPE_INTERNAL_DATA, + KDATA_DMA_ACTIVE, 0); + + DPRINTK(DPMOD, "resuming dsp\n"); + m3_assp_continue(card); + + DPRINTK(DPMOD, "enabling ints\n"); + m3_enable_ints(card); + + /* bring back the old school flavor */ + for(i = 0; i < SOUND_MIXER_NRDEVICES ; i++) { + int state = card->ac97->mixer_state[i]; + if (!supported_mixer(card->ac97, i)) + continue; + + card->ac97->write_mixer(card->ac97, i, + state & 0xff, (state >> 8) & 0xff); + } + + m3_amp_enable(card, 1); + + /* + * now we flip on the music + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + continue; + /* + * db->ready makes it so these guys can be + * called unconditionally.. + */ + DPRINTK(DPMOD, "turning on dacs ind %d\n",i); + start_dac(s); + start_adc(s); + } + + spin_unlock_irqrestore(&card->lock, flags); + + /* + * all right, we think things are ready, + * wake up people who were using the device + * when we suspended + */ + wake_up(&card->suspend_queue); + + return 0; +} + +MODULE_AUTHOR("Zach Brown "); +MODULE_DESCRIPTION("ESS Maestro3/Allegro Driver"); +MODULE_LICENSE("GPL"); + +#ifdef M_DEBUG +module_param(debug, int, 0); +#endif +module_param(external_amp, int, 0); +module_param(gpio_pin, int, 0); + +static struct pci_driver m3_pci_driver = { + .name = "ess_m3_audio", + .id_table = m3_id_table, + .probe = m3_probe, + .remove = m3_remove, + .suspend = m3_suspend, + .resume = m3_resume, +}; + +static int __init m3_init_module(void) +{ + printk(KERN_INFO PFX "version " DRIVER_VERSION " built at " __TIME__ " " __DATE__ "\n"); + + if (register_reboot_notifier(&m3_reboot_nb)) { + printk(KERN_WARNING PFX "reboot notifier registration failed\n"); + return -ENODEV; /* ? */ + } + + if (pci_register_driver(&m3_pci_driver)) { + unregister_reboot_notifier(&m3_reboot_nb); + return -ENODEV; + } + return 0; +} + +static void __exit m3_cleanup_module(void) +{ + pci_unregister_driver(&m3_pci_driver); +} + +module_init(m3_init_module); +module_exit(m3_cleanup_module); + +void check_suspend(struct m3_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!card->in_suspend) + return; + + card->in_suspend++; + add_wait_queue(&card->suspend_queue, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule(); + remove_wait_queue(&card->suspend_queue, &wait); + set_current_state(TASK_RUNNING); +} diff --git a/trunk/sound/oss/maestro3.h b/trunk/sound/oss/maestro3.h new file mode 100644 index 000000000000..dde29862c572 --- /dev/null +++ b/trunk/sound/oss/maestro3.h @@ -0,0 +1,821 @@ +/* + * ESS Technology allegro audio driver. + * + * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Hacked for the maestro3 driver by zab + */ + +// Allegro PCI configuration registers +#define PCI_LEGACY_AUDIO_CTRL 0x40 +#define SOUND_BLASTER_ENABLE 0x00000001 +#define FM_SYNTHESIS_ENABLE 0x00000002 +#define GAME_PORT_ENABLE 0x00000004 +#define MPU401_IO_ENABLE 0x00000008 +#define MPU401_IRQ_ENABLE 0x00000010 +#define ALIAS_10BIT_IO 0x00000020 +#define SB_DMA_MASK 0x000000C0 +#define SB_DMA_0 0x00000040 +#define SB_DMA_1 0x00000040 +#define SB_DMA_R 0x00000080 +#define SB_DMA_3 0x000000C0 +#define SB_IRQ_MASK 0x00000700 +#define SB_IRQ_5 0x00000000 +#define SB_IRQ_7 0x00000100 +#define SB_IRQ_9 0x00000200 +#define SB_IRQ_10 0x00000300 +#define MIDI_IRQ_MASK 0x00003800 +#define SERIAL_IRQ_ENABLE 0x00004000 +#define DISABLE_LEGACY 0x00008000 + +#define PCI_ALLEGRO_CONFIG 0x50 +#define SB_ADDR_240 0x00000004 +#define MPU_ADDR_MASK 0x00000018 +#define MPU_ADDR_330 0x00000000 +#define MPU_ADDR_300 0x00000008 +#define MPU_ADDR_320 0x00000010 +#define MPU_ADDR_340 0x00000018 +#define USE_PCI_TIMING 0x00000040 +#define POSTED_WRITE_ENABLE 0x00000080 +#define DMA_POLICY_MASK 0x00000700 +#define DMA_DDMA 0x00000000 +#define DMA_TDMA 0x00000100 +#define DMA_PCPCI 0x00000200 +#define DMA_WBDMA16 0x00000400 +#define DMA_WBDMA4 0x00000500 +#define DMA_WBDMA2 0x00000600 +#define DMA_WBDMA1 0x00000700 +#define DMA_SAFE_GUARD 0x00000800 +#define HI_PERF_GP_ENABLE 0x00001000 +#define PIC_SNOOP_MODE_0 0x00002000 +#define PIC_SNOOP_MODE_1 0x00004000 +#define SOUNDBLASTER_IRQ_MASK 0x00008000 +#define RING_IN_ENABLE 0x00010000 +#define SPDIF_TEST_MODE 0x00020000 +#define CLK_MULT_MODE_SELECT_2 0x00040000 +#define EEPROM_WRITE_ENABLE 0x00080000 +#define CODEC_DIR_IN 0x00100000 +#define HV_BUTTON_FROM_GD 0x00200000 +#define REDUCED_DEBOUNCE 0x00400000 +#define HV_CTRL_ENABLE 0x00800000 +#define SPDIF_ENABLE 0x01000000 +#define CLK_DIV_SELECT 0x06000000 +#define CLK_DIV_BY_48 0x00000000 +#define CLK_DIV_BY_49 0x02000000 +#define CLK_DIV_BY_50 0x04000000 +#define CLK_DIV_RESERVED 0x06000000 +#define PM_CTRL_ENABLE 0x08000000 +#define CLK_MULT_MODE_SELECT 0x30000000 +#define CLK_MULT_MODE_SHIFT 28 +#define CLK_MULT_MODE_0 0x00000000 +#define CLK_MULT_MODE_1 0x10000000 +#define CLK_MULT_MODE_2 0x20000000 +#define CLK_MULT_MODE_3 0x30000000 +#define INT_CLK_SELECT 0x40000000 +#define INT_CLK_MULT_RESET 0x80000000 + +// M3 +#define INT_CLK_SRC_NOT_PCI 0x00100000 +#define INT_CLK_MULT_ENABLE 0x80000000 + +#define PCI_ACPI_CONTROL 0x54 +#define PCI_ACPI_D0 0x00000000 +#define PCI_ACPI_D1 0xB4F70000 +#define PCI_ACPI_D2 0xB4F7B4F7 + +#define PCI_USER_CONFIG 0x58 +#define EXT_PCI_MASTER_ENABLE 0x00000001 +#define SPDIF_OUT_SELECT 0x00000002 +#define TEST_PIN_DIR_CTRL 0x00000004 +#define AC97_CODEC_TEST 0x00000020 +#define TRI_STATE_BUFFER 0x00000080 +#define IN_CLK_12MHZ_SELECT 0x00000100 +#define MULTI_FUNC_DISABLE 0x00000200 +#define EXT_MASTER_PAIR_SEL 0x00000400 +#define PCI_MASTER_SUPPORT 0x00000800 +#define STOP_CLOCK_ENABLE 0x00001000 +#define EAPD_DRIVE_ENABLE 0x00002000 +#define REQ_TRI_STATE_ENABLE 0x00004000 +#define REQ_LOW_ENABLE 0x00008000 +#define MIDI_1_ENABLE 0x00010000 +#define MIDI_2_ENABLE 0x00020000 +#define SB_AUDIO_SYNC 0x00040000 +#define HV_CTRL_TEST 0x00100000 +#define SOUNDBLASTER_TEST 0x00400000 + +#define PCI_USER_CONFIG_C 0x5C + +#define PCI_DDMA_CTRL 0x60 +#define DDMA_ENABLE 0x00000001 + + +// Allegro registers +#define HOST_INT_CTRL 0x18 +#define SB_INT_ENABLE 0x0001 +#define MPU401_INT_ENABLE 0x0002 +#define ASSP_INT_ENABLE 0x0010 +#define RING_INT_ENABLE 0x0020 +#define HV_INT_ENABLE 0x0040 +#define CLKRUN_GEN_ENABLE 0x0100 +#define HV_CTRL_TO_PME 0x0400 +#define SOFTWARE_RESET_ENABLE 0x8000 + +/* + * should be using the above defines, probably. + */ +#define REGB_ENABLE_RESET 0x01 +#define REGB_STOP_CLOCK 0x10 + +#define HOST_INT_STATUS 0x1A +#define SB_INT_PENDING 0x01 +#define MPU401_INT_PENDING 0x02 +#define ASSP_INT_PENDING 0x10 +#define RING_INT_PENDING 0x20 +#define HV_INT_PENDING 0x40 + +#define HARDWARE_VOL_CTRL 0x1B +#define SHADOW_MIX_REG_VOICE 0x1C +#define HW_VOL_COUNTER_VOICE 0x1D +#define SHADOW_MIX_REG_MASTER 0x1E +#define HW_VOL_COUNTER_MASTER 0x1F + +#define CODEC_COMMAND 0x30 +#define CODEC_READ_B 0x80 + +#define CODEC_STATUS 0x30 +#define CODEC_BUSY_B 0x01 + +#define CODEC_DATA 0x32 + +#define RING_BUS_CTRL_A 0x36 +#define RAC_PME_ENABLE 0x0100 +#define RAC_SDFS_ENABLE 0x0200 +#define LAC_PME_ENABLE 0x0400 +#define LAC_SDFS_ENABLE 0x0800 +#define SERIAL_AC_LINK_ENABLE 0x1000 +#define IO_SRAM_ENABLE 0x2000 +#define IIS_INPUT_ENABLE 0x8000 + +#define RING_BUS_CTRL_B 0x38 +#define SECOND_CODEC_ID_MASK 0x0003 +#define SPDIF_FUNC_ENABLE 0x0010 +#define SECOND_AC_ENABLE 0x0020 +#define SB_MODULE_INTF_ENABLE 0x0040 +#define SSPE_ENABLE 0x0040 +#define M3I_DOCK_ENABLE 0x0080 + +#define SDO_OUT_DEST_CTRL 0x3A +#define COMMAND_ADDR_OUT 0x0003 +#define PCM_LR_OUT_LOCAL 0x0000 +#define PCM_LR_OUT_REMOTE 0x0004 +#define PCM_LR_OUT_MUTE 0x0008 +#define PCM_LR_OUT_BOTH 0x000C +#define LINE1_DAC_OUT_LOCAL 0x0000 +#define LINE1_DAC_OUT_REMOTE 0x0010 +#define LINE1_DAC_OUT_MUTE 0x0020 +#define LINE1_DAC_OUT_BOTH 0x0030 +#define PCM_CLS_OUT_LOCAL 0x0000 +#define PCM_CLS_OUT_REMOTE 0x0040 +#define PCM_CLS_OUT_MUTE 0x0080 +#define PCM_CLS_OUT_BOTH 0x00C0 +#define PCM_RLF_OUT_LOCAL 0x0000 +#define PCM_RLF_OUT_REMOTE 0x0100 +#define PCM_RLF_OUT_MUTE 0x0200 +#define PCM_RLF_OUT_BOTH 0x0300 +#define LINE2_DAC_OUT_LOCAL 0x0000 +#define LINE2_DAC_OUT_REMOTE 0x0400 +#define LINE2_DAC_OUT_MUTE 0x0800 +#define LINE2_DAC_OUT_BOTH 0x0C00 +#define HANDSET_OUT_LOCAL 0x0000 +#define HANDSET_OUT_REMOTE 0x1000 +#define HANDSET_OUT_MUTE 0x2000 +#define HANDSET_OUT_BOTH 0x3000 +#define IO_CTRL_OUT_LOCAL 0x0000 +#define IO_CTRL_OUT_REMOTE 0x4000 +#define IO_CTRL_OUT_MUTE 0x8000 +#define IO_CTRL_OUT_BOTH 0xC000 + +#define SDO_IN_DEST_CTRL 0x3C +#define STATUS_ADDR_IN 0x0003 +#define PCM_LR_IN_LOCAL 0x0000 +#define PCM_LR_IN_REMOTE 0x0004 +#define PCM_LR_RESERVED 0x0008 +#define PCM_LR_IN_BOTH 0x000C +#define LINE1_ADC_IN_LOCAL 0x0000 +#define LINE1_ADC_IN_REMOTE 0x0010 +#define LINE1_ADC_IN_MUTE 0x0020 +#define MIC_ADC_IN_LOCAL 0x0000 +#define MIC_ADC_IN_REMOTE 0x0040 +#define MIC_ADC_IN_MUTE 0x0080 +#define LINE2_DAC_IN_LOCAL 0x0000 +#define LINE2_DAC_IN_REMOTE 0x0400 +#define LINE2_DAC_IN_MUTE 0x0800 +#define HANDSET_IN_LOCAL 0x0000 +#define HANDSET_IN_REMOTE 0x1000 +#define HANDSET_IN_MUTE 0x2000 +#define IO_STATUS_IN_LOCAL 0x0000 +#define IO_STATUS_IN_REMOTE 0x4000 + +#define SPDIF_IN_CTRL 0x3E +#define SPDIF_IN_ENABLE 0x0001 + +#define GPIO_DATA 0x60 +#define GPIO_DATA_MASK 0x0FFF +#define GPIO_HV_STATUS 0x3000 +#define GPIO_PME_STATUS 0x4000 + +#define GPIO_MASK 0x64 +#define GPIO_DIRECTION 0x68 +#define GPO_PRIMARY_AC97 0x0001 +#define GPI_LINEOUT_SENSE 0x0004 +#define GPO_SECONDARY_AC97 0x0008 +#define GPI_VOL_DOWN 0x0010 +#define GPI_VOL_UP 0x0020 +#define GPI_IIS_CLK 0x0040 +#define GPI_IIS_LRCLK 0x0080 +#define GPI_IIS_DATA 0x0100 +#define GPI_DOCKING_STATUS 0x0100 +#define GPI_HEADPHONE_SENSE 0x0200 +#define GPO_EXT_AMP_SHUTDOWN 0x1000 + +// M3 +#define GPO_M3_EXT_AMP_SHUTDN 0x0002 + +#define ASSP_INDEX_PORT 0x80 +#define ASSP_MEMORY_PORT 0x82 +#define ASSP_DATA_PORT 0x84 + +#define MPU401_DATA_PORT 0x98 +#define MPU401_STATUS_PORT 0x99 + +#define CLK_MULT_DATA_PORT 0x9C + +#define ASSP_CONTROL_A 0xA2 +#define ASSP_0_WS_ENABLE 0x01 +#define ASSP_CTRL_A_RESERVED1 0x02 +#define ASSP_CTRL_A_RESERVED2 0x04 +#define ASSP_CLK_49MHZ_SELECT 0x08 +#define FAST_PLU_ENABLE 0x10 +#define ASSP_CTRL_A_RESERVED3 0x20 +#define DSP_CLK_36MHZ_SELECT 0x40 + +#define ASSP_CONTROL_B 0xA4 +#define RESET_ASSP 0x00 +#define RUN_ASSP 0x01 +#define ENABLE_ASSP_CLOCK 0x00 +#define STOP_ASSP_CLOCK 0x10 +#define RESET_TOGGLE 0x40 + +#define ASSP_CONTROL_C 0xA6 +#define ASSP_HOST_INT_ENABLE 0x01 +#define FM_ADDR_REMAP_DISABLE 0x02 +#define HOST_WRITE_PORT_ENABLE 0x08 + +#define ASSP_HOST_INT_STATUS 0xAC +#define DSP2HOST_REQ_PIORECORD 0x01 +#define DSP2HOST_REQ_I2SRATE 0x02 +#define DSP2HOST_REQ_TIMER 0x04 + +// AC97 registers +// XXX fix this crap up +/*#define AC97_RESET 0x00*/ + +#define AC97_VOL_MUTE_B 0x8000 +#define AC97_VOL_M 0x1F +#define AC97_LEFT_VOL_S 8 + +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 +#define AC97_PC_BEEP_VOL 0x0A +#define AC97_PC_BEEP_VOL_M 0x0F +#define AC97_SROUND_MASTER_VOL 0x38 +#define AC97_PC_BEEP_VOL_S 1 + +/*#define AC97_PHONE_VOL 0x0C +#define AC97_MIC_VOL 0x0E*/ +#define AC97_MIC_20DB_ENABLE 0x40 + +/*#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16*/ +#define AC97_PCM_OUT_VOL 0x18 +/*#define AC97_RECORD_SELECT 0x1A*/ +#define AC97_RECORD_MIC 0x00 +#define AC97_RECORD_CD 0x01 +#define AC97_RECORD_VIDEO 0x02 +#define AC97_RECORD_AUX 0x03 +#define AC97_RECORD_MONO_MUX 0x02 +#define AC97_RECORD_DIGITAL 0x03 +#define AC97_RECORD_LINE 0x04 +#define AC97_RECORD_STEREO 0x05 +#define AC97_RECORD_MONO 0x06 +#define AC97_RECORD_PHONE 0x07 + +/*#define AC97_RECORD_GAIN 0x1C*/ +#define AC97_RECORD_VOL_M 0x0F + +/*#define AC97_GENERAL_PURPOSE 0x20*/ +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_ADC_READY 0x0001 +#define AC97_DAC_READY 0x0002 +#define AC97_ANALOG_READY 0x0004 +#define AC97_VREF_ON 0x0008 +#define AC97_PR0 0x0100 +#define AC97_PR1 0x0200 +#define AC97_PR2 0x0400 +#define AC97_PR3 0x0800 +#define AC97_PR4 0x1000 + +#define AC97_RESERVED1 0x28 + +#define AC97_VENDOR_TEST 0x5A + +#define AC97_CLOCK_DELAY 0x5C +#define AC97_LINEOUT_MUX_SEL 0x0001 +#define AC97_MONO_MUX_SEL 0x0002 +#define AC97_CLOCK_DELAY_SEL 0x1F +#define AC97_DAC_CDS_SHIFT 6 +#define AC97_ADC_CDS_SHIFT 11 + +#define AC97_MULTI_CHANNEL_SEL 0x74 + +/*#define AC97_VENDOR_ID1 0x7C +#define AC97_VENDOR_ID2 0x7E*/ + +/* + * ASSP control regs + */ +#define DSP_PORT_TIMER_COUNT 0x06 + +#define DSP_PORT_MEMORY_INDEX 0x80 + +#define DSP_PORT_MEMORY_TYPE 0x82 +#define MEMTYPE_INTERNAL_CODE 0x0002 +#define MEMTYPE_INTERNAL_DATA 0x0003 +#define MEMTYPE_MASK 0x0003 + +#define DSP_PORT_MEMORY_DATA 0x84 + +#define DSP_PORT_CONTROL_REG_A 0xA2 +#define DSP_PORT_CONTROL_REG_B 0xA4 +#define DSP_PORT_CONTROL_REG_C 0xA6 + +#define REV_A_CODE_MEMORY_BEGIN 0x0000 +#define REV_A_CODE_MEMORY_END 0x0FFF +#define REV_A_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_A_CODE_MEMORY_LENGTH (REV_A_CODE_MEMORY_END - REV_A_CODE_MEMORY_BEGIN + 1) + +#define REV_B_CODE_MEMORY_BEGIN 0x0000 +#define REV_B_CODE_MEMORY_END 0x0BFF +#define REV_B_CODE_MEMORY_UNIT_LENGTH 0x0040 +#define REV_B_CODE_MEMORY_LENGTH (REV_B_CODE_MEMORY_END - REV_B_CODE_MEMORY_BEGIN + 1) + +#define REV_A_DATA_MEMORY_BEGIN 0x1000 +#define REV_A_DATA_MEMORY_END 0x2FFF +#define REV_A_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_A_DATA_MEMORY_LENGTH (REV_A_DATA_MEMORY_END - REV_A_DATA_MEMORY_BEGIN + 1) + +#define REV_B_DATA_MEMORY_BEGIN 0x1000 +#define REV_B_DATA_MEMORY_END 0x2BFF +#define REV_B_DATA_MEMORY_UNIT_LENGTH 0x0080 +#define REV_B_DATA_MEMORY_LENGTH (REV_B_DATA_MEMORY_END - REV_B_DATA_MEMORY_BEGIN + 1) + + +#define NUM_UNITS_KERNEL_CODE 16 +#define NUM_UNITS_KERNEL_DATA 2 + +#define NUM_UNITS_KERNEL_CODE_WITH_HSP 16 +#define NUM_UNITS_KERNEL_DATA_WITH_HSP 5 + +/* + * Kernel data layout + */ + +#define DP_SHIFT_COUNT 7 + +#define KDATA_BASE_ADDR 0x1000 +#define KDATA_BASE_ADDR2 0x1080 + +#define KDATA_TASK0 (KDATA_BASE_ADDR + 0x0000) +#define KDATA_TASK1 (KDATA_BASE_ADDR + 0x0001) +#define KDATA_TASK2 (KDATA_BASE_ADDR + 0x0002) +#define KDATA_TASK3 (KDATA_BASE_ADDR + 0x0003) +#define KDATA_TASK4 (KDATA_BASE_ADDR + 0x0004) +#define KDATA_TASK5 (KDATA_BASE_ADDR + 0x0005) +#define KDATA_TASK6 (KDATA_BASE_ADDR + 0x0006) +#define KDATA_TASK7 (KDATA_BASE_ADDR + 0x0007) +#define KDATA_TASK_ENDMARK (KDATA_BASE_ADDR + 0x0008) + +#define KDATA_CURRENT_TASK (KDATA_BASE_ADDR + 0x0009) +#define KDATA_TASK_SWITCH (KDATA_BASE_ADDR + 0x000A) + +#define KDATA_INSTANCE0_POS3D (KDATA_BASE_ADDR + 0x000B) +#define KDATA_INSTANCE1_POS3D (KDATA_BASE_ADDR + 0x000C) +#define KDATA_INSTANCE2_POS3D (KDATA_BASE_ADDR + 0x000D) +#define KDATA_INSTANCE3_POS3D (KDATA_BASE_ADDR + 0x000E) +#define KDATA_INSTANCE4_POS3D (KDATA_BASE_ADDR + 0x000F) +#define KDATA_INSTANCE5_POS3D (KDATA_BASE_ADDR + 0x0010) +#define KDATA_INSTANCE6_POS3D (KDATA_BASE_ADDR + 0x0011) +#define KDATA_INSTANCE7_POS3D (KDATA_BASE_ADDR + 0x0012) +#define KDATA_INSTANCE8_POS3D (KDATA_BASE_ADDR + 0x0013) +#define KDATA_INSTANCE_POS3D_ENDMARK (KDATA_BASE_ADDR + 0x0014) + +#define KDATA_INSTANCE0_SPKVIRT (KDATA_BASE_ADDR + 0x0015) +#define KDATA_INSTANCE_SPKVIRT_ENDMARK (KDATA_BASE_ADDR + 0x0016) + +#define KDATA_INSTANCE0_SPDIF (KDATA_BASE_ADDR + 0x0017) +#define KDATA_INSTANCE_SPDIF_ENDMARK (KDATA_BASE_ADDR + 0x0018) + +#define KDATA_INSTANCE0_MODEM (KDATA_BASE_ADDR + 0x0019) +#define KDATA_INSTANCE_MODEM_ENDMARK (KDATA_BASE_ADDR + 0x001A) + +#define KDATA_INSTANCE0_SRC (KDATA_BASE_ADDR + 0x001B) +#define KDATA_INSTANCE1_SRC (KDATA_BASE_ADDR + 0x001C) +#define KDATA_INSTANCE_SRC_ENDMARK (KDATA_BASE_ADDR + 0x001D) + +#define KDATA_INSTANCE0_MINISRC (KDATA_BASE_ADDR + 0x001E) +#define KDATA_INSTANCE1_MINISRC (KDATA_BASE_ADDR + 0x001F) +#define KDATA_INSTANCE2_MINISRC (KDATA_BASE_ADDR + 0x0020) +#define KDATA_INSTANCE3_MINISRC (KDATA_BASE_ADDR + 0x0021) +#define KDATA_INSTANCE_MINISRC_ENDMARK (KDATA_BASE_ADDR + 0x0022) + +#define KDATA_INSTANCE0_CPYTHRU (KDATA_BASE_ADDR + 0x0023) +#define KDATA_INSTANCE1_CPYTHRU (KDATA_BASE_ADDR + 0x0024) +#define KDATA_INSTANCE_CPYTHRU_ENDMARK (KDATA_BASE_ADDR + 0x0025) + +#define KDATA_CURRENT_DMA (KDATA_BASE_ADDR + 0x0026) +#define KDATA_DMA_SWITCH (KDATA_BASE_ADDR + 0x0027) +#define KDATA_DMA_ACTIVE (KDATA_BASE_ADDR + 0x0028) + +#define KDATA_DMA_XFER0 (KDATA_BASE_ADDR + 0x0029) +#define KDATA_DMA_XFER1 (KDATA_BASE_ADDR + 0x002A) +#define KDATA_DMA_XFER2 (KDATA_BASE_ADDR + 0x002B) +#define KDATA_DMA_XFER3 (KDATA_BASE_ADDR + 0x002C) +#define KDATA_DMA_XFER4 (KDATA_BASE_ADDR + 0x002D) +#define KDATA_DMA_XFER5 (KDATA_BASE_ADDR + 0x002E) +#define KDATA_DMA_XFER6 (KDATA_BASE_ADDR + 0x002F) +#define KDATA_DMA_XFER7 (KDATA_BASE_ADDR + 0x0030) +#define KDATA_DMA_XFER8 (KDATA_BASE_ADDR + 0x0031) +#define KDATA_DMA_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0032) + +#define KDATA_I2S_SAMPLE_COUNT (KDATA_BASE_ADDR + 0x0033) +#define KDATA_I2S_INT_METER (KDATA_BASE_ADDR + 0x0034) +#define KDATA_I2S_ACTIVE (KDATA_BASE_ADDR + 0x0035) + +#define KDATA_TIMER_COUNT_RELOAD (KDATA_BASE_ADDR + 0x0036) +#define KDATA_TIMER_COUNT_CURRENT (KDATA_BASE_ADDR + 0x0037) + +#define KDATA_HALT_SYNCH_CLIENT (KDATA_BASE_ADDR + 0x0038) +#define KDATA_HALT_SYNCH_DMA (KDATA_BASE_ADDR + 0x0039) +#define KDATA_HALT_ACKNOWLEDGE (KDATA_BASE_ADDR + 0x003A) + +#define KDATA_ADC1_XFER0 (KDATA_BASE_ADDR + 0x003B) +#define KDATA_ADC1_XFER_ENDMARK (KDATA_BASE_ADDR + 0x003C) +#define KDATA_ADC1_LEFT_VOLUME (KDATA_BASE_ADDR + 0x003D) +#define KDATA_ADC1_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x003E) +#define KDATA_ADC1_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x003F) +#define KDATA_ADC1_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0040) + +#define KDATA_ADC2_XFER0 (KDATA_BASE_ADDR + 0x0041) +#define KDATA_ADC2_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0042) +#define KDATA_ADC2_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0043) +#define KDATA_ADC2_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x0044) +#define KDATA_ADC2_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x0045) +#define KDATA_ADC2_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x0046) + +#define KDATA_CD_XFER0 (KDATA_BASE_ADDR + 0x0047) +#define KDATA_CD_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0048) +#define KDATA_CD_LEFT_VOLUME (KDATA_BASE_ADDR + 0x0049) +#define KDATA_CD_RIGHT_VOLUME (KDATA_BASE_ADDR + 0x004A) +#define KDATA_CD_LEFT_SUR_VOL (KDATA_BASE_ADDR + 0x004B) +#define KDATA_CD_RIGHT_SUR_VOL (KDATA_BASE_ADDR + 0x004C) + +#define KDATA_MIC_XFER0 (KDATA_BASE_ADDR + 0x004D) +#define KDATA_MIC_XFER_ENDMARK (KDATA_BASE_ADDR + 0x004E) +#define KDATA_MIC_VOLUME (KDATA_BASE_ADDR + 0x004F) +#define KDATA_MIC_SUR_VOL (KDATA_BASE_ADDR + 0x0050) + +#define KDATA_I2S_XFER0 (KDATA_BASE_ADDR + 0x0051) +#define KDATA_I2S_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0052) + +#define KDATA_CHI_XFER0 (KDATA_BASE_ADDR + 0x0053) +#define KDATA_CHI_XFER_ENDMARK (KDATA_BASE_ADDR + 0x0054) + +#define KDATA_SPDIF_XFER (KDATA_BASE_ADDR + 0x0055) +#define KDATA_SPDIF_CURRENT_FRAME (KDATA_BASE_ADDR + 0x0056) +#define KDATA_SPDIF_FRAME0 (KDATA_BASE_ADDR + 0x0057) +#define KDATA_SPDIF_FRAME1 (KDATA_BASE_ADDR + 0x0058) +#define KDATA_SPDIF_FRAME2 (KDATA_BASE_ADDR + 0x0059) + +#define KDATA_SPDIF_REQUEST (KDATA_BASE_ADDR + 0x005A) +#define KDATA_SPDIF_TEMP (KDATA_BASE_ADDR + 0x005B) + +#define KDATA_SPDIFIN_XFER0 (KDATA_BASE_ADDR + 0x005C) +#define KDATA_SPDIFIN_XFER_ENDMARK (KDATA_BASE_ADDR + 0x005D) +#define KDATA_SPDIFIN_INT_METER (KDATA_BASE_ADDR + 0x005E) + +#define KDATA_DSP_RESET_COUNT (KDATA_BASE_ADDR + 0x005F) +#define KDATA_DEBUG_OUTPUT (KDATA_BASE_ADDR + 0x0060) + +#define KDATA_KERNEL_ISR_LIST (KDATA_BASE_ADDR + 0x0061) + +#define KDATA_KERNEL_ISR_CBSR1 (KDATA_BASE_ADDR + 0x0062) +#define KDATA_KERNEL_ISR_CBER1 (KDATA_BASE_ADDR + 0x0063) +#define KDATA_KERNEL_ISR_CBCR (KDATA_BASE_ADDR + 0x0064) +#define KDATA_KERNEL_ISR_AR0 (KDATA_BASE_ADDR + 0x0065) +#define KDATA_KERNEL_ISR_AR1 (KDATA_BASE_ADDR + 0x0066) +#define KDATA_KERNEL_ISR_AR2 (KDATA_BASE_ADDR + 0x0067) +#define KDATA_KERNEL_ISR_AR3 (KDATA_BASE_ADDR + 0x0068) +#define KDATA_KERNEL_ISR_AR4 (KDATA_BASE_ADDR + 0x0069) +#define KDATA_KERNEL_ISR_AR5 (KDATA_BASE_ADDR + 0x006A) +#define KDATA_KERNEL_ISR_BRCR (KDATA_BASE_ADDR + 0x006B) +#define KDATA_KERNEL_ISR_PASR (KDATA_BASE_ADDR + 0x006C) +#define KDATA_KERNEL_ISR_PAER (KDATA_BASE_ADDR + 0x006D) + +#define KDATA_CLIENT_SCRATCH0 (KDATA_BASE_ADDR + 0x006E) +#define KDATA_CLIENT_SCRATCH1 (KDATA_BASE_ADDR + 0x006F) +#define KDATA_KERNEL_SCRATCH (KDATA_BASE_ADDR + 0x0070) +#define KDATA_KERNEL_ISR_SCRATCH (KDATA_BASE_ADDR + 0x0071) + +#define KDATA_OUEUE_LEFT (KDATA_BASE_ADDR + 0x0072) +#define KDATA_QUEUE_RIGHT (KDATA_BASE_ADDR + 0x0073) + +#define KDATA_ADC1_REQUEST (KDATA_BASE_ADDR + 0x0074) +#define KDATA_ADC2_REQUEST (KDATA_BASE_ADDR + 0x0075) +#define KDATA_CD_REQUEST (KDATA_BASE_ADDR + 0x0076) +#define KDATA_MIC_REQUEST (KDATA_BASE_ADDR + 0x0077) + +#define KDATA_ADC1_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0078) +#define KDATA_ADC2_MIXER_REQUEST (KDATA_BASE_ADDR + 0x0079) +#define KDATA_CD_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007A) +#define KDATA_MIC_MIXER_REQUEST (KDATA_BASE_ADDR + 0x007B) +#define KDATA_MIC_SYNC_COUNTER (KDATA_BASE_ADDR + 0x007C) + +/* + * second 'segment' (?) reserved for mixer + * buffers.. + */ + +#define KDATA_MIXER_WORD0 (KDATA_BASE_ADDR2 + 0x0000) +#define KDATA_MIXER_WORD1 (KDATA_BASE_ADDR2 + 0x0001) +#define KDATA_MIXER_WORD2 (KDATA_BASE_ADDR2 + 0x0002) +#define KDATA_MIXER_WORD3 (KDATA_BASE_ADDR2 + 0x0003) +#define KDATA_MIXER_WORD4 (KDATA_BASE_ADDR2 + 0x0004) +#define KDATA_MIXER_WORD5 (KDATA_BASE_ADDR2 + 0x0005) +#define KDATA_MIXER_WORD6 (KDATA_BASE_ADDR2 + 0x0006) +#define KDATA_MIXER_WORD7 (KDATA_BASE_ADDR2 + 0x0007) +#define KDATA_MIXER_WORD8 (KDATA_BASE_ADDR2 + 0x0008) +#define KDATA_MIXER_WORD9 (KDATA_BASE_ADDR2 + 0x0009) +#define KDATA_MIXER_WORDA (KDATA_BASE_ADDR2 + 0x000A) +#define KDATA_MIXER_WORDB (KDATA_BASE_ADDR2 + 0x000B) +#define KDATA_MIXER_WORDC (KDATA_BASE_ADDR2 + 0x000C) +#define KDATA_MIXER_WORDD (KDATA_BASE_ADDR2 + 0x000D) +#define KDATA_MIXER_WORDE (KDATA_BASE_ADDR2 + 0x000E) +#define KDATA_MIXER_WORDF (KDATA_BASE_ADDR2 + 0x000F) + +#define KDATA_MIXER_XFER0 (KDATA_BASE_ADDR2 + 0x0010) +#define KDATA_MIXER_XFER1 (KDATA_BASE_ADDR2 + 0x0011) +#define KDATA_MIXER_XFER2 (KDATA_BASE_ADDR2 + 0x0012) +#define KDATA_MIXER_XFER3 (KDATA_BASE_ADDR2 + 0x0013) +#define KDATA_MIXER_XFER4 (KDATA_BASE_ADDR2 + 0x0014) +#define KDATA_MIXER_XFER5 (KDATA_BASE_ADDR2 + 0x0015) +#define KDATA_MIXER_XFER6 (KDATA_BASE_ADDR2 + 0x0016) +#define KDATA_MIXER_XFER7 (KDATA_BASE_ADDR2 + 0x0017) +#define KDATA_MIXER_XFER8 (KDATA_BASE_ADDR2 + 0x0018) +#define KDATA_MIXER_XFER9 (KDATA_BASE_ADDR2 + 0x0019) +#define KDATA_MIXER_XFER_ENDMARK (KDATA_BASE_ADDR2 + 0x001A) + +#define KDATA_MIXER_TASK_NUMBER (KDATA_BASE_ADDR2 + 0x001B) +#define KDATA_CURRENT_MIXER (KDATA_BASE_ADDR2 + 0x001C) +#define KDATA_MIXER_ACTIVE (KDATA_BASE_ADDR2 + 0x001D) +#define KDATA_MIXER_BANK_STATUS (KDATA_BASE_ADDR2 + 0x001E) +#define KDATA_DAC_LEFT_VOLUME (KDATA_BASE_ADDR2 + 0x001F) +#define KDATA_DAC_RIGHT_VOLUME (KDATA_BASE_ADDR2 + 0x0020) + +#define MAX_INSTANCE_MINISRC (KDATA_INSTANCE_MINISRC_ENDMARK - KDATA_INSTANCE0_MINISRC) +#define MAX_VIRTUAL_DMA_CHANNELS (KDATA_DMA_XFER_ENDMARK - KDATA_DMA_XFER0) +#define MAX_VIRTUAL_MIXER_CHANNELS (KDATA_MIXER_XFER_ENDMARK - KDATA_MIXER_XFER0) +#define MAX_VIRTUAL_ADC1_CHANNELS (KDATA_ADC1_XFER_ENDMARK - KDATA_ADC1_XFER0) + +/* + * client data area offsets + */ +#define CDATA_INSTANCE_READY 0x00 + +#define CDATA_HOST_SRC_ADDRL 0x01 +#define CDATA_HOST_SRC_ADDRH 0x02 +#define CDATA_HOST_SRC_END_PLUS_1L 0x03 +#define CDATA_HOST_SRC_END_PLUS_1H 0x04 +#define CDATA_HOST_SRC_CURRENTL 0x05 +#define CDATA_HOST_SRC_CURRENTH 0x06 + +#define CDATA_IN_BUF_CONNECT 0x07 +#define CDATA_OUT_BUF_CONNECT 0x08 + +#define CDATA_IN_BUF_BEGIN 0x09 +#define CDATA_IN_BUF_END_PLUS_1 0x0A +#define CDATA_IN_BUF_HEAD 0x0B +#define CDATA_IN_BUF_TAIL 0x0C +#define CDATA_OUT_BUF_BEGIN 0x0D +#define CDATA_OUT_BUF_END_PLUS_1 0x0E +#define CDATA_OUT_BUF_HEAD 0x0F +#define CDATA_OUT_BUF_TAIL 0x10 + +#define CDATA_DMA_CONTROL 0x11 +#define CDATA_RESERVED 0x12 + +#define CDATA_FREQUENCY 0x13 +#define CDATA_LEFT_VOLUME 0x14 +#define CDATA_RIGHT_VOLUME 0x15 +#define CDATA_LEFT_SUR_VOL 0x16 +#define CDATA_RIGHT_SUR_VOL 0x17 + +#define CDATA_HEADER_LEN 0x18 + +#define SRC3_DIRECTION_OFFSET CDATA_HEADER_LEN +#define SRC3_MODE_OFFSET (CDATA_HEADER_LEN + 1) +#define SRC3_WORD_LENGTH_OFFSET (CDATA_HEADER_LEN + 2) +#define SRC3_PARAMETER_OFFSET (CDATA_HEADER_LEN + 3) +#define SRC3_COEFF_ADDR_OFFSET (CDATA_HEADER_LEN + 8) +#define SRC3_FILTAP_ADDR_OFFSET (CDATA_HEADER_LEN + 10) +#define SRC3_TEMP_INBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 16) +#define SRC3_TEMP_OUTBUF_ADDR_OFFSET (CDATA_HEADER_LEN + 17) + +#define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) +#define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) +#define MINISRC_BIQUAD_STAGE 2 +#define MINISRC_COEF_LOC 0X175 + +#define DMACONTROL_BLOCK_MASK 0x000F +#define DMAC_BLOCK0_SELECTOR 0x0000 +#define DMAC_BLOCK1_SELECTOR 0x0001 +#define DMAC_BLOCK2_SELECTOR 0x0002 +#define DMAC_BLOCK3_SELECTOR 0x0003 +#define DMAC_BLOCK4_SELECTOR 0x0004 +#define DMAC_BLOCK5_SELECTOR 0x0005 +#define DMAC_BLOCK6_SELECTOR 0x0006 +#define DMAC_BLOCK7_SELECTOR 0x0007 +#define DMAC_BLOCK8_SELECTOR 0x0008 +#define DMAC_BLOCK9_SELECTOR 0x0009 +#define DMAC_BLOCKA_SELECTOR 0x000A +#define DMAC_BLOCKB_SELECTOR 0x000B +#define DMAC_BLOCKC_SELECTOR 0x000C +#define DMAC_BLOCKD_SELECTOR 0x000D +#define DMAC_BLOCKE_SELECTOR 0x000E +#define DMAC_BLOCKF_SELECTOR 0x000F +#define DMACONTROL_PAGE_MASK 0x00F0 +#define DMAC_PAGE0_SELECTOR 0x0030 +#define DMAC_PAGE1_SELECTOR 0x0020 +#define DMAC_PAGE2_SELECTOR 0x0010 +#define DMAC_PAGE3_SELECTOR 0x0000 +#define DMACONTROL_AUTOREPEAT 0x1000 +#define DMACONTROL_STOPPED 0x2000 +#define DMACONTROL_DIRECTION 0x0100 + + +/* + * DSP Code images + */ + +static u16 assp_kernel_image[] = { + 0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4, + 0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x0063, 0x7980, 0x006B, 0x7980, 0x03B4, 0x7980, 0x03B4, + 0xBF80, 0x2C7C, 0x8806, 0x8804, 0xBE40, 0xBC20, 0xAE09, 0x1000, 0xAE0A, 0x0001, 0x6938, 0xEB08, + 0x0053, 0x695A, 0xEB08, 0x00D6, 0x0009, 0x8B88, 0x6980, 0xE388, 0x0036, 0xBE30, 0xBC20, 0x6909, + 0xB801, 0x9009, 0xBE41, 0xBE41, 0x6928, 0xEB88, 0x0078, 0xBE41, 0xBE40, 0x7980, 0x0038, 0xBE41, + 0xBE41, 0x903A, 0x6938, 0xE308, 0x0056, 0x903A, 0xBE41, 0xBE40, 0xEF00, 0x903A, 0x6939, 0xE308, + 0x005E, 0x903A, 0xEF00, 0x690B, 0x660C, 0xEF8C, 0x690A, 0x660C, 0x620B, 0x6609, 0xEF00, 0x6910, + 0x660F, 0xEF04, 0xE388, 0x0075, 0x690E, 0x660F, 0x6210, 0x660D, 0xEF00, 0x690E, 0x660D, 0xEF00, + 0xAE70, 0x0001, 0xBC20, 0xAE27, 0x0001, 0x6939, 0xEB08, 0x005D, 0x6926, 0xB801, 0x9026, 0x0026, + 0x8B88, 0x6980, 0xE388, 0x00CB, 0x9028, 0x0D28, 0x4211, 0xE100, 0x007A, 0x4711, 0xE100, 0x00A0, + 0x7A80, 0x0063, 0xB811, 0x660A, 0x6209, 0xE304, 0x007A, 0x0C0B, 0x4005, 0x100A, 0xBA01, 0x9012, + 0x0C12, 0x4002, 0x7980, 0x00AF, 0x7A80, 0x006B, 0xBE02, 0x620E, 0x660D, 0xBA10, 0xE344, 0x007A, + 0x0C10, 0x4005, 0x100E, 0xBA01, 0x9012, 0x0C12, 0x4002, 0x1003, 0xBA02, 0x9012, 0x0C12, 0x4000, + 0x1003, 0xE388, 0x00BA, 0x1004, 0x7980, 0x00BC, 0x1004, 0xBA01, 0x9012, 0x0C12, 0x4001, 0x0C05, + 0x4003, 0x0C06, 0x4004, 0x1011, 0xBFB0, 0x01FF, 0x9012, 0x0C12, 0x4006, 0xBC20, 0xEF00, 0xAE26, + 0x1028, 0x6970, 0xBFD0, 0x0001, 0x9070, 0xE388, 0x007A, 0xAE28, 0x0000, 0xEF00, 0xAE70, 0x0300, + 0x0C70, 0xB00C, 0xAE5A, 0x0000, 0xEF00, 0x7A80, 0x038A, 0x697F, 0xB801, 0x907F, 0x0056, 0x8B88, + 0x0CA0, 0xB008, 0xAF71, 0xB000, 0x4E71, 0xE200, 0x00F3, 0xAE56, 0x1057, 0x0056, 0x0CA0, 0xB008, + 0x8056, 0x7980, 0x03A1, 0x0810, 0xBFA0, 0x1059, 0xE304, 0x03A1, 0x8056, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0xBF01, 0xBE43, 0xBE59, 0x907C, 0x6937, 0xE388, 0x010D, 0xBA01, 0xE308, 0x010C, 0xAE71, + 0x0004, 0x0C71, 0x5000, 0x6936, 0x9037, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, 0xBF0A, + 0x0560, 0xF500, 0xBF0A, 0x0520, 0xB900, 0xBB17, 0x90A0, 0x6917, 0xE388, 0x0148, 0x0D17, 0xE100, + 0x0127, 0xBF0C, 0x0578, 0xBF0D, 0x057C, 0x7980, 0x012B, 0xBF0C, 0x0538, 0xBF0D, 0x053C, 0x6900, + 0xE308, 0x0135, 0x8B8C, 0xBE59, 0xBB07, 0x90A0, 0xBC20, 0x7980, 0x0157, 0x030C, 0x8B8B, 0xB903, + 0x8809, 0xBEC6, 0x013E, 0x69AC, 0x90AB, 0x69AD, 0x90AB, 0x0813, 0x660A, 0xE344, 0x0144, 0x0309, + 0x830C, 0xBC20, 0x7980, 0x0157, 0x6955, 0xE388, 0x0157, 0x7C38, 0xBF0B, 0x0578, 0xF500, 0xBF0B, + 0x0538, 0xB907, 0x8809, 0xBEC6, 0x0156, 0x10AB, 0x90AA, 0x6974, 0xE388, 0x0163, 0xAE72, 0x0540, + 0xF500, 0xAE72, 0x0500, 0xAE61, 0x103B, 0x7A80, 0x02F6, 0x6978, 0xE388, 0x0182, 0x8B8C, 0xBF0C, + 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA20, 0x8812, 0x733D, 0x7A80, 0x0380, 0x733E, 0x7A80, 0x0380, + 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA2C, 0x8812, 0x733F, 0x7A80, 0x0380, 0x7340, + 0x7A80, 0x0380, 0x6975, 0xE388, 0x018E, 0xAE72, 0x0548, 0xF500, 0xAE72, 0x0508, 0xAE61, 0x1041, + 0x7A80, 0x02F6, 0x6979, 0xE388, 0x01AD, 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA18, + 0x8812, 0x7343, 0x7A80, 0x0380, 0x7344, 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, + 0x0814, 0xBA24, 0x8812, 0x7345, 0x7A80, 0x0380, 0x7346, 0x7A80, 0x0380, 0x6976, 0xE388, 0x01B9, + 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x1047, 0x7A80, 0x02F6, 0x697A, 0xE388, 0x01D8, + 0x8B8C, 0xBF0C, 0x0560, 0xE500, 0x7C40, 0x0814, 0xBA08, 0x8812, 0x7349, 0x7A80, 0x0380, 0x734A, + 0x7A80, 0x0380, 0x8B8C, 0xBF0C, 0x056C, 0xE500, 0x7C40, 0x0814, 0xBA14, 0x8812, 0x734B, 0x7A80, + 0x0380, 0x734C, 0x7A80, 0x0380, 0xBC21, 0xAE1C, 0x1090, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, + 0x0812, 0xB804, 0x8813, 0x8B8D, 0xBF0D, 0x056C, 0xE500, 0x7C40, 0x0815, 0xB804, 0x8811, 0x7A80, + 0x034A, 0x8B8A, 0xBF0A, 0x0560, 0xE500, 0x7C40, 0x731F, 0xB903, 0x8809, 0xBEC6, 0x01F9, 0x548A, + 0xBE03, 0x98A0, 0x7320, 0xB903, 0x8809, 0xBEC6, 0x0201, 0x548A, 0xBE03, 0x98A0, 0x1F20, 0x2F1F, + 0x9826, 0xBC20, 0x6935, 0xE388, 0x03A1, 0x6933, 0xB801, 0x9033, 0xBFA0, 0x02EE, 0xE308, 0x03A1, + 0x9033, 0xBF00, 0x6951, 0xE388, 0x021F, 0x7334, 0xBE80, 0x5760, 0xBE03, 0x9F7E, 0xBE59, 0x9034, + 0x697E, 0x0D51, 0x9013, 0xBC20, 0x695C, 0xE388, 0x03A1, 0x735E, 0xBE80, 0x5760, 0xBE03, 0x9F7E, + 0xBE59, 0x905E, 0x697E, 0x0D5C, 0x9013, 0x7980, 0x03A1, 0x7A80, 0x038A, 0xBF01, 0xBE43, 0x6977, + 0xE388, 0x024E, 0xAE61, 0x104D, 0x0061, 0x8B88, 0x6980, 0xE388, 0x024E, 0x9071, 0x0D71, 0x000B, + 0xAFA0, 0x8010, 0xAFA0, 0x8010, 0x0810, 0x660A, 0xE308, 0x0249, 0x0009, 0x0810, 0x660C, 0xE388, + 0x024E, 0x800B, 0xBC20, 0x697B, 0xE388, 0x03A1, 0xBF0A, 0x109E, 0x8B8A, 0xAF80, 0x8014, 0x4C80, + 0xE100, 0x0266, 0x697C, 0xBF90, 0x0560, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0564, 0x9073, 0x0473, + 0x7980, 0x0270, 0x697C, 0xBF90, 0x0520, 0x9072, 0x0372, 0x697C, 0xBF90, 0x0524, 0x9073, 0x0473, + 0x697C, 0xB801, 0x907C, 0xBF0A, 0x10FD, 0x8B8A, 0xAF80, 0x8010, 0x734F, 0x548A, 0xBE03, 0x9880, + 0xBC21, 0x7326, 0x548B, 0xBE03, 0x618B, 0x988C, 0xBE03, 0x6180, 0x9880, 0x7980, 0x03A1, 0x7A80, + 0x038A, 0x0D28, 0x4711, 0xE100, 0x02BE, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, 0x02B6, + 0xBFA0, 0x0800, 0xE388, 0x02B2, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02A3, 0x6909, + 0x900B, 0x7980, 0x02A5, 0xAF0B, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, 0x02ED, + 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x6909, 0x900B, 0x7980, 0x02B8, 0xAF0B, 0x4005, + 0xAF05, 0x4003, 0xAF06, 0x4004, 0x7980, 0x02ED, 0xAF12, 0x4006, 0x6912, 0xBFB0, 0x0C00, 0xE388, + 0x02E7, 0xBFA0, 0x0800, 0xE388, 0x02E3, 0x6912, 0xBFB0, 0x0C00, 0xBFA0, 0x0400, 0xE388, 0x02D4, + 0x690D, 0x9010, 0x7980, 0x02D6, 0xAF10, 0x4005, 0x6901, 0x9005, 0x6902, 0x9006, 0x4311, 0xE100, + 0x02ED, 0x6911, 0xBFC0, 0x2000, 0x9011, 0x7980, 0x02ED, 0x690D, 0x9010, 0x7980, 0x02E9, 0xAF10, + 0x4005, 0xAF05, 0x4003, 0xAF06, 0x4004, 0xBC20, 0x6970, 0x9071, 0x7A80, 0x0078, 0x6971, 0x9070, + 0x7980, 0x03A1, 0xBC20, 0x0361, 0x8B8B, 0x6980, 0xEF88, 0x0272, 0x0372, 0x7804, 0x9071, 0x0D71, + 0x8B8A, 0x000B, 0xB903, 0x8809, 0xBEC6, 0x0309, 0x69A8, 0x90AB, 0x69A8, 0x90AA, 0x0810, 0x660A, + 0xE344, 0x030F, 0x0009, 0x0810, 0x660C, 0xE388, 0x0314, 0x800B, 0xBC20, 0x6961, 0xB801, 0x9061, + 0x7980, 0x02F7, 0x7A80, 0x038A, 0x5D35, 0x0001, 0x6934, 0xB801, 0x9034, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4880, 0xAE72, 0x0550, 0xF500, 0xAE72, 0x0510, 0xAE61, 0x1051, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x7A80, 0x038A, 0x5D35, 0x0002, 0x695E, 0xB801, 0x905E, 0xBF0A, 0x109E, 0x8B8A, + 0xAF80, 0x8014, 0x4780, 0xAE72, 0x0558, 0xF500, 0xAE72, 0x0518, 0xAE61, 0x105C, 0x7A80, 0x02F6, + 0x7980, 0x03A1, 0x001C, 0x8B88, 0x6980, 0xEF88, 0x901D, 0x0D1D, 0x100F, 0x6610, 0xE38C, 0x0358, + 0x690E, 0x6610, 0x620F, 0x660D, 0xBA0F, 0xE301, 0x037A, 0x0410, 0x8B8A, 0xB903, 0x8809, 0xBEC6, + 0x036C, 0x6A8C, 0x61AA, 0x98AB, 0x6A8C, 0x61AB, 0x98AD, 0x6A8C, 0x61AD, 0x98A9, 0x6A8C, 0x61A9, + 0x98AA, 0x7C04, 0x8B8B, 0x7C04, 0x8B8D, 0x7C04, 0x8B89, 0x7C04, 0x0814, 0x660E, 0xE308, 0x0379, + 0x040D, 0x8410, 0xBC21, 0x691C, 0xB801, 0x901C, 0x7980, 0x034A, 0xB903, 0x8809, 0x8B8A, 0xBEC6, + 0x0388, 0x54AC, 0xBE03, 0x618C, 0x98AA, 0xEF00, 0xBC20, 0xBE46, 0x0809, 0x906B, 0x080A, 0x906C, + 0x080B, 0x906D, 0x081A, 0x9062, 0x081B, 0x9063, 0x081E, 0x9064, 0xBE59, 0x881E, 0x8065, 0x8166, + 0x8267, 0x8368, 0x8469, 0x856A, 0xEF00, 0xBC20, 0x696B, 0x8809, 0x696C, 0x880A, 0x696D, 0x880B, + 0x6962, 0x881A, 0x6963, 0x881B, 0x6964, 0x881E, 0x0065, 0x0166, 0x0267, 0x0368, 0x0469, 0x056A, + 0xBE3A, +}; + +/* + * Mini sample rate converter code image + * that is to be loaded at 0x400 on the DSP. + */ +static u16 assp_minisrc_image[] = { + + 0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412, + 0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41, + 0x7A80, 0x002A, 0xBE40, 0x3029, 0xEFCC, 0xBE41, 0x7A80, 0x0028, 0xBE40, 0x3028, 0xEFCC, 0x6907, + 0xE308, 0x042A, 0x6909, 0x902C, 0x7980, 0x042C, 0x690D, 0x902C, 0x1009, 0x881A, 0x100A, 0xBA01, + 0x881B, 0x100D, 0x881C, 0x100E, 0xBA01, 0x881D, 0xBF80, 0x00ED, 0x881E, 0x050C, 0x0124, 0xB904, + 0x9027, 0x6918, 0xE308, 0x04B3, 0x902D, 0x6913, 0xBFA0, 0x7598, 0xF704, 0xAE2D, 0x00FF, 0x8B8D, + 0x6919, 0xE308, 0x0463, 0x691A, 0xE308, 0x0456, 0xB907, 0x8809, 0xBEC6, 0x0453, 0x10A9, 0x90AD, + 0x7980, 0x047C, 0xB903, 0x8809, 0xBEC6, 0x0460, 0x1889, 0x6C22, 0x90AD, 0x10A9, 0x6E23, 0x6C22, + 0x90AD, 0x7980, 0x047C, 0x101A, 0xE308, 0x046F, 0xB903, 0x8809, 0xBEC6, 0x046C, 0x10A9, 0x90A0, + 0x90AD, 0x7980, 0x047C, 0xB901, 0x8809, 0xBEC6, 0x047B, 0x1889, 0x6C22, 0x90A0, 0x90AD, 0x10A9, + 0x6E23, 0x6C22, 0x90A0, 0x90AD, 0x692D, 0xE308, 0x049C, 0x0124, 0xB703, 0xB902, 0x8818, 0x8B89, + 0x022C, 0x108A, 0x7C04, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99A0, + 0x108A, 0x90A0, 0x692B, 0x881F, 0x7E80, 0x055B, 0x692A, 0x8809, 0x8B89, 0x99AF, 0x7B99, 0x0484, + 0x0124, 0x060F, 0x101B, 0x2013, 0x901B, 0xBFA0, 0x7FFF, 0xE344, 0x04AC, 0x901B, 0x8B89, 0x7A80, + 0x051A, 0x6927, 0xBA01, 0x9027, 0x7A80, 0x0523, 0x6927, 0xE308, 0x049E, 0x7980, 0x050F, 0x0624, + 0x1026, 0x2013, 0x9026, 0xBFA0, 0x7FFF, 0xE304, 0x04C0, 0x8B8D, 0x7A80, 0x051A, 0x7980, 0x04B4, + 0x9026, 0x1013, 0x3026, 0x901B, 0x8B8D, 0x7A80, 0x051A, 0x7A80, 0x0523, 0x1027, 0xBA01, 0x9027, + 0xE308, 0x04B4, 0x0124, 0x060F, 0x8B89, 0x691A, 0xE308, 0x04EA, 0x6919, 0xE388, 0x04E0, 0xB903, + 0x8809, 0xBEC6, 0x04DD, 0x1FA0, 0x2FAE, 0x98A9, 0x7980, 0x050F, 0xB901, 0x8818, 0xB907, 0x8809, + 0xBEC6, 0x04E7, 0x10EE, 0x90A9, 0x7980, 0x050F, 0x6919, 0xE308, 0x04FE, 0xB903, 0x8809, 0xBE46, + 0xBEC6, 0x04FA, 0x17A0, 0xBE1E, 0x1FAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0xBE47, + 0x7980, 0x050F, 0xB901, 0x8809, 0xBEC6, 0x050E, 0x16A0, 0x26A0, 0xBFB7, 0xFF00, 0xBE1E, 0x1EA0, + 0x2EAE, 0xBFBF, 0xFF00, 0xBE13, 0xBFDF, 0x8080, 0x99A9, 0x850C, 0x860F, 0x6907, 0xE388, 0x0516, + 0x0D07, 0x8510, 0xBE59, 0x881E, 0xBE4A, 0xEF00, 0x101E, 0x901C, 0x101F, 0x901D, 0x10A0, 0x901E, + 0x10A0, 0x901F, 0xEF00, 0x101E, 0x301C, 0x9020, 0x731B, 0x5420, 0xBE03, 0x9825, 0x1025, 0x201C, + 0x9025, 0x7325, 0x5414, 0xBE03, 0x8B8E, 0x9880, 0x692F, 0xE388, 0x0539, 0xBE59, 0xBB07, 0x6180, + 0x9880, 0x8BA0, 0x101F, 0x301D, 0x9021, 0x731B, 0x5421, 0xBE03, 0x982E, 0x102E, 0x201D, 0x902E, + 0x732E, 0x5415, 0xBE03, 0x9880, 0x692F, 0xE388, 0x054F, 0xBE59, 0xBB07, 0x6180, 0x9880, 0x8BA0, + 0x6918, 0xEF08, 0x7325, 0x5416, 0xBE03, 0x98A0, 0x732E, 0x5417, 0xBE03, 0x98A0, 0xEF00, 0x8BA0, + 0xBEC6, 0x056B, 0xBE59, 0xBB04, 0xAA90, 0xBE04, 0xBE1E, 0x99E0, 0x8BE0, 0x69A0, 0x90D0, 0x69A0, + 0x90D0, 0x081F, 0xB805, 0x881F, 0x8B90, 0x69A0, 0x90D0, 0x69A0, 0x9090, 0x8BD0, 0x8BD8, 0xBE1F, + 0xEF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + diff --git a/trunk/sound/oss/maui.c b/trunk/sound/oss/maui.c new file mode 100644 index 000000000000..9130fcf96552 --- /dev/null +++ b/trunk/sound/oss/maui.c @@ -0,0 +1,477 @@ +/* + * sound/oss/maui.c + * + * The low level driver for Turtle Beach Maui and Tropez. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox General clean up, use kernel IRQ + * system + * Christoph Hellwig Adapted to module_init/module_exit + * Bartlomiej Zolnierkiewicz + * Added __init to download_code() + * + * Status: + * Andrew J. Kroll Tested 06/01/1999 with: + * * OSWF.MOT File Version: 1.15 + * * OSWF.MOT File Dated: 09/12/94 + * * Older versions will cause problems. + */ + +#include +#include +#include + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include "sound_config.h" +#include "sound_firmware.h" + +#include "mpu401.h" + +static int maui_base = 0x330; + +static volatile int irq_ok; +static int *maui_osp; + +#define HOST_DATA_PORT (maui_base + 2) +#define HOST_STAT_PORT (maui_base + 3) +#define HOST_CTRL_PORT (maui_base + 3) + +#define STAT_TX_INTR 0x40 +#define STAT_TX_AVAIL 0x20 +#define STAT_TX_IENA 0x10 +#define STAT_RX_INTR 0x04 +#define STAT_RX_AVAIL 0x02 +#define STAT_RX_IENA 0x01 + +static int (*orig_load_patch)(int dev, int format, const char __user *addr, + int offs, int count, int pmgr_flag) = NULL; + +#include "maui_boot.h" + +static int maui_wait(int mask) +{ + int i; + + /* + * Perform a short initial wait without sleeping + */ + + for (i = 0; i < 100; i++) + if (inb(HOST_STAT_PORT) & mask) + return 1; + + /* + * Wait up to 15 seconds with sleeping + */ + + for (i = 0; i < 150; i++) { + if (inb(HOST_STAT_PORT) & mask) + return 1; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + if (signal_pending(current)) + return 0; + } + return 0; +} + +static int maui_read(void) +{ + if (maui_wait(STAT_RX_AVAIL)) + return inb(HOST_DATA_PORT); + return -1; +} + +static int maui_write(unsigned char data) +{ + if (maui_wait(STAT_TX_AVAIL)) { + outb((data), HOST_DATA_PORT); + return 1; + } + printk(KERN_WARNING "Maui: Write timeout\n"); + return 0; +} + +static irqreturn_t mauiintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + irq_ok = 1; + return IRQ_HANDLED; +} + +static int __init download_code(void) +{ + int i, lines = 0; + int eol_seen = 0, done = 0; + int skip = 1; + + printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); + + for (i = 0; i < maui_osLen; i++) { + if (maui_os[i] != '\r') { + if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) { + skip = 0; + + if (maui_os[i] == '\n') + eol_seen = skip = 1; + else if (maui_os[i] == 'S') { + if (maui_os[i + 1] == '8') + done = 1; + if (!maui_write(0xF1)) + goto failure; + if (!maui_write('S')) + goto failure; + } else { + if (!maui_write(maui_os[i])) + goto failure; + } + + if (eol_seen) { + int c = 0; + int n; + + eol_seen = 0; + + for (n = 0; n < 2; n++) { + if (maui_wait(STAT_RX_AVAIL)) { + c = inb(HOST_DATA_PORT); + break; + } + } + if (c != 0x80) { + printk("Download not acknowledged\n"); + return 0; + } + else if (!(lines++ % 10)) + printk("."); + + if (done) { + printk("\n"); + printk(KERN_INFO "Download complete\n"); + return 1; + } + } + } + } + } + +failure: + printk("\n"); + printk(KERN_ERR "Download failed!!!\n"); + return 0; +} + +static int __init maui_init(int irq) +{ + unsigned char bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); + return 0; + } + outb((0x00), HOST_CTRL_PORT); /* Reset */ + outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ + outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ + +#ifdef CONFIG_SMP + { + int i; + for (i = 0; i < 1000000 && !irq_ok; i++) + ; + if (!irq_ok) + return 0; + } +#endif + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + + printk(KERN_INFO "Turtle Beach Maui initialization\n"); + + if (!download_code()) + return 0; + + outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ + + /* Select mpu401 mode */ + + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) { + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) + printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); + } + printk(KERN_INFO "Maui initialized OK\n"); + return 1; +} + +static int maui_short_wait(int mask) { + int i; + + for (i = 0; i < 1000; i++) { + if (inb(HOST_STAT_PORT) & mask) { + return 1; + } + } + return 0; +} + +static int maui_load_patch(int dev, int format, const char __user *addr, + int offs, int count, int pmgr_flag) +{ + + struct sysex_info header; + unsigned long left, src_offs; + int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; + int i; + + if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ + return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); + + if (format != MAUI_PATCH) + { + printk(KERN_WARNING "Maui: Unknown patch format\n"); + } + if (count < hdr_size) { +/* printk("Maui error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; + + if (count < header.len) { + printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); + header.len = count; + } + left = header.len; + src_offs = 0; + + for (i = 0; i < left; i++) { + unsigned char data; + + if(get_user(*(unsigned char *) &data, (unsigned char __user *) &((addr)[hdr_size + i]))) + return -EFAULT; + if (i == 0 && !(data & 0x80)) + return -EINVAL; + + if (maui_write(data) == -1) + return -EIO; + } + + if ((i = maui_read()) != 0x80) { + if (i != -1) + printk("Maui: Error status %02x\n", i); + return -EIO; + } + return 0; +} + +static int __init probe_maui(struct address_info *hw_config) +{ + struct resource *ports; + int this_dev; + int i; + int tmp1, tmp2, ret; + + ports = request_region(hw_config->io_base, 2, "mpu401"); + if (!ports) + return 0; + + if (!request_region(hw_config->io_base + 2, 6, "Maui")) + goto out; + + maui_base = hw_config->io_base; + maui_osp = hw_config->osp; + + if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) + goto out2; + + /* + * Initialize the processor if necessary + */ + + if (maui_osLen > 0) { + if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || + !maui_write(0x9F) || /* Report firmware version */ + !maui_short_wait(STAT_RX_AVAIL) || + maui_read() == -1 || maui_read() == -1) + if (!maui_init(hw_config->irq)) + goto out3; + } + if (!maui_write(0xCF)) /* Report hardware version */ { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + goto out3; + } + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + goto out3; + } + if (tmp1 == 0xff || tmp2 == 0xff) + goto out3; + printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); + + if (!maui_write(0x9F)) /* Report firmware version */ + goto out3; + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) + goto out3; + + printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); + + if (!maui_write(0x85)) /* Report free DRAM */ + goto out3; + tmp1 = 0; + for (i = 0; i < 4; i++) { + tmp1 |= maui_read() << (7 * i); + } + printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); + + for (i = 0; i < 1000; i++) + if (probe_mpu401(hw_config, ports)) + break; + + ret = probe_mpu401(hw_config, ports); + if (!ret) + goto out3; + + conf_printf("Maui", hw_config); + + hw_config->irq *= -1; + hw_config->name = "Maui"; + attach_mpu401(hw_config, THIS_MODULE); + + if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ { + struct synth_operations *synth; + + this_dev = hw_config->slots[1]; + + /* + * Intercept patch loading calls so that they can be handled + * by the Maui driver. + */ + + synth = midi_devs[this_dev]->converter; + if (synth != NULL) { + synth->id = "MAUI"; + orig_load_patch = synth->load_patch; + synth->load_patch = &maui_load_patch; + } else + printk(KERN_ERR "Maui: Can't install patch loader\n"); + } + return 1; + +out3: + free_irq(hw_config->irq, NULL); +out2: + release_region(hw_config->io_base + 2, 6); +out: + release_region(hw_config->io_base, 2); + return 0; +} + +static void __exit unload_maui(struct address_info *hw_config) +{ + int irq = hw_config->irq; + release_region(hw_config->io_base + 2, 6); + unload_mpu401(hw_config); + + if (irq < 0) + irq = -irq; + if (irq > 0) + free_irq(irq, NULL); +} + +static int fw_load; + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; + +module_param(io, int, 0); +module_param(irq, int, 0); + +/* + * Install a Maui card. Needs mpu401 loaded already. + */ + +static int __init init_maui(void) +{ + printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); + + cfg.io_base = io; + cfg.irq = irq; + + if (cfg.io_base == -1 || cfg.irq == -1) { + printk(KERN_INFO "maui: irq and io must be set.\n"); + return -EINVAL; + } + + if (maui_os == NULL) { + fw_load = 1; + maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); + } + if (probe_maui(&cfg) == 0) + return -ENODEV; + + return 0; +} + +static void __exit cleanup_maui(void) +{ + if (fw_load && maui_os) + vfree(maui_os); + unload_maui(&cfg); +} + +module_init(init_maui); +module_exit(cleanup_maui); + +#ifndef MODULE +static int __init setup_maui(char *str) +{ + /* io, irq */ + int ints[3]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + + return 1; +} + +__setup("maui=", setup_maui); +#endif +MODULE_LICENSE("GPL"); diff --git a/trunk/sound/oss/midi_syms.c b/trunk/sound/oss/midi_syms.c new file mode 100644 index 000000000000..5b146ddf5725 --- /dev/null +++ b/trunk/sound/oss/midi_syms.c @@ -0,0 +1,29 @@ +/* + * Exported symbols for midi driver. + */ + +#include + +char midi_syms_symbol; + +#include "sound_config.h" +#define _MIDI_SYNTH_C_ +#include "midi_synth.h" + +EXPORT_SYMBOL(do_midi_msg); +EXPORT_SYMBOL(midi_synth_open); +EXPORT_SYMBOL(midi_synth_close); +EXPORT_SYMBOL(midi_synth_ioctl); +EXPORT_SYMBOL(midi_synth_kill_note); +EXPORT_SYMBOL(midi_synth_start_note); +EXPORT_SYMBOL(midi_synth_set_instr); +EXPORT_SYMBOL(midi_synth_reset); +EXPORT_SYMBOL(midi_synth_hw_control); +EXPORT_SYMBOL(midi_synth_aftertouch); +EXPORT_SYMBOL(midi_synth_controller); +EXPORT_SYMBOL(midi_synth_panning); +EXPORT_SYMBOL(midi_synth_setup_voice); +EXPORT_SYMBOL(midi_synth_send_sysex); +EXPORT_SYMBOL(midi_synth_bender); +EXPORT_SYMBOL(midi_synth_load_patch); +EXPORT_SYMBOL(MIDIbuf_avail); diff --git a/trunk/sound/oss/midi_synth.c b/trunk/sound/oss/midi_synth.c index 9e450988ed36..d2ab5c08b616 100644 --- a/trunk/sound/oss/midi_synth.c +++ b/trunk/sound/oss/midi_synth.c @@ -84,7 +84,6 @@ do_midi_msg(int synthno, unsigned char *msg, int mlen) ; } } -EXPORT_SYMBOL(do_midi_msg); static void midi_outc(int midi_dev, int data) @@ -277,7 +276,6 @@ int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg) return -EINVAL; } } -EXPORT_SYMBOL(midi_synth_ioctl); int midi_synth_kill_note(int dev, int channel, int note, int velocity) @@ -344,7 +342,6 @@ midi_synth_kill_note(int dev, int channel, int note, int velocity) return 0; } -EXPORT_SYMBOL(midi_synth_kill_note); int midi_synth_set_instr(int dev, int channel, int instr_no) @@ -367,7 +364,6 @@ midi_synth_set_instr(int dev, int channel, int instr_no) return 0; } -EXPORT_SYMBOL(midi_synth_set_instr); int midi_synth_start_note(int dev, int channel, int note, int velocity) @@ -409,7 +405,6 @@ midi_synth_start_note(int dev, int channel, int note, int velocity) } return 0; } -EXPORT_SYMBOL(midi_synth_start_note); void midi_synth_reset(int dev) @@ -417,7 +412,6 @@ midi_synth_reset(int dev) leave_sysex(dev); } -EXPORT_SYMBOL(midi_synth_reset); int midi_synth_open(int dev, int mode) @@ -450,7 +444,6 @@ midi_synth_open(int dev, int mode) return 1; } -EXPORT_SYMBOL(midi_synth_open); void midi_synth_close(int dev) @@ -466,13 +459,11 @@ midi_synth_close(int dev) midi_devs[orig_dev]->close(orig_dev); } -EXPORT_SYMBOL(midi_synth_close); void midi_synth_hw_control(int dev, unsigned char *event) { } -EXPORT_SYMBOL(midi_synth_hw_control); int midi_synth_load_patch(int dev, int format, const char __user *addr, @@ -551,13 +542,11 @@ midi_synth_load_patch(int dev, int format, const char __user *addr, midi_outc(orig_dev, 0xf7); return 0; } -EXPORT_SYMBOL(midi_synth_load_patch); - + void midi_synth_panning(int dev, int channel, int pressure) { } -EXPORT_SYMBOL(midi_synth_panning); - + void midi_synth_aftertouch(int dev, int channel, int pressure) { int orig_dev = synth_devs[dev]->midi_dev; @@ -587,7 +576,6 @@ void midi_synth_aftertouch(int dev, int channel, int pressure) midi_outc(orig_dev, pressure); } -EXPORT_SYMBOL(midi_synth_aftertouch); void midi_synth_controller(int dev, int channel, int ctrl_num, int value) @@ -616,7 +604,6 @@ midi_synth_controller(int dev, int channel, int ctrl_num, int value) midi_outc(orig_dev, ctrl_num); midi_outc(orig_dev, value & 0x7f); } -EXPORT_SYMBOL(midi_synth_controller); void midi_synth_bender(int dev, int channel, int value) @@ -648,13 +635,11 @@ midi_synth_bender(int dev, int channel, int value) midi_outc(orig_dev, value & 0x7f); midi_outc(orig_dev, (value >> 7) & 0x7f); } -EXPORT_SYMBOL(midi_synth_bender); void midi_synth_setup_voice(int dev, int voice, int channel) { } -EXPORT_SYMBOL(midi_synth_setup_voice); int midi_synth_send_sysex(int dev, unsigned char *bytes, int len) @@ -710,5 +695,3 @@ midi_synth_send_sysex(int dev, unsigned char *bytes, int len) return 0; } -EXPORT_SYMBOL(midi_synth_send_sysex); - diff --git a/trunk/sound/oss/midibuf.c b/trunk/sound/oss/midibuf.c index a40be0cf1d97..c0e4bbc22c80 100644 --- a/trunk/sound/oss/midibuf.c +++ b/trunk/sound/oss/midibuf.c @@ -414,11 +414,18 @@ unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) } +void MIDIbuf_init(void) +{ + /* drag in midi_syms.o */ + { + extern char midi_syms_symbol; + midi_syms_symbol = 0; + } +} + int MIDIbuf_avail(int dev) { if (midi_in_buf[dev]) return DATA_AVAIL (midi_in_buf[dev]); return 0; } -EXPORT_SYMBOL(MIDIbuf_avail); - diff --git a/trunk/sound/oss/mpu401.c b/trunk/sound/oss/mpu401.c index 162d07cc489f..321f4c4b5a7b 100644 --- a/trunk/sound/oss/mpu401.c +++ b/trunk/sound/oss/mpu401.c @@ -432,7 +432,16 @@ static void mpu401_input_loop(struct mpu_config *devc) devc->m_busy = 0; } -static irqreturn_t mpuintr(int irq, void *dev_id, struct pt_regs *dummy) +int intchk_mpu401(void *dev_id) +{ + struct mpu_config *devc; + int dev = (int) dev_id; + + devc = &dev_conf[dev]; + return input_avail(devc); +} + +irqreturn_t mpuintr(int irq, void *dev_id, struct pt_regs *dummy) { struct mpu_config *devc; int dev = (int) dev_id; @@ -1752,6 +1761,8 @@ static int mpu_timer_init(int midi_dev) EXPORT_SYMBOL(probe_mpu401); EXPORT_SYMBOL(attach_mpu401); EXPORT_SYMBOL(unload_mpu401); +EXPORT_SYMBOL(intchk_mpu401); +EXPORT_SYMBOL(mpuintr); static struct address_info cfg; diff --git a/trunk/sound/oss/mpu401.h b/trunk/sound/oss/mpu401.h index 84c0e9522ef7..bdc5bde641e6 100644 --- a/trunk/sound/oss/mpu401.h +++ b/trunk/sound/oss/mpu401.h @@ -10,3 +10,5 @@ int probe_mpu401(struct address_info *hw_config, struct resource *ports); int attach_mpu401(struct address_info * hw_config, struct module *owner); void unload_mpu401(struct address_info *hw_info); +int intchk_mpu401(void *dev_id); +irqreturn_t mpuintr(int irq, void *dev_id, struct pt_regs * dummy); diff --git a/trunk/sound/oss/opl3sa.c b/trunk/sound/oss/opl3sa.c new file mode 100644 index 000000000000..2535ed0b5fbf --- /dev/null +++ b/trunk/sound/oss/opl3sa.c @@ -0,0 +1,329 @@ +/* + * sound/oss/opl3sa.c + * + * Low level driver for Yamaha YMF701B aka OPL3-SA chip + * + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * Alan Cox Modularisation + * Christoph Hellwig Adapted to module_init/module_exit + * Arnaldo C. de Melo got rid of attach_uart401 + * + * FIXME: + * Check for install of mpu etc is wrong, should check result of the mss stuff + */ + +#include +#include +#include + +#undef SB_OK + +#include "sound_config.h" + +#include "ad1848.h" +#include "mpu401.h" + +#ifdef SB_OK +#include "sb.h" +static int sb_initialized; +#endif + +static DEFINE_SPINLOCK(lock); + +static unsigned char opl3sa_read(int addr) +{ + unsigned long flags; + unsigned char tmp; + + spin_lock_irqsave(&lock,flags); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + tmp = inb(0xf87); /* data */ + spin_unlock_irqrestore(&lock,flags); + + return tmp; +} + +static void opl3sa_write(int addr, int data) +{ + unsigned long flags; + + spin_lock_irqsave(&lock,flags); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + outb(((unsigned char) data), 0xf87); /* data */ + spin_unlock_irqrestore(&lock,flags); +} + +static int __init opl3sa_detect(void) +{ + int tmp; + + if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) + { + DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); + /* return 0; */ + } + + /* + * Check that the password feature has any effect + */ + + if (inb(0xf87) == tmp) + { + DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); + return 0; + } + tmp = (opl3sa_read(0x04) & 0xe0) >> 5; + + if (tmp != 0 && tmp != 1) + { + DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); + return 0; + } + DDB(printk("OPL3-SA mode %x detected\n", tmp)); + + opl3sa_write(0x01, 0x00); /* Disable MSS */ + opl3sa_write(0x02, 0x00); /* Disable SB */ + opl3sa_write(0x03, 0x00); /* Disable MPU */ + + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * OPL3-SA + */ + +static int __init probe_opl3sa_wss(struct address_info *hw_config, struct resource *ports) +{ + unsigned char tmp = 0x24; /* WSS enable */ + + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (OPL3-SA for example) + * return 0x00. + */ + + if (!opl3sa_detect()) + { + printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0xe80: + tmp |= 0x08; + break; + case 0xf40: + tmp |= 0x10; + break; + case 0x604: + tmp |= 0x18; + break; + default: + printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); + return 0; + } + + opl3sa_write(0x01, tmp); /* WSS setup register */ + + return probe_ms_sound(hw_config, ports); +} + +static void __init attach_opl3sa_wss(struct address_info *hw_config, struct resource *ports) +{ + int nm = num_mixers; + + /* FIXME */ + attach_ms_sound(hw_config, ports, THIS_MODULE); + if (num_mixers > nm) /* A mixer was installed */ + { + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } +} + + +static int __init probe_opl3sa_mpu(struct address_info *hw_config) +{ + unsigned char conf; + static signed char irq_bits[] = { + -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 + }; + + if (hw_config->irq > 10) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x332: + conf = 0x20; + break; + case 0x334: + conf = 0x40; + break; + case 0x300: + conf = 0x60; + break; + default: + return 0; /* Invalid port */ + } + + conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ + conf |= irq_bits[hw_config->irq] << 2; + + opl3sa_write(0x03, conf); + + hw_config->name = "OPL3-SA (MPU401)"; + + return probe_uart401(hw_config, THIS_MODULE); +} + +static void __exit unload_opl3sa_wss(struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region(0xf86, 2); + release_region(hw_config->io_base, 4); + + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); + sound_unload_audiodev(hw_config->slots[0]); +} + +static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config) +{ + unload_uart401(hw_config); +} + +#ifdef SB_OK +static inline void __exit unload_opl3sa_sb(struct address_info *hw_config) +{ + sb_dsp_unload(hw_config); +} +#endif + +static int found_mpu; + +static struct address_info cfg; +static struct address_info cfg_mpu; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata mpu_io = -1; +static int __initdata mpu_irq = -1; + +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(dma, int, 0); +module_param(dma2, int, 0); +module_param(mpu_io, int, 0); +module_param(mpu_irq, int, 0); + +static int __init init_opl3sa(void) +{ + struct resource *ports; + if (io == -1 || irq == -1 || dma == -1) { + printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); + return -EINVAL; + } + + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + cfg_mpu.io_base = mpu_io; + cfg_mpu.irq = mpu_irq; + + ports = request_region(io + 4, 4, "ad1848"); + if (!ports) + return -EBUSY; + + if (!request_region(0xf86, 2, "OPL3-SA"))/* Control port is busy */ { + release_region(io + 4, 4); + return 0; + } + + if (!request_region(io, 4, "WSS config")) { + release_region(0x86, 2); + release_region(io + 4, 4); + return 0; + } + + if (probe_opl3sa_wss(&cfg, ports) == 0) { + release_region(0xf86, 2); + release_region(io, 4); + release_region(io + 4, 4); + return -ENODEV; + } + + found_mpu=probe_opl3sa_mpu(&cfg_mpu); + + attach_opl3sa_wss(&cfg, ports); + return 0; +} + +static void __exit cleanup_opl3sa(void) +{ + if(found_mpu) + unload_opl3sa_mpu(&cfg_mpu); + unload_opl3sa_wss(&cfg); +} + +module_init(init_opl3sa); +module_exit(cleanup_opl3sa); + +#ifndef MODULE +static int __init setup_opl3sa(char *str) +{ + /* io, irq, dma, dma2, mpu_io, mpu_irq */ + int ints[7]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + mpu_io = ints[5]; + mpu_irq = ints[6]; + + return 1; +} + +__setup("opl3sa=", setup_opl3sa); +#endif +MODULE_LICENSE("GPL"); diff --git a/trunk/sound/oss/rme96xx.c b/trunk/sound/oss/rme96xx.c new file mode 100644 index 000000000000..f17d25b6f836 --- /dev/null +++ b/trunk/sound/oss/rme96xx.c @@ -0,0 +1,1857 @@ +/* (C) 2000 Guenter Geiger + with copy/pastes from the driver of Winfried Ritsch + based on es1370.c + + + + * 10 Jan 2001: 0.1 initial version + * 19 Jan 2001: 0.2 fixed bug in select() + * 27 Apr 2001: 0.3 more than one card usable + * 11 May 2001: 0.4 fixed for SMP, included into kernel source tree + * 17 May 2001: 0.5 draining code didn't work on new cards + * 18 May 2001: 0.6 remove synchronize_irq() call + * 17 Jul 2001: 0.7 updated xrmectrl to make it work for newer cards + * 2 feb 2002: 0.8 fixed pci device handling, see below for patches from Heiko (Thanks!) + Marcus Meissner + + Modifications - Heiko Purnhagen + HP20020108 fixed handling of "large" read() + HP20020116 towards REV 1.5 support, based on ALSA's card-rme9652.c + HP20020118 made mixer ioctl and handling of devices>1 more safe + HP20020201 fixed handling of "large" read() properly + added REV 1.5 S/P-DIF receiver support + SNDCTL_DSP_SPEED now returns the actual speed + * 10 Aug 2002: added synchronize_irq() again + +TODO: + - test more than one card --- done + - check for pci IOREGION (see es1370) in rme96xx_probe ?? + - error detection + - mmap interface + - mixer mmap interface + - mixer ioctl + - get rid of noise upon first open (why ??) + - allow multiple open (at least for read) + - allow multiple open for non overlapping regions + - recheck the multiple devices part (offsets of different devices, etc) + - do decent draining in _release --- done + - SMP support + - what about using fragstotal>2 for small fragsize? (HP20020118) + - add support for AFMT_S32_LE +*/ + +#ifndef RMEVERSION +#define RMEVERSION "0.8" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "rme96xx.h" + +#define NR_DEVICE 2 + +static int devices = 1; +module_param(devices, int, 0); +MODULE_PARM_DESC(devices, "number of dsp devices allocated by the driver"); + + +MODULE_AUTHOR("Guenter Geiger, geiger@debian.org"); +MODULE_DESCRIPTION("RME9652/36 \"Hammerfall\" Driver"); +MODULE_LICENSE("GPL"); + + +#ifdef DEBUG +#define DBG(x) printk("RME_DEBUG:");x +#define COMM(x) printk("RME_COMM: " x "\n"); +#else +#define DBG(x) while (0) {} +#define COMM(x) +#endif + +/*-------------------------------------------------------------------------- + Preporcessor Macros and Definitions + --------------------------------------------------------------------------*/ + +#define RME96xx_MAGIC 0x6473 + +/* Registers-Space in offsets from base address with 16MByte size */ + +#define RME96xx_IO_EXTENT 16l*1024l*1024l +#define RME96xx_CHANNELS_PER_CARD 26 + +/* Write - Register */ + +/* 0,4,8,12,16,20,24,28 ... hardware init (erasing fifo-pointer intern) */ +#define RME96xx_num_of_init_regs 8 + +#define RME96xx_init_buffer (0/4) +#define RME96xx_play_buffer (32/4) /* pointer to 26x64kBit RAM from mainboard */ +#define RME96xx_rec_buffer (36/4) /* pointer to 26x64kBit RAM from mainboard */ +#define RME96xx_control_register (64/4) /* exact meaning see below */ +#define RME96xx_irq_clear (96/4) /* irq acknowledge */ +#define RME96xx_time_code (100/4) /* if used with alesis adat */ +#define RME96xx_thru_base (128/4) /* 132...228 Thru for 26 channels */ +#define RME96xx_thru_channels RME96xx_CHANNELS_PER_CARD + +/* Read Register */ + +#define RME96xx_status_register 0 /* meaning see below */ + + + +/* Status Register: */ +/* ------------------------------------------------------------------------ */ +#define RME96xx_IRQ 0x0000001 /* IRQ is High if not reset by RMExx_irq_clear */ +#define RME96xx_lock_2 0x0000002 /* ADAT 3-PLL: 1=locked, 0=unlocked */ +#define RME96xx_lock_1 0x0000004 /* ADAT 2-PLL: 1=locked, 0=unlocked */ +#define RME96xx_lock_0 0x0000008 /* ADAT 1-PLL: 1=locked, 0=unlocked */ + +#define RME96xx_fs48 0x0000010 /* sample rate 0 ...44.1/88.2, 1 ... 48/96 Khz */ +#define RME96xx_wsel_rd 0x0000020 /* if Word-Clock is used and valid then 1 */ +#define RME96xx_buf_pos1 0x0000040 /* Bit 6..15 : Position of buffer-pointer in 64Bytes-blocks */ +#define RME96xx_buf_pos2 0x0000080 /* resolution +/- 1 64Byte/block (since 64Bytes bursts) */ + +#define RME96xx_buf_pos3 0x0000100 /* 10 bits = 1024 values */ +#define RME96xx_buf_pos4 0x0000200 /* if we mask off the first 6 bits, we can take the status */ +#define RME96xx_buf_pos5 0x0000400 /* register as sample counter in the hardware buffer */ +#define RME96xx_buf_pos6 0x0000800 + +#define RME96xx_buf_pos7 0x0001000 +#define RME96xx_buf_pos8 0x0002000 +#define RME96xx_buf_pos9 0x0004000 +#define RME96xx_buf_pos10 0x0008000 + +#define RME96xx_sync_2 0x0010000 /* if ADAT-IN3 synced to system clock */ +#define RME96xx_sync_1 0x0020000 /* if ADAT-IN2 synced to system clock */ +#define RME96xx_sync_0 0x0040000 /* if ADAT-IN1 synced to system clock */ +#define RME96xx_DS_rd 0x0080000 /* 1=Double Speed, 0=Normal Speed */ + +#define RME96xx_tc_busy 0x0100000 /* 1=time-code copy in progress (960ms) */ +#define RME96xx_tc_out 0x0200000 /* time-code out bit */ +#define RME96xx_F_0 0x0400000 /* 000=64kHz, 100=88.2kHz, 011=96kHz */ +#define RME96xx_F_1 0x0800000 /* 111=32kHz, 110=44.1kHz, 101=48kHz, */ + +#define RME96xx_F_2 0x1000000 /* 001=Rev 1.5+ external Crystal Chip */ +#define RME96xx_ERF 0x2000000 /* Error-Flag of SDPIF Receiver (1=No Lock)*/ +#define RME96xx_buffer_id 0x4000000 /* toggles by each interrupt on rec/play */ +#define RME96xx_tc_valid 0x8000000 /* 1 = a signal is detected on time-code input */ +#define RME96xx_SPDIF_READ 0x10000000 /* byte available from Rev 1.5+ SPDIF interface */ + +/* Status Register Fields */ + +#define RME96xx_lock (RME96xx_lock_0|RME96xx_lock_1|RME96xx_lock_2) +#define RME96xx_sync (RME96xx_sync_0|RME96xx_sync_1|RME96xx_sync_2) +#define RME96xx_F (RME96xx_F_0|RME96xx_F_1|RME96xx_F_2) +#define rme96xx_decode_spdif_rate(x) ((x)>>22) + +/* Bit 6..15 : h/w buffer pointer */ +#define RME96xx_buf_pos 0x000FFC0 +/* Bits 31,30,29 are bits 5,4,3 of h/w pointer position on later + Rev G EEPROMS and Rev 1.5 cards or later. +*/ +#define RME96xx_REV15_buf_pos(x) ((((x)&0xE0000000)>>26)|((x)&RME96xx_buf_pos)) + + +/* Control-Register: */ +/*--------------------------------------------------------------------------------*/ + +#define RME96xx_start_bit 0x0001 /* start record/play */ +#define RME96xx_latency0 0x0002 /* Buffer size / latency */ +#define RME96xx_latency1 0x0004 /* buffersize = 512Bytes * 2^n */ +#define RME96xx_latency2 0x0008 /* 0=64samples ... 7=8192samples */ + +#define RME96xx_Master 0x0010 /* Clock Mode 1=Master, 0=Slave/Auto */ +#define RME96xx_IE 0x0020 /* Interupt Enable */ +#define RME96xx_freq 0x0040 /* samplerate 0=44.1/88.2, 1=48/96 kHz*/ +#define RME96xx_freq1 0x0080 /* samplerate 0=32 kHz, 1=other rates ??? (from ALSA, but may be wrong) */ +#define RME96xx_DS 0x0100 /* double speed 0=44.1/48, 1=88.2/96 Khz */ +#define RME96xx_PRO 0x0200 /* SPDIF-OUT 0=consumer, 1=professional */ +#define RME96xx_EMP 0x0400 /* SPDIF-OUT emphasis 0=off, 1=on */ +#define RME96xx_Dolby 0x0800 /* SPDIF-OUT non-audio bit 1=set, 0=unset */ + +#define RME96xx_opt_out 0x1000 /* use 1st optical OUT as SPDIF: 1=yes, 0=no */ +#define RME96xx_wsel 0x2000 /* use Wordclock as sync (overwrites master) */ +#define RME96xx_inp_0 0x4000 /* SPDIF-IN 00=optical (ADAT1), */ +#define RME96xx_inp_1 0x8000 /* 01=coaxial (Cinch), 10=internal CDROM */ + +#define RME96xx_SyncRef0 0x10000 /* preferred sync-source in autosync */ +#define RME96xx_SyncRef1 0x20000 /* 00=ADAT1, 01=ADAT2, 10=ADAT3, 11=SPDIF */ + +#define RME96xx_SPDIF_RESET (1<<18) /* Rev 1.5+: h/w SPDIF receiver */ +#define RME96xx_SPDIF_SELECT (1<<19) +#define RME96xx_SPDIF_CLOCK (1<<20) +#define RME96xx_SPDIF_WRITE (1<<21) +#define RME96xx_ADAT1_INTERNAL (1<<22) /* Rev 1.5+: if set, internal CD connector carries ADAT */ + + +#define RME96xx_ctrl_init (RME96xx_latency0 |\ + RME96xx_Master |\ + RME96xx_inp_1) + + + +/* Control register fields and shortcuts */ + +#define RME96xx_latency (RME96xx_latency0|RME96xx_latency1|RME96xx_latency2) +#define RME96xx_inp (RME96xx_inp_0|RME96xx_inp_1) +#define RME96xx_SyncRef (RME96xx_SyncRef0|RME96xx_SyncRef1) +#define RME96xx_mixer_allowed (RME96xx_Master|RME96xx_PRO|RME96xx_EMP|RME96xx_Dolby|RME96xx_opt_out|RME96xx_wsel|RME96xx_inp|RME96xx_SyncRef|RME96xx_ADAT1_INTERNAL) + +/* latency = 512Bytes * 2^n, where n is made from Bit3 ... Bit1 (??? HP20020201) */ + +#define RME96xx_SET_LATENCY(x) (((x)&0x7)<<1) +#define RME96xx_GET_LATENCY(x) (((x)>>1)&0x7) +#define RME96xx_SET_inp(x) (((x)&0x3)<<14) +#define RME96xx_GET_inp(x) (((x)>>14)&0x3) +#define RME96xx_SET_SyncRef(x) (((x)&0x3)<<17) +#define RME96xx_GET_SyncRef(x) (((x)>>17)&0x3) + + +/* buffer sizes */ +#define RME96xx_BYTES_PER_SAMPLE 4 /* sizeof(u32) */ +#define RME_16K 16*1024 + +#define RME96xx_DMA_MAX_SAMPLES (RME_16K) +#define RME96xx_DMA_MAX_SIZE (RME_16K * RME96xx_BYTES_PER_SAMPLE) +#define RME96xx_DMA_MAX_SIZE_ALL (RME96xx_DMA_MAX_SIZE * RME96xx_CHANNELS_PER_CARD) + +#define RME96xx_NUM_OF_FRAGMENTS 2 +#define RME96xx_FRAGMENT_MAX_SIZE (RME96xx_DMA_MAX_SIZE/2) +#define RME96xx_FRAGMENT_MAX_SAMPLES (RME96xx_DMA_MAX_SAMPLES/2) +#define RME96xx_MAX_LATENCY 7 /* 16k samples */ + + +#define RME96xx_MAX_DEVS 4 /* we provide some OSS stereodevs */ +#define RME96xx_MASK_DEVS 0x3 /* RME96xx_MAX_DEVS-1 */ + +#define RME_MESS "rme96xx:" +/*------------------------------------------------------------------------ + Types, struct and function declarations + ------------------------------------------------------------------------*/ + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT RME_MESS" invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != RME96xx_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + + +static struct file_operations rme96xx_audio_fops; +static struct file_operations rme96xx_mixer_fops; +static int numcards; + +typedef int32_t raw_sample_t; + +typedef struct _rme96xx_info { + + /* hardware settings */ + int magic; + struct pci_dev * pcidev; /* pci_dev structure */ + unsigned long __iomem *iobase; + unsigned int irq; + + /* list of rme96xx devices */ + struct list_head devs; + + spinlock_t lock; + + u32 *recbuf; /* memory for rec buffer */ + u32 *playbuf; /* memory for play buffer */ + + u32 control_register; + + u32 thru_bits; /* thru 1=on, 0=off channel 1=Bit1... channel 26= Bit26 */ + + int hw_rev; /* h/w rev * 10 (i.e. 1.5 has hw_rev = 15) */ + char *card_name; /* hammerfall or hammerfall light names */ + + int open_count; /* unused ??? HP20020201 */ + + int rate; + int latency; + unsigned int fragsize; + int started; + + int hwptr; /* can be negativ because of pci burst offset */ + unsigned int hwbufid; /* set by interrupt, buffer which is written/read now */ + + struct dmabuf { + + unsigned int format; + int formatshift; + int inchannels; /* number of channels for device */ + int outchannels; /* number of channels for device */ + int mono; /* if true, we play mono on 2 channels */ + int inoffset; /* which channel is considered the first one */ + int outoffset; + + /* state */ + int opened; /* open() made */ + int started; /* first write/read */ + int mmapped; /* mmap */ + int open_mode; + + struct _rme96xx_info *s; + + /* pointer to read/write position in buffer */ + unsigned readptr; + unsigned writeptr; + + unsigned error; /* over/underruns cleared on sync again */ + + /* waiting and locking */ + wait_queue_head_t wait; + struct mutex open_mutex; + wait_queue_head_t open_wait; + + } dma[RME96xx_MAX_DEVS]; + + int dspnum[RME96xx_MAX_DEVS]; /* register with sound subsystem */ + int mixer; /* register with sound subsystem */ +} rme96xx_info; + + +/* fiddling with the card (first level hardware control) */ + +static inline void rme96xx_set_ctrl(rme96xx_info* s,int mask) +{ + + s->control_register|=mask; + writel(s->control_register,s->iobase + RME96xx_control_register); + +} + +static inline void rme96xx_unset_ctrl(rme96xx_info* s,int mask) +{ + + s->control_register&=(~mask); + writel(s->control_register,s->iobase + RME96xx_control_register); + +} + +static inline int rme96xx_get_sample_rate_status(rme96xx_info* s) +{ + int val; + u32 status; + status = readl(s->iobase + RME96xx_status_register); + val = (status & RME96xx_fs48) ? 48000 : 44100; + if (status & RME96xx_DS_rd) + val *= 2; + return val; +} + +static inline int rme96xx_get_sample_rate_ctrl(rme96xx_info* s) +{ + int val; + val = (s->control_register & RME96xx_freq) ? 48000 : 44100; + if (s->control_register & RME96xx_DS) + val *= 2; + return val; +} + + +/* code from ALSA card-rme9652.c for rev 1.5 SPDIF receiver HP 20020201 */ + +static void rme96xx_spdif_set_bit (rme96xx_info* s, int mask, int onoff) +{ + if (onoff) + s->control_register |= mask; + else + s->control_register &= ~mask; + + writel(s->control_register,s->iobase + RME96xx_control_register); +} + +static void rme96xx_spdif_write_byte (rme96xx_info* s, const int val) +{ + long mask; + long i; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + if (val & mask) + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_WRITE, 1); + else + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_WRITE, 0); + + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 1); + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 0); + } +} + +static int rme96xx_spdif_read_byte (rme96xx_info* s) +{ + long mask; + long val; + long i; + + val = 0; + + for (i = 0, mask = 0x80; i < 8; i++, mask >>= 1) { + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 1); + if (readl(s->iobase + RME96xx_status_register) & RME96xx_SPDIF_READ) + val |= mask; + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_CLOCK, 0); + } + + return val; +} + +static void rme96xx_write_spdif_codec (rme96xx_info* s, const int address, const int data) +{ + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1); + rme96xx_spdif_write_byte (s, 0x20); + rme96xx_spdif_write_byte (s, address); + rme96xx_spdif_write_byte (s, data); + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0); +} + + +static int rme96xx_spdif_read_codec (rme96xx_info* s, const int address) +{ + int ret; + + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1); + rme96xx_spdif_write_byte (s, 0x20); + rme96xx_spdif_write_byte (s, address); + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0); + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 1); + + rme96xx_spdif_write_byte (s, 0x21); + ret = rme96xx_spdif_read_byte (s); + rme96xx_spdif_set_bit (s, RME96xx_SPDIF_SELECT, 0); + + return ret; +} + +static void rme96xx_initialize_spdif_receiver (rme96xx_info* s) +{ + /* XXX what unsets this ? */ + /* no idea ??? HP 20020201 */ + + s->control_register |= RME96xx_SPDIF_RESET; + + rme96xx_write_spdif_codec (s, 4, 0x40); + rme96xx_write_spdif_codec (s, 17, 0x13); + rme96xx_write_spdif_codec (s, 6, 0x02); +} + +static inline int rme96xx_spdif_sample_rate (rme96xx_info *s, int *spdifrate) +{ + unsigned int rate_bits; + + *spdifrate = 0x1; + if (readl(s->iobase + RME96xx_status_register) & RME96xx_ERF) { + return -1; /* error condition */ + } + + if (s->hw_rev == 15) { + + int x, y, ret; + + x = rme96xx_spdif_read_codec (s, 30); + + if (x != 0) + y = 48000 * 64 / x; + else + y = 0; + + if (y > 30400 && y < 33600) {ret = 32000; *spdifrate = 0x7;} + else if (y > 41900 && y < 46000) {ret = 44100; *spdifrate = 0x6;} + else if (y > 46000 && y < 50400) {ret = 48000; *spdifrate = 0x5;} + else if (y > 60800 && y < 67200) {ret = 64000; *spdifrate = 0x0;} + else if (y > 83700 && y < 92000) {ret = 88200; *spdifrate = 0x4;} + else if (y > 92000 && y < 100000) {ret = 96000; *spdifrate = 0x3;} + else {ret = 0; *spdifrate = 0x1;} + return ret; + } + + rate_bits = readl(s->iobase + RME96xx_status_register) & RME96xx_F; + + switch (*spdifrate = rme96xx_decode_spdif_rate(rate_bits)) { + case 0x7: + return 32000; + break; + + case 0x6: + return 44100; + break; + + case 0x5: + return 48000; + break; + + case 0x4: + return 88200; + break; + + case 0x3: + return 96000; + break; + + case 0x0: + return 64000; + break; + + default: + /* was an ALSA warning ... + snd_printk("%s: unknown S/PDIF input rate (bits = 0x%x)\n", + s->card_name, rate_bits); + */ + return 0; + break; + } +} + +/* end of code from ALSA card-rme9652.c */ + + + +/* the hwbuf in the status register seems to have some jitter, to get rid of + it, we first only let the numbers grow, to be on the secure side we + subtract a certain amount RME96xx_BURSTBYTES from the resulting number */ + +/* the function returns the hardware pointer in bytes */ +#define RME96xx_BURSTBYTES -64 /* bytes by which hwptr could be off */ + +static inline int rme96xx_gethwptr(rme96xx_info* s,int exact) +{ + unsigned long flags; + if (exact) { + unsigned int hwp; +/* the hwptr seems to be rather unreliable :(, so we don't use it */ + spin_lock_irqsave(&s->lock,flags); + + hwp = readl(s->iobase + RME96xx_status_register) & 0xffc0; + s->hwptr = (hwp < s->hwptr) ? s->hwptr : hwp; +// s->hwptr = hwp; + + spin_unlock_irqrestore(&s->lock,flags); + return (s->hwptr+RME96xx_BURSTBYTES) & ((s->fragsize<<1)-1); + } + return (s->hwbufid ? s->fragsize : 0); +} + +static inline void rme96xx_setlatency(rme96xx_info* s,int l) +{ + s->latency = l; + s->fragsize = 1<<(8+l); + rme96xx_unset_ctrl(s,RME96xx_latency); + rme96xx_set_ctrl(s,RME96xx_SET_LATENCY(l)); +} + + +static void rme96xx_clearbufs(struct dmabuf* dma) +{ + int i,j; + unsigned long flags; + + /* clear dmabufs */ + for(i=0;ioutchannels + dma->mono;j++) + memset(&dma->s->playbuf[(dma->outoffset + j)*RME96xx_DMA_MAX_SAMPLES], + 0, RME96xx_DMA_MAX_SIZE); + } + spin_lock_irqsave(&dma->s->lock,flags); + dma->writeptr = 0; + dma->readptr = 0; + spin_unlock_irqrestore(&dma->s->lock,flags); +} + +static int rme96xx_startcard(rme96xx_info *s,int stop) +{ + int i; + unsigned long flags; + + COMM ("startcard"); + if(s->control_register & RME96xx_IE){ + /* disable interrupt first */ + + rme96xx_unset_ctrl( s,RME96xx_start_bit ); + udelay(10); + rme96xx_unset_ctrl( s,RME96xx_IE); + spin_lock_irqsave(&s->lock,flags); /* timing is critical */ + s->started = 0; + spin_unlock_irqrestore(&s->lock,flags); + if (stop) { + COMM("Sound card stopped"); + return 1; + } + } + COMM ("interrupt disabled"); + /* first initialize all pointers on card */ + for(i=0;iiobase + i); + udelay(10); /* ?? */ + } + COMM ("regs cleaned"); + + spin_lock_irqsave(&s->lock,flags); /* timing is critical */ + udelay(10); + s->started = 1; + s->hwptr = 0; + spin_unlock_irqrestore(&s->lock,flags); + + rme96xx_set_ctrl( s, RME96xx_IE | RME96xx_start_bit); + + + COMM("Sound card started"); + + return 1; +} + + +static inline int rme96xx_getospace(struct dmabuf * dma, unsigned int hwp) +{ + int cnt; + int swptr; + unsigned long flags; + + spin_lock_irqsave(&dma->s->lock,flags); + swptr = dma->writeptr; + cnt = (hwp - swptr); + + if (cnt < 0) { + cnt = ((dma->s->fragsize<<1) - swptr); + } + spin_unlock_irqrestore(&dma->s->lock,flags); + return cnt; +} + +static inline int rme96xx_getispace(struct dmabuf * dma, unsigned int hwp) +{ + int cnt; + int swptr; + unsigned long flags; + + spin_lock_irqsave(&dma->s->lock,flags); + swptr = dma->readptr; + cnt = (hwp - swptr); + + if (cnt < 0) { + cnt = ((dma->s->fragsize<<1) - swptr); + } + spin_unlock_irqrestore(&dma->s->lock,flags); + return cnt; +} + + +static inline int rme96xx_copyfromuser(struct dmabuf* dma,const char __user * buffer,int count,int hop) +{ + int swptr = dma->writeptr; + switch (dma->format) { + case AFMT_S32_BLOCKED: + { + char __user * buf = (char __user *)buffer; + int cnt = count/dma->outchannels; + int i; + for (i=0;i < dma->outchannels;i++) { + char* hwbuf =(char*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=swptr; + + if (copy_from_user(hwbuf,buf, cnt)) + return -1; + buf+=hop; + } + swptr+=cnt; + break; + } + case AFMT_S16_LE: + { + int i,j; + int cnt = count/dma->outchannels; + for (i=0;i < dma->outchannels + dma->mono;i++) { + short __user * sbuf = (short __user *)buffer + i*(!dma->mono); + short* hwbuf =(short*) &dma->s->playbuf[(dma->outoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=(swptr>>1); + for (j=0;j<(cnt>>1);j++) { + hwbuf++; /* skip the low 16 bits */ + __get_user(*hwbuf++,sbuf++); + sbuf+=(dma->outchannels-1); + } + } + swptr += (cnt<<1); + break; + } + default: + printk(RME_MESS" unsupported format\n"); + return -1; + } /* switch */ + + swptr&=((dma->s->fragsize<<1) -1); + dma->writeptr = swptr; + + return 0; +} + +/* The count argument is the number of bytes */ +static inline int rme96xx_copytouser(struct dmabuf* dma,const char __user* buffer,int count,int hop) +{ + int swptr = dma->readptr; + switch (dma->format) { + case AFMT_S32_BLOCKED: + { + char __user * buf = (char __user *)buffer; + int cnt = count/dma->inchannels; + int i; + + for (i=0;i < dma->inchannels;i++) { + char* hwbuf =(char*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=swptr; + + if (copy_to_user(buf,hwbuf,cnt)) + return -1; + buf+=hop; + } + swptr+=cnt; + break; + } + case AFMT_S16_LE: + { + int i,j; + int cnt = count/dma->inchannels; + for (i=0;i < dma->inchannels;i++) { + short __user * sbuf = (short __user *)buffer + i; + short* hwbuf =(short*) &dma->s->recbuf[(dma->inoffset + i)*RME96xx_DMA_MAX_SAMPLES]; + hwbuf+=(swptr>>1); + for (j=0;j<(cnt>>1);j++) { + hwbuf++; + __put_user(*hwbuf++,sbuf++); + sbuf+=(dma->inchannels-1); + } + } + swptr += (cnt<<1); + break; + } + default: + printk(RME_MESS" unsupported format\n"); + return -1; + } /* switch */ + + swptr&=((dma->s->fragsize<<1) -1); + dma->readptr = swptr; + return 0; +} + + +static irqreturn_t rme96xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + rme96xx_info *s = (rme96xx_info *)dev_id; + struct dmabuf *db; + u32 status; + unsigned long flags; + + status = readl(s->iobase + RME96xx_status_register); + if (!(status & RME96xx_IRQ)) { + return IRQ_NONE; + } + + spin_lock_irqsave(&s->lock,flags); + writel(0,s->iobase + RME96xx_irq_clear); + + s->hwbufid = (status & RME96xx_buffer_id)>>26; + if ((status & 0xffc0) <= 256) s->hwptr = 0; + for(i=0;idma[i]); + if(db->started > 0) + wake_up(&(db->wait)); + } + spin_unlock_irqrestore(&s->lock,flags); + return IRQ_HANDLED; +} + + + +/*---------------------------------------------------------------------------- + PCI detection and module initialization stuff + ----------------------------------------------------------------------------*/ + +static void* busmaster_malloc(int size) { + int pg; /* 2 s exponent of memory size */ + char *buf; + + DBG(printk("kernel malloc pages ..\n")); + + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + + buf = (char *) __get_free_pages(GFP_KERNEL | GFP_DMA, pg); + + if (buf) { + struct page* page, *last_page; + + page = virt_to_page(buf); + last_page = page + (1 << pg); + DBG(printk("setting reserved bit\n")); + while (page < last_page) { + SetPageReserved(page); + page++; + } + return buf; + } + DBG(printk("allocated %ld",(long)buf)); + return NULL; +} + +static void busmaster_free(void* ptr,int size) { + int pg; + struct page* page, *last_page; + + if (ptr == NULL) + return; + + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + + page = virt_to_page(ptr); + last_page = page + (1 << pg); + while (page < last_page) { + ClearPageReserved(page); + page++; + } + DBG(printk("freeing pages\n")); + free_pages((unsigned long) ptr, pg); + DBG(printk("done\n")); +} + +/* initialize those parts of the info structure which are not pci detectable resources */ + +static int rme96xx_dmabuf_init(rme96xx_info * s,struct dmabuf* dma,int ioffset,int ooffset) { + + mutex_init(&dma->open_mutex); + init_waitqueue_head(&dma->open_wait); + init_waitqueue_head(&dma->wait); + dma->s = s; + dma->error = 0; + + dma->format = AFMT_S32_BLOCKED; + dma->formatshift = 0; + dma->inchannels = dma->outchannels = 1; + dma->inoffset = ioffset; + dma->outoffset = ooffset; + + dma->opened=0; + dma->started=0; + dma->mmapped=0; + dma->open_mode=0; + dma->mono=0; + + rme96xx_clearbufs(dma); + return 0; +} + + +static int rme96xx_init(rme96xx_info* s) +{ + int i; + int status; + unsigned short rev; + + DBG(printk("%s\n", __FUNCTION__)); + numcards++; + + s->magic = RME96xx_MAGIC; + + spin_lock_init(&s->lock); + + COMM ("setup busmaster memory") + s->recbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); + s->playbuf = busmaster_malloc(RME96xx_DMA_MAX_SIZE_ALL); + + if (!s->recbuf || !s->playbuf) { + printk(KERN_ERR RME_MESS" Unable to allocate busmaster memory\n"); + return -ENODEV; + } + + COMM ("setting rec and playbuffers") + + writel((u32) virt_to_bus(s->recbuf),s->iobase + RME96xx_rec_buffer); + writel((u32) virt_to_bus(s->playbuf),s->iobase + RME96xx_play_buffer); + + COMM ("initializing control register") + rme96xx_unset_ctrl(s,0xffffffff); + rme96xx_set_ctrl(s,RME96xx_ctrl_init); + + + COMM ("setup devices") + for (i=0;i < devices;i++) { + struct dmabuf * dma = &s->dma[i]; + rme96xx_dmabuf_init(s,dma,2*i,2*i); + } + + /* code from ALSA card-rme9652.c HP 20020201 */ + /* Determine the h/w rev level of the card. This seems like + a particularly kludgy way to encode it, but its what RME + chose to do, so we follow them ... + */ + + status = readl(s->iobase + RME96xx_status_register); + if (rme96xx_decode_spdif_rate(status&RME96xx_F) == 1) { + s->hw_rev = 15; + } else { + s->hw_rev = 11; + } + + /* Differentiate between the standard Hammerfall, and the + "Light", which does not have the expansion board. This + method comes from information received from Mathhias + Clausen at RME. Display the EEPROM and h/w revID where + relevant. + */ + + pci_read_config_word(s->pcidev, PCI_CLASS_REVISION, &rev); + switch (rev & 0xff) { + case 8: /* original eprom */ + if (s->hw_rev == 15) { + s->card_name = "RME Digi9636 (Rev 1.5)"; + } else { + s->card_name = "RME Digi9636"; + } + break; + case 9: /* W36_G EPROM */ + s->card_name = "RME Digi9636 (Rev G)"; + break; + case 4: /* W52_G EPROM */ + s->card_name = "RME Digi9652 (Rev G)"; + break; + default: + case 3: /* original eprom */ + if (s->hw_rev == 15) { + s->card_name = "RME Digi9652 (Rev 1.5)"; + } else { + s->card_name = "RME Digi9652"; + } + break; + } + + printk(KERN_INFO RME_MESS" detected %s (hw_rev %d)\n",s->card_name,s->hw_rev); + + if (s->hw_rev == 15) + rme96xx_initialize_spdif_receiver (s); + + s->started = 0; + rme96xx_setlatency(s,7); + + printk(KERN_INFO RME_MESS" card %d initialized\n",numcards); + return 0; +} + + +/* open uses this to figure out which device was opened .. this seems to be + unnecessary complex */ + +static LIST_HEAD(devs); + +static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + int i; + rme96xx_info *s; + + DBG(printk("%s\n", __FUNCTION__)); + + if (pcidev->irq == 0) + return -1; + if (!pci_dma_supported(pcidev, 0xffffffff)) { + printk(KERN_WARNING RME_MESS" architecture does not support 32bit PCI busmaster DMA\n"); + return -1; + } + if (!(s = kmalloc(sizeof(rme96xx_info), GFP_KERNEL))) { + printk(KERN_WARNING RME_MESS" out of memory\n"); + return -1; + } + memset(s, 0, sizeof(rme96xx_info)); + + s->pcidev = pcidev; + s->iobase = ioremap(pci_resource_start(pcidev, 0),RME96xx_IO_EXTENT); + s->irq = pcidev->irq; + + DBG(printk("remapped iobase: %lx irq %d\n",(long)s->iobase,s->irq)); + + if (pci_enable_device(pcidev)) + goto err_irq; + if (request_irq(s->irq, rme96xx_interrupt, IRQF_SHARED, "rme96xx", s)) { + printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); + goto err_irq; + } + + /* initialize the card */ + + i = 0; + if (rme96xx_init(s) < 0) { + printk(KERN_ERR RME_MESS" initialization failed\n"); + goto err_devices; + } + for (i=0;idspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0) + goto err_devices; + } + + if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0) + goto err_devices; + + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; /* ????? */ + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + + DBG(printk("initialization successful\n")); + return 0; + + /* error handler */ + err_devices: + while (i--) + unregister_sound_dsp(s->dspnum[i]); + free_irq(s->irq,s); + err_irq: + kfree(s); + return -1; +} + + +static void __devexit rme96xx_remove(struct pci_dev *dev) +{ + int i; + rme96xx_info *s = pci_get_drvdata(dev); + + if (!s) { + printk(KERN_ERR"device structure not valid\n"); + return ; + } + + if (s->started) rme96xx_startcard(s,0); + + i = devices; + while (i) { + i--; + unregister_sound_dsp(s->dspnum[i]); + } + + unregister_sound_mixer(s->mixer); + synchronize_irq(s->irq); + free_irq(s->irq,s); + busmaster_free(s->recbuf,RME96xx_DMA_MAX_SIZE_ALL); + busmaster_free(s->playbuf,RME96xx_DMA_MAX_SIZE_ALL); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + +#ifndef PCI_VENDOR_ID_RME +#define PCI_VENDOR_ID_RME 0x10ee +#endif +#ifndef PCI_DEVICE_ID_RME9652 +#define PCI_DEVICE_ID_RME9652 0x3fc4 +#endif +#ifndef PCI_ANY_ID +#define PCI_ANY_ID 0 +#endif + +static struct pci_device_id id_table[] = { + { + .vendor = PCI_VENDOR_ID_RME, + .device = PCI_DEVICE_ID_RME9652, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver rme96xx_driver = { + .name = "rme96xx", + .id_table = id_table, + .probe = rme96xx_probe, + .remove = __devexit_p(rme96xx_remove), +}; + +static int __init init_rme96xx(void) +{ + printk(KERN_INFO RME_MESS" version "RMEVERSION" time " __TIME__ " " __DATE__ "\n"); + devices = ((devices-1) & RME96xx_MASK_DEVS) + 1; + printk(KERN_INFO RME_MESS" reserving %d dsp device(s)\n",devices); + numcards = 0; + return pci_register_driver(&rme96xx_driver); +} + +static void __exit cleanup_rme96xx(void) +{ + printk(KERN_INFO RME_MESS" unloading\n"); + pci_unregister_driver(&rme96xx_driver); +} + +module_init(init_rme96xx); +module_exit(cleanup_rme96xx); + + + + + +/*-------------------------------------------------------------------------- + Implementation of file operations +---------------------------------------------------------------------------*/ + +#define RME96xx_FMT (AFMT_S16_LE|AFMT_U8|AFMT_S32_BLOCKED) +/* AFTM_U8 is not (yet?) supported ... HP20020201 */ + +static int rme96xx_ioctl(struct inode *in, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dmabuf * dma = (struct dmabuf *)file->private_data; + rme96xx_info *s = dma->s; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val = 0; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + + DBG(printk("ioctl %ud\n",cmd)); + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: +#if 0 + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); +#endif + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: +// rme96xx_clearbufs(dma); + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { +/* generally it's not a problem if we change the speed + if (dma->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; +*/ + spin_lock_irqsave(&s->lock, flags); + + switch (val) { + case 44100: + case 88200: + rme96xx_unset_ctrl(s,RME96xx_freq); + break; + case 48000: + case 96000: + rme96xx_set_ctrl(s,RME96xx_freq); + break; + /* just report current rate as default + e.g. use 0 to "select" current digital input rate + default: + rme96xx_unset_ctrl(s,RME96xx_freq); + val = 44100; + */ + } + if (val > 50000) + rme96xx_set_ctrl(s,RME96xx_DS); + else + rme96xx_unset_ctrl(s,RME96xx_DS); + /* set val to actual value HP 20020201 */ + /* NOTE: if not "Sync Master", reported rate might be not yet "updated" ... but I don't want to insert a long udelay() here */ + if ((s->control_register & RME96xx_Master) && !(s->control_register & RME96xx_wsel)) + val = rme96xx_get_sample_rate_ctrl(s); + else + val = rme96xx_get_sample_rate_status(s); + s->rate = val; + spin_unlock_irqrestore(&s->lock, flags); + } + DBG(printk("speed set to %d\n",val)); + return put_user(val, p); + + case SNDCTL_DSP_STEREO: /* this plays a mono file on two channels */ + if (get_user(val, p)) + return -EFAULT; + + if (!val) { + DBG(printk("setting to mono\n")); + dma->mono=1; + dma->inchannels = 1; + dma->outchannels = 1; + } + else { + DBG(printk("setting to stereo\n")); + dma->mono = 0; + dma->inchannels = 2; + dma->outchannels = 2; + } + return 0; + case SNDCTL_DSP_CHANNELS: + /* remember to check for resonable offset/channel pairs here */ + if (get_user(val, p)) + return -EFAULT; + + if (file->f_mode & FMODE_WRITE) { + if (val > 0 && (dma->outoffset + val) <= RME96xx_CHANNELS_PER_CARD) + dma->outchannels = val; + else + dma->outchannels = val = 2; + DBG(printk("setting to outchannels %d\n",val)); + } + if (file->f_mode & FMODE_READ) { + if (val > 0 && (dma->inoffset + val) <= RME96xx_CHANNELS_PER_CARD) + dma->inchannels = val; + else + dma->inchannels = val = 2; + DBG(printk("setting to inchannels %d\n",val)); + } + + dma->mono=0; + + return put_user(val, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(RME96xx_FMT, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + DBG(printk("setting to format %x\n",val)); + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (val & RME96xx_FMT) + dma->format = val; + switch (dma->format) { + case AFMT_S16_LE: + dma->formatshift=1; + break; + case AFMT_S32_BLOCKED: + dma->formatshift=0; + break; + } + } + return put_user(dma->format, p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; +#if 0 + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; +#endif + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; +#if 0 + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } +#endif + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + + + count = rme96xx_getospace(dma,val); + if (!s->started) count = s->fragsize*2; + abinfo.fragsize =(s->fragsize*dma->outchannels)>>dma->formatshift; + abinfo.bytes = (count*dma->outchannels)>>dma->formatshift; + abinfo.fragstotal = 2; + abinfo.fragments = (count > s->fragsize); + + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + + count = rme96xx_getispace(dma,val); + + abinfo.fragsize = (s->fragsize*dma->inchannels)>>dma->formatshift; + abinfo.bytes = (count*dma->inchannels)>>dma->formatshift; + abinfo.fragstotal = 2; + abinfo.fragments = count > s->fragsize; + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: /* What should this exactly do ? , + ATM it is just abinfo.bytes */ + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + val = rme96xx_gethwptr(dma->s,0); + count = val - dma->readptr; + if (count < 0) + count += s->fragsize<<1; + + return put_user(count, p); + + +/* check out how to use mmaped mode (can only be blocked !!!) */ + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + val = rme96xx_gethwptr(dma->s,0); + spin_lock_irqsave(&s->lock,flags); + cinfo.bytes = s->fragsize<<1; + count = val - dma->readptr; + if (count < 0) + count += s->fragsize<<1; + + cinfo.blocks = (count > s->fragsize); + cinfo.ptr = val; + if (dma->mmapped) + dma->readptr &= s->fragsize<<1; + spin_unlock_irqrestore(&s->lock,flags); + + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + val = rme96xx_gethwptr(dma->s,0); + spin_lock_irqsave(&s->lock,flags); + cinfo.bytes = s->fragsize<<1; + count = val - dma->writeptr; + if (count < 0) + count += s->fragsize<<1; + + cinfo.blocks = (count > s->fragsize); + cinfo.ptr = val; + if (dma->mmapped) + dma->writeptr &= s->fragsize<<1; + spin_unlock_irqrestore(&s->lock,flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + case SNDCTL_DSP_GETBLKSIZE: + return put_user(s->fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + val&=0xffff; + val -= 7; + if (val < 0) val = 0; + if (val > 7) val = 7; + rme96xx_setlatency(s,val); + return 0; + + case SNDCTL_DSP_SUBDIVIDE: +#if 0 + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; +#endif + return 0; + + case SOUND_PCM_READ_RATE: + /* HP20020201 */ + s->rate = rme96xx_get_sample_rate_status(s); + return put_user(s->rate, p); + + case SOUND_PCM_READ_CHANNELS: + return put_user(dma->outchannels, p); + + case SOUND_PCM_READ_BITS: + switch (dma->format) { + case AFMT_S32_BLOCKED: + val = 32; + break; + case AFMT_S16_LE: + val = 16; + break; + } + return put_user(val, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + + + return -ENODEV; +} + + + +static int rme96xx_open(struct inode *in, struct file *f) +{ + int minor = iminor(in); + struct list_head *list; + int devnum; + rme96xx_info *s; + struct dmabuf* dma; + DECLARE_WAITQUEUE(wait, current); + + DBG(printk("device num %d open\n",devnum)); + + nonseekable_open(in, f); + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, rme96xx_info, devs); + for (devnum=0; devnumdspnum[devnum] ^ minor) & ~0xf)) + break; + if (devnumdma[devnum]; + f->private_data = dma; + /* wait for device to become free */ + mutex_lock(&dma->open_mutex); + while (dma->open_mode & f->f_mode) { + if (f->f_flags & O_NONBLOCK) { + mutex_unlock(&dma->open_mutex); + return -EBUSY; + } + add_wait_queue(&dma->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&dma->open_mutex); + schedule(); + remove_wait_queue(&dma->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&dma->open_mutex); + } + + COMM ("hardware open") + + if (!dma->opened) rme96xx_dmabuf_init(dma->s,dma,dma->inoffset,dma->outoffset); + + dma->open_mode |= (f->f_mode & (FMODE_READ | FMODE_WRITE)); + dma->opened = 1; + mutex_unlock(&dma->open_mutex); + + DBG(printk("device num %d open finished\n",devnum)); + return 0; +} + +static int rme96xx_release(struct inode *in, struct file *file) +{ + struct dmabuf * dma = (struct dmabuf*) file->private_data; + /* int hwp; ... was unused HP20020201 */ + DBG(printk("%s\n", __FUNCTION__)); + + COMM ("draining") + if (dma->open_mode & FMODE_WRITE) { +#if 0 /* Why doesn't this work with some cards ?? */ + hwp = rme96xx_gethwptr(dma->s,0); + while (rme96xx_getospace(dma,hwp)) { + interruptible_sleep_on(&(dma->wait)); + hwp = rme96xx_gethwptr(dma->s,0); + } +#endif + rme96xx_clearbufs(dma); + } + + dma->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + if (!(dma->open_mode & (FMODE_READ|FMODE_WRITE))) { + dma->opened = 0; + if (dma->s->started) rme96xx_startcard(dma->s,1); + } + + wake_up(&dma->open_wait); + mutex_unlock(&dma->open_mutex); + + return 0; +} + + +static ssize_t rme96xx_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + ssize_t ret = 0; + int cnt; /* number of bytes from "buffer" that will/can be used */ + int hop = count/dma->outchannels; + int hwp; + int exact = (file->f_flags & O_NONBLOCK); + + + if(dma == NULL || (dma->s) == NULL) + return -ENXIO; + + if (dma->mmapped || !dma->opened) + return -ENXIO; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + if (! (dma->open_mode & FMODE_WRITE)) + return -ENXIO; + + if (!dma->s->started) rme96xx_startcard(dma->s,exact); + hwp = rme96xx_gethwptr(dma->s,0); + + if(!(dma->started)){ + COMM ("first write") + + dma->readptr = hwp; + dma->writeptr = hwp; + dma->started = 1; + } + + while (count > 0) { + cnt = rme96xx_getospace(dma,hwp); + cnt>>=dma->formatshift; + cnt*=dma->outchannels; + if (cnt > count) + cnt = count; + + if (cnt != 0) { + if (rme96xx_copyfromuser(dma,buffer,cnt,hop)) + return ret ? ret : -EFAULT; + count -= cnt; + buffer += cnt; + ret += cnt; + if (count == 0) return ret; + } + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + + if ((hwp - dma->writeptr) <= 0) { + interruptible_sleep_on(&(dma->wait)); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + } + + hwp = rme96xx_gethwptr(dma->s,exact); + + }; /* count > 0 */ + + return ret; +} + +static ssize_t rme96xx_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + ssize_t ret = 0; + int cnt; /* number of bytes from "buffer" that will/can be used */ + int hop = count/dma->inchannels; + int hwp; + int exact = (file->f_flags & O_NONBLOCK); + + + if(dma == NULL || (dma->s) == NULL) + return -ENXIO; + + if (dma->mmapped || !dma->opened) + return -ENXIO; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + if (! (dma->open_mode & FMODE_READ)) + return -ENXIO; + + if (!dma->s->started) rme96xx_startcard(dma->s,exact); + hwp = rme96xx_gethwptr(dma->s,0); + + if(!(dma->started)){ + COMM ("first read") + + dma->writeptr = hwp; + dma->readptr = hwp; + dma->started = 1; + } + + while (count > 0) { + cnt = rme96xx_getispace(dma,hwp); + cnt>>=dma->formatshift; + cnt*=dma->inchannels; + + if (cnt > count) + cnt = count; + + if (cnt != 0) { + + if (rme96xx_copytouser(dma,buffer,cnt,hop)) + return ret ? ret : -EFAULT; + + count -= cnt; + buffer += cnt; + ret += cnt; + if (count == 0) return ret; + } + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + + if ((hwp - dma->readptr) <= 0) { + interruptible_sleep_on(&(dma->wait)); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + } + hwp = rme96xx_gethwptr(dma->s,exact); + + }; /* count > 0 */ + + return ret; +} + +static int rm96xx_mmap(struct file *file, struct vm_area_struct *vma) { + struct dmabuf *dma = (struct dmabuf *)file->private_data; + rme96xx_info* s = dma->s; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > RME96xx_DMA_MAX_SIZE) { + unlock_kernel(); + return -EINVAL; + } + + + if (vma->vm_flags & VM_WRITE) { + if (!s->started) rme96xx_startcard(s,1); + + if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->outoffset*RME96xx_DMA_MAX_SIZE) >> PAGE_SHIFT, size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + } + else if (vma->vm_flags & VM_READ) { + if (!s->started) rme96xx_startcard(s,1); + if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(s->playbuf + dma->inoffset*RME96xx_DMA_MAX_SIZE) >> PAGE_SHIFT, size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + } else { + unlock_kernel(); + return -EINVAL; + } + + +/* this is the mapping */ + vma->vm_flags &= ~VM_IO; + dma->mmapped = 1; + unlock_kernel(); + return 0; +} + +static unsigned int rme96xx_poll(struct file *file, struct poll_table_struct *wait) +{ + struct dmabuf *dma = (struct dmabuf *)file->private_data; + rme96xx_info* s = dma->s; + unsigned int mask = 0; + unsigned int hwp,cnt; + + DBG(printk("rme96xx poll_wait ...\n")); + VALIDATE_STATE(s); + + if (!s->started) { + mask |= POLLOUT | POLLWRNORM; + } + poll_wait(file, &dma->wait, wait); + + hwp = rme96xx_gethwptr(dma->s,0); + + DBG(printk("rme96xx poll: ..cnt %d > %d\n",cnt,s->fragsize)); + + cnt = rme96xx_getispace(dma,hwp); + + if (file->f_mode & FMODE_READ) + if (cnt > 0) + mask |= POLLIN | POLLRDNORM; + + + + cnt = rme96xx_getospace(dma,hwp); + + if (file->f_mode & FMODE_WRITE) + if (cnt > 0) + mask |= POLLOUT | POLLWRNORM; + + +// printk("rme96xx poll_wait ...%d > %d\n",rme96xx_getospace(dma,hwp),rme96xx_getispace(dma,hwp)); + + return mask; +} + + +static struct file_operations rme96xx_audio_fops = { + .owner = THIS_MODULE, + .read = rme96xx_read, + .write = rme96xx_write, + .poll = rme96xx_poll, + .ioctl = rme96xx_ioctl, + .mmap = rm96xx_mmap, + .open = rme96xx_open, + .release = rme96xx_release +}; + +static int rme96xx_mixer_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct list_head *list; + rme96xx_info *s; + + COMM ("mixer open"); + + nonseekable_open(inode, file); + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, rme96xx_info, devs); + if (s->mixer== minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + + COMM ("mixer opened") + return 0; +} + +static int rme96xx_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + rme96xx_info *s = (rme96xx_info *)file->private_data; + u32 status; + int spdifrate; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + status = readl(s->iobase + RME96xx_status_register); + /* hack to convert rev 1.5 SPDIF rate to "crystalrate" format HP 20020201 */ + rme96xx_spdif_sample_rate(s,&spdifrate); + status = (status & ~RME96xx_F) | ((spdifrate<<22) & RME96xx_F); + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + rme_mixer mixer; + if (copy_from_user(&mixer,argp,sizeof(mixer))) + return -EFAULT; + + mixer.devnr &= RME96xx_MASK_DEVS; + if (mixer.devnr >= devices) + mixer.devnr = devices-1; + if (file->f_mode & FMODE_WRITE && !s->dma[mixer.devnr].opened) { + /* modify only if device not open */ + if (mixer.o_offset < 0) + mixer.o_offset = 0; + if (mixer.o_offset >= RME96xx_CHANNELS_PER_CARD) + mixer.o_offset = RME96xx_CHANNELS_PER_CARD-1; + if (mixer.i_offset < 0) + mixer.i_offset = 0; + if (mixer.i_offset >= RME96xx_CHANNELS_PER_CARD) + mixer.i_offset = RME96xx_CHANNELS_PER_CARD-1; + s->dma[mixer.devnr].outoffset = mixer.o_offset; + s->dma[mixer.devnr].inoffset = mixer.i_offset; + } + + mixer.o_offset = s->dma[mixer.devnr].outoffset; + mixer.i_offset = s->dma[mixer.devnr].inoffset; + + return copy_to_user(argp, &mixer, sizeof(mixer)) ? -EFAULT : 0; + } + if (cmd == SOUND_MIXER_PRIVATE2) { + return put_user(status, p); + } + if (cmd == SOUND_MIXER_PRIVATE3) { + u32 control; + if (copy_from_user(&control,argp,sizeof(control))) + return -EFAULT; + if (file->f_mode & FMODE_WRITE) { + s->control_register &= ~RME96xx_mixer_allowed; + s->control_register |= control & RME96xx_mixer_allowed; + writel(control,s->iobase + RME96xx_control_register); + } + + return put_user(s->control_register, p); + } + return -1; +} + + + +static int rme96xx_mixer_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static /*const*/ struct file_operations rme96xx_mixer_fops = { + .owner = THIS_MODULE, + .ioctl = rme96xx_mixer_ioctl, + .open = rme96xx_mixer_open, + .release = rme96xx_mixer_release, +}; diff --git a/trunk/sound/oss/rme96xx.h b/trunk/sound/oss/rme96xx.h new file mode 100644 index 000000000000..7a3c188ea0a8 --- /dev/null +++ b/trunk/sound/oss/rme96xx.h @@ -0,0 +1,78 @@ +/* (C) 2000 Guenter Geiger + with copy/pastes from the driver of Winfried Ritsch + +Modifications - Heiko Purnhagen + HP20020116 towards REV 1.5 support, based on ALSA's card-rme9652.c + HP20020201 completed? + +A text/graphic control panel (rmectrl/xrmectrl) is available from + http://gige.xdv.org/pages/soft/pages/rme +*/ + + +#ifndef AFMT_S32_BLOCKED +#define AFMT_S32_BLOCKED 0x0000400 +#endif + +/* AFMT_S16_BLOCKED not yet supported */ +#ifndef AFMT_S16_BLOCKED +#define AFMT_S16_BLOCKED 0x0000800 +#endif + + +typedef struct rme_status { + unsigned int irq:1; + unsigned int lockmask:3; /* ADAT input PLLs locked */ + /* 100=ADAT1, 010=ADAT2, 001=ADAT3 */ + unsigned int sr48:1; /* sample rate: 0=44.1/88.2 1=48/96 kHz */ + unsigned int wclock:1; /* 1=wordclock used */ + unsigned int bufpoint:10; + unsigned int syncmask:3; /* ADAT input in sync with system clock */ + /* 100=ADAT1, 010=ADAT2, 001=ADAT3 */ + unsigned int doublespeed:1; /* sample rate: 0=44.1/48 1=88.2/96 kHz */ + unsigned int tc_busy:1; + unsigned int tc_out:1; + unsigned int crystalrate:3; /* spdif input sample rate: */ + /* 000=64kHz, 100=88.2kHz, 011=96kHz */ + /* 111=32kHz, 110=44.1kHz, 101=48kHz */ + unsigned int spdif_error:1; /* 1=no spdif lock */ + unsigned int bufid:1; + unsigned int tc_valid:1; /* 1=timecode input detected */ + unsigned int spdif_read:1; +} rme_status_t; + + +/* only fields marked W: can be modified by writing to SOUND_MIXER_PRIVATE3 */ +typedef struct rme_control { + unsigned int start:1; + unsigned int latency:3; /* buffer size / latency [samples]: */ + /* 0=64 ... 7=8192 */ + unsigned int master:1; /* W: clock mode: 1=master 0=slave/auto */ + unsigned int ie:1; + unsigned int sr48:1; /* samplerate 0=44.1/88.2, 1=48/96 kHz */ + unsigned int spare:1; + unsigned int doublespeed:1; /* double speed 0=44.1/48, 1=88.2/96 Khz */ + unsigned int pro:1; /* W: SPDIF-OUT 0=consumer, 1=professional */ + unsigned int emphasis:1; /* W: SPDIF-OUT emphasis 0=off, 1=on */ + unsigned int dolby:1; /* W: SPDIF-OUT non-audio bit 1=set, 0=unset */ + unsigned int opt_out:1; /* W: use 1st optical OUT as SPDIF: 1=yes, 0=no */ + unsigned int wordclock:1; /* W: use Wordclock as sync (overwrites master) */ + unsigned int spdif_in:2; /* W: SPDIF-IN: */ + /* 00=optical (ADAT1), 01=coaxial (Cinch), 10=internal CDROM */ + unsigned int sync_ref:2; /* W: preferred sync-source in autosync */ + /* 00=ADAT1, 01=ADAT2, 10=ADAT3, 11=SPDIF */ + unsigned int spdif_reset:1; + unsigned int spdif_select:1; + unsigned int spdif_clock:1; + unsigned int spdif_write:1; + unsigned int adat1_cd:1; /* W: Rev 1.5+: if set, internal CD connector carries ADAT */ +} rme_ctrl_t; + + +typedef struct _rme_mixer { + int i_offset; + int o_offset; + int devnr; + int spare[8]; +} rme_mixer; + diff --git a/trunk/sound/oss/sequencer.c b/trunk/sound/oss/sequencer.c index 5c215f787ca9..0ce4e4ef6fe9 100644 --- a/trunk/sound/oss/sequencer.c +++ b/trunk/sound/oss/sequencer.c @@ -16,6 +16,7 @@ */ #include #include +#define SEQUENCER_C #include "sound_config.h" #include "midi_ctrl.h" @@ -156,7 +157,6 @@ void seq_copy_to_input(unsigned char *event_rec, int len) wake_up(&midi_sleeper); spin_unlock_irqrestore(&lock,flags); } -EXPORT_SYMBOL(seq_copy_to_input); static void sequencer_midi_input(int dev, unsigned char data) { @@ -206,7 +206,6 @@ void seq_input_event(unsigned char *event_rec, int len) } seq_copy_to_input(event_rec, len); } -EXPORT_SYMBOL(seq_input_event); int sequencer_write(int dev, struct file *file, const char __user *buf, int count) { @@ -1555,7 +1554,6 @@ void sequencer_timer(unsigned long dummy) { seq_startplay(); } -EXPORT_SYMBOL(sequencer_timer); int note_to_freq(int note_num) { @@ -1589,7 +1587,6 @@ int note_to_freq(int note_num) return note_freq; } -EXPORT_SYMBOL(note_to_freq); unsigned long compute_finetune(unsigned long base_freq, int bend, int range, int vibrato_cents) @@ -1643,12 +1640,19 @@ unsigned long compute_finetune(unsigned long base_freq, int bend, int range, else return (base_freq * amount) / 10000; /* Bend up */ } -EXPORT_SYMBOL(compute_finetune); + void sequencer_init(void) { + /* drag in sequencer_syms.o */ + { + extern char sequencer_syms_symbol; + sequencer_syms_symbol = 0; + } + if (sequencer_ok) return; + MIDIbuf_init(); queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ); if (queue == NULL) { @@ -1664,7 +1668,6 @@ void sequencer_init(void) } sequencer_ok = 1; } -EXPORT_SYMBOL(sequencer_init); void sequencer_unload(void) { diff --git a/trunk/sound/oss/sequencer_syms.c b/trunk/sound/oss/sequencer_syms.c new file mode 100644 index 000000000000..5d008798c310 --- /dev/null +++ b/trunk/sound/oss/sequencer_syms.c @@ -0,0 +1,29 @@ +/* + * Exported symbols for sequencer driver. + */ + +#include + +char sequencer_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +EXPORT_SYMBOL(note_to_freq); +EXPORT_SYMBOL(compute_finetune); +EXPORT_SYMBOL(seq_copy_to_input); +EXPORT_SYMBOL(seq_input_event); +EXPORT_SYMBOL(sequencer_init); +EXPORT_SYMBOL(sequencer_timer); + +EXPORT_SYMBOL(sound_timer_init); +EXPORT_SYMBOL(sound_timer_interrupt); +EXPORT_SYMBOL(sound_timer_syncinterval); + +/* Tuning */ + +#define _SEQUENCER_C_ +#include "tuning.h" + +EXPORT_SYMBOL(cent_tuning); +EXPORT_SYMBOL(semitone_tuning); diff --git a/trunk/sound/oss/sgalaxy.c b/trunk/sound/oss/sgalaxy.c new file mode 100644 index 000000000000..0bcff6735319 --- /dev/null +++ b/trunk/sound/oss/sgalaxy.c @@ -0,0 +1,207 @@ +/* + * sound/oss/sgalaxy.c + * + * Low level driver for Aztech Sound Galaxy cards. + * Copyright 1998 Artur Skawina + * + * Supported cards: + * Aztech Sound Galaxy Waverider Pro 32 - 3D + * Aztech Sound Galaxy Washington 16 + * + * Based on cs4232.c by Hannu Savolainen and Alan Cox. + * + * + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added __init to sb_rst() and sb_cmd() + */ + +#include +#include + +#include "sound_config.h" +#include "ad1848.h" + +static void sleep( unsigned howlong ) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(howlong); +} + +#define DPORT 0x80 + +/* Sound Blaster regs */ + +#define SBDSP_RESET 0x6 +#define SBDSP_READ 0xA +#define SBDSP_COMMAND 0xC +#define SBDSP_STATUS SBDSP_COMMAND +#define SBDSP_DATA_AVAIL 0xE + +static int __init sb_rst(int base) +{ + int i; + + outb( 1, base+SBDSP_RESET ); /* reset the DSP */ + outb( 0, base+SBDSP_RESET ); + + for ( i=0; i<500; i++ ) /* delay */ + inb(DPORT); + + for ( i=0; i<100000; i++ ) + { + if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) + break; + } + + if ( inb( base+SBDSP_READ )!=0xAA ) + return 0; + + return 1; +} + +static int __init sb_cmd( int base, unsigned char val ) +{ + int i; + + for ( i=100000; i; i-- ) + { + if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) + { + outb( val, base+SBDSP_COMMAND ); + break; + } + } + return i; /* i>0 == success */ +} + + +#define ai_sgbase driver_use_1 + +static int __init probe_sgalaxy( struct address_info *ai ) +{ + struct resource *ports; + int n; + + if (!request_region(ai->io_base, 4, "WSS config")) { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + return 0; + } + + ports = request_region(ai->io_base + 4, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + release_region(ai->io_base, 4); + return 0; + } + + if (!request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB")) { + printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); + release_region(ai->io_base + 4, 4); + release_region(ai->io_base, 4); + return 0; + } + + if (ad1848_detect(ports, NULL, ai->osp)) + goto out; /* The card is already active, check irq etc... */ + + /* switch to MSS/WSS mode */ + + sb_rst( ai->ai_sgbase ); + + sb_cmd( ai->ai_sgbase, 9 ); + sb_cmd( ai->ai_sgbase, 0 ); + + sleep( HZ/10 ); + +out: + if (!probe_ms_sound(ai, ports)) { + release_region(ai->io_base + 4, 4); + release_region(ai->io_base, 4); + release_region(ai->ai_sgbase, 0x10); + return 0; + } + + attach_ms_sound(ai, ports, THIS_MODULE); + n=ai->slots[0]; + + if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) { + AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ + AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ + AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ + } + return 1; +} + +static void __exit unload_sgalaxy( struct address_info *ai ) +{ + unload_ms_sound( ai ); + release_region( ai->ai_sgbase, 0x10 ); +} + +static struct address_info cfg; + +static int __initdata io = -1; +static int __initdata irq = -1; +static int __initdata dma = -1; +static int __initdata dma2 = -1; +static int __initdata sgbase = -1; + +module_param(io, int, 0); +module_param(irq, int, 0); +module_param(dma, int, 0); +module_param(dma2, int, 0); +module_param(sgbase, int, 0); + +static int __init init_sgalaxy(void) +{ + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + cfg.ai_sgbase = sgbase; + + if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.ai_sgbase == -1 ) { + printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); + return -EINVAL; + } + + if ( probe_sgalaxy(&cfg) == 0 ) + return -ENODEV; + + return 0; +} + +static void __exit cleanup_sgalaxy(void) +{ + unload_sgalaxy(&cfg); +} + +module_init(init_sgalaxy); +module_exit(cleanup_sgalaxy); + +#ifndef MODULE +static int __init setup_sgalaxy(char *str) +{ + /* io, irq, dma, dma2, sgbase */ + int ints[6]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + io = ints[1]; + irq = ints[2]; + dma = ints[3]; + dma2 = ints[4]; + sgbase = ints[5]; + + return 1; +} + +__setup("sgalaxy=", setup_sgalaxy); +#endif +MODULE_LICENSE("GPL"); diff --git a/trunk/sound/oss/sonicvibes.c b/trunk/sound/oss/sonicvibes.c new file mode 100644 index 000000000000..8ea532d40198 --- /dev/null +++ b/trunk/sound/oss/sonicvibes.c @@ -0,0 +1,2792 @@ +/*****************************************************************************/ + +/* + * sonicvibes.c -- S3 Sonic Vibes audio driver. + * + * Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * none so far + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.1998 0.1 Initial release + * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in sv_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.1998 0.4 Don't allow excessive interrupt rates + * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice + * 03.08.1998 0.6 Do not include modversions.h + * Now mixer behaviour can basically be selected between + * "OSS documented" and "OSS actual" behaviour + * 31.08.1998 0.7 Fix realplayer problems - dac.count issues + * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA + * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs + * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. + * hopefully killed the egcs section type conflict + * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 05.04.1999 0.13 added code to sv_read and sv_write which should detect + * lockups of the sound chip and revive it. This is basically + * an ugly hack, but at least applications using this driver + * won't hang forever. I don't know why these lockups happen, + * it might well be the motherboard chipset (an early 486 PCI + * board with ALI chipset), since every busmastering 100MB + * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) + * exhibit similar behaviour (they work for a couple of packets + * and then lock up and can be revived by ifconfig down/up). + * 07.04.1999 0.14 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * Note: dmaio hack might still be wrong on archs other than i386 + * 15.06.1999 0.15 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.1999 0.16 Add pci_set_master + * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall + * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.1999 0.18 module_init/__setup fixes + * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource + * 31.08.1999 0.20 add spin_lock_init + * use new resource allocation to allocate DDMA IO space + * replaced current->state = x with set_current_state(x) + * 03.09.1999 0.21 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 28.10.1999 0.22 More waitqueue races fixed + * 01.12.1999 0.23 New argument to allocate_resource + * 07.12.1999 0.24 More allocate_resource semantics change + * 08.01.2000 0.25 Prevent some ioctl's from returning bad count values on underrun/overrun; + * Tim Janik's BSE (Bedevilled Sound Engine) found this + * use Martin Mares' pci_assign_resource + * 07.02.2000 0.26 Use pci_alloc_consistent and pci_register_driver + * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask + * 12.12.2000 0.28 More dma buffer initializations, patch from + * Tjeerd Mulder + * 31.01.2001 0.29 Register/Unregister gameport + * Fix SETTRIGGER non OSS API conformity + * 18.05.2001 0.30 PCI probing and error values cleaned up by Marcus + * Meissner + * 03.01.2003 0.31 open_mode fixes from Georg Acher + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include "dm.h" + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) + +#define SV_EXTENT_SB 0x10 +#define SV_EXTENT_ENH 0x10 +#define SV_EXTENT_SYNTH 0x4 +#define SV_EXTENT_MIDI 0x4 +#define SV_EXTENT_GAME 0x8 +#define SV_EXTENT_DMA 0x10 + +/* + * we are not a bridge and thus use a resource for DDMA that is used for bridges but + * left empty for normal devices + */ +#define RESOURCE_SB 0 +#define RESOURCE_ENH 1 +#define RESOURCE_SYNTH 2 +#define RESOURCE_MIDI 3 +#define RESOURCE_GAME 4 +#define RESOURCE_DDMA 7 + +#define SV_MIDI_DATA 0 +#define SV_MIDI_COMMAND 1 +#define SV_MIDI_STATUS 1 + +#define SV_DMA_ADDR0 0 +#define SV_DMA_ADDR1 1 +#define SV_DMA_ADDR2 2 +#define SV_DMA_ADDR3 3 +#define SV_DMA_COUNT0 4 +#define SV_DMA_COUNT1 5 +#define SV_DMA_COUNT2 6 +#define SV_DMA_MODE 0xb +#define SV_DMA_RESET 0xd +#define SV_DMA_MASK 0xf + +/* + * DONT reset the DMA controllers unless you understand + * the reset semantics. Assuming reset semantics as in + * the 8237 does not work. + */ + +#define DMA_MODE_AUTOINIT 0x10 +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ + +#define SV_CODEC_CONTROL 0 +#define SV_CODEC_INTMASK 1 +#define SV_CODEC_STATUS 2 +#define SV_CODEC_IADDR 4 +#define SV_CODEC_IDATA 5 + +#define SV_CCTRL_RESET 0x80 +#define SV_CCTRL_INTADRIVE 0x20 +#define SV_CCTRL_WAVETABLE 0x08 +#define SV_CCTRL_REVERB 0x04 +#define SV_CCTRL_ENHANCED 0x01 + +#define SV_CINTMASK_DMAA 0x01 +#define SV_CINTMASK_DMAC 0x04 +#define SV_CINTMASK_SPECIAL 0x08 +#define SV_CINTMASK_UPDOWN 0x40 +#define SV_CINTMASK_MIDI 0x80 + +#define SV_CSTAT_DMAA 0x01 +#define SV_CSTAT_DMAC 0x04 +#define SV_CSTAT_SPECIAL 0x08 +#define SV_CSTAT_UPDOWN 0x40 +#define SV_CSTAT_MIDI 0x80 + +#define SV_CIADDR_TRD 0x80 +#define SV_CIADDR_MCE 0x40 + +/* codec indirect registers */ +#define SV_CIMIX_ADCINL 0x00 +#define SV_CIMIX_ADCINR 0x01 +#define SV_CIMIX_AUX1INL 0x02 +#define SV_CIMIX_AUX1INR 0x03 +#define SV_CIMIX_CDINL 0x04 +#define SV_CIMIX_CDINR 0x05 +#define SV_CIMIX_LINEINL 0x06 +#define SV_CIMIX_LINEINR 0x07 +#define SV_CIMIX_MICIN 0x08 +#define SV_CIMIX_SYNTHINL 0x0A +#define SV_CIMIX_SYNTHINR 0x0B +#define SV_CIMIX_AUX2INL 0x0C +#define SV_CIMIX_AUX2INR 0x0D +#define SV_CIMIX_ANALOGINL 0x0E +#define SV_CIMIX_ANALOGINR 0x0F +#define SV_CIMIX_PCMINL 0x10 +#define SV_CIMIX_PCMINR 0x11 + +#define SV_CIGAMECONTROL 0x09 +#define SV_CIDATAFMT 0x12 +#define SV_CIENABLE 0x13 +#define SV_CIUPDOWN 0x14 +#define SV_CIREVISION 0x15 +#define SV_CIADCOUTPUT 0x16 +#define SV_CIDMAABASECOUNT1 0x18 +#define SV_CIDMAABASECOUNT0 0x19 +#define SV_CIDMACBASECOUNT1 0x1c +#define SV_CIDMACBASECOUNT0 0x1d +#define SV_CIPCMSR0 0x1e +#define SV_CIPCMSR1 0x1f +#define SV_CISYNTHSR0 0x20 +#define SV_CISYNTHSR1 0x21 +#define SV_CIADCCLKSOURCE 0x22 +#define SV_CIADCALTSR 0x23 +#define SV_CIADCPLLM 0x24 +#define SV_CIADCPLLN 0x25 +#define SV_CISYNTHPLLM 0x26 +#define SV_CISYNTHPLLN 0x27 +#define SV_CIUARTCONTROL 0x2a +#define SV_CIDRIVECONTROL 0x2b +#define SV_CISRSSPACE 0x2c +#define SV_CISRSCENTER 0x2d +#define SV_CIWAVETABLESRC 0x2e +#define SV_CIANALOGPWRDOWN 0x30 +#define SV_CIDIGITALPWRDOWN 0x31 + + +#define SV_CIMIX_ADCSRC_CD 0x20 +#define SV_CIMIX_ADCSRC_DAC 0x40 +#define SV_CIMIX_ADCSRC_AUX2 0x60 +#define SV_CIMIX_ADCSRC_LINE 0x80 +#define SV_CIMIX_ADCSRC_AUX1 0xa0 +#define SV_CIMIX_ADCSRC_MIC 0xc0 +#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 +#define SV_CIMIX_ADCSRC_MASK 0xe0 + +#define SV_CFMT_STEREO 0x01 +#define SV_CFMT_16BIT 0x02 +#define SV_CFMT_MASK 0x03 +#define SV_CFMT_ASHIFT 0 +#define SV_CFMT_CSHIFT 4 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define SV_CENABLE_PPE 0x4 +#define SV_CENABLE_RE 0x2 +#define SV_CENABLE_PE 0x1 + + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +/* --------------------------------------------------------------------- */ + +struct sv_state { + /* magic */ + unsigned int magic; + + /* list of sonicvibes devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iosb, ioenh, iosynth, iomidi; /* long for SPARC */ + unsigned int iodmaa, iodmac, irq; + + /* mixer stuff */ + struct { + unsigned int modcnt; +#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS + unsigned short vol[13]; +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } mix; + + /* wave stuff */ + unsigned int rateadc, ratedac; + unsigned char fmt, enable; + + spinlock_t lock; + struct mutex open_mutex; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + +#if SUPPORT_JOYSTICK + struct gameport *gameport; +#endif +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); +static unsigned long wavetable_mem; + +/* --------------------------------------------------------------------- */ + +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +/* + * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. + */ + +#undef DMABYTEIO + +static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa, u; + + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count--; + outl(addr, s->iodmaa + SV_DMA_ADDR0); + outl(count, s->iodmaa + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x18, s->iodmaa + SV_DMA_MODE); +} + +static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac, u; + + count >>= 1; + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count >>= 1; + count--; + outl(addr, s->iodmac + SV_DMA_ADDR0); + outl(count, s->iodmac + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x14, s->iodmac + SV_DMA_MODE); +} + +static inline unsigned get_dmaa(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return v + 1; +#else /* DMABYTEIO */ + return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; +#endif /* DMABYTEIO */ +} + +static inline unsigned get_dmac(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return (v + 1) << 1; +#else /* DMABYTEIO */ + return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +#endif /* DMABYTEIO */ +} + +static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +static unsigned char rdindir(struct sv_state *s, unsigned char idx) +{ + unsigned char v; + + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + v = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + return v; +} + +static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); + if (mask) { + s->fmt = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(0, s->ioenh + SV_CODEC_IADDR); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); +} + +static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +#define REFFREQUENCY 24576000 +#define ADCMULT 512 +#define FULLRATE 48000 + +static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) +{ + unsigned long flags; + unsigned char r, m=0, n=0; + unsigned xm, xn, xr, xd, metric = ~0U; + /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ + + if (rate < 625000/ADCMULT) + rate = 625000/ADCMULT; + if (rate > 150000000/ADCMULT) + rate = 150000000/ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 35; xn++) + for (xm = 3; xm < 130; xm++) { + xr = REFFREQUENCY/ADCMULT * xm / xn; + xd = abs((signed)(xr - rate)); + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(m, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(r | n, s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); +} + +#if 0 + +static unsigned getpll(struct sv_state *s, unsigned char reg) +{ + unsigned long flags; + unsigned char m, n; + + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + m = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + n = inb(s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); +} + +#endif + +static void set_dac_rate(struct sv_state *s, unsigned rate) +{ + unsigned div; + unsigned long flags; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + div = (rate * 65536 + FULLRATE/2) / FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIPCMSR1, div >> 8); + wrindir(s, SV_CIPCMSR0, div); + spin_unlock_irqrestore(&s->lock, flags); + s->ratedac = (div * FULLRATE + 32768) / 65536; +} + +static void set_adc_rate(struct sv_state *s, unsigned rate) +{ + unsigned long flags; + unsigned rate1, rate2, div; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + rate1 = setpll(s, SV_CIADCPLLM, rate); + div = (48000 + rate/2) / rate; + if (div > 8) + div = 8; + rate2 = (48000 + div/2) / div; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIADCALTSR, (div-1) << 4); + if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { + wrindir(s, SV_CIADCCLKSOURCE, 0x10); + s->rateadc = rate2; + } else { + wrindir(s, SV_CIADCCLKSOURCE, 0x00); + s->rateadc = rate1; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static inline void stop_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->enable |= SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +static void dealloc_dmabuf(struct sv_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + ClearPageReserved(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + +/* DMAA is used for playback, DMAC is used for recording */ + +static int prog_dmabuf(struct sv_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + s->enable &= ~SV_CENABLE_RE; + fmt >>= SV_CFMT_CSHIFT; + } else { + s->enable &= ~SV_CENABLE_PE; + fmt >>= SV_CFMT_ASHIFT; + } + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); + fmt &= SV_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + SetPageReserved(page); + } + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + set_dmac(s, db->dmaaddr, db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); + } else { + set_dmaa(s, db->dmaaddr, db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); + } + spin_unlock_irqrestore(&s->lock, flags); + db->enabled = 1; + db->ready = 1; + return 0; +} + +static inline void clear_advance(struct sv_state *s) +{ + unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void sv_update_ptr(struct sv_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->enable &= ~SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* hold spinlock for the following! */ +static void sv_handle_midi(struct sv_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static irqreturn_t sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sv_state *s = (struct sv_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->ioenh + SV_CODEC_STATUS); + if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) + return IRQ_NONE; + spin_lock(&s->lock); + sv_update_ptr(s); + sv_handle_midi(s); + spin_unlock(&s->lock); + return IRQ_HANDLED; +} + +static void sv_midi_timer(unsigned long data) +{ + struct sv_state *s = (struct sv_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SV_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 + +static const struct { + unsigned left:5; + unsigned right:5; + unsigned type:3; + unsigned rec:3; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, + [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, + [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, + [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, + [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, + [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, + [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, + [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, + [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } +}; + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + +static int return_mixval(struct sv_state *s, unsigned i, int *arg) +{ + unsigned long flags; + unsigned char l, r, rl, rr; + + spin_lock_irqsave(&s->lock, flags); + l = rdindir(s, mixtable[i].left); + r = rdindir(s, mixtable[i].right); + spin_unlock_irqrestore(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + r &= 0xf; + l &= 0xf; + rl = 10 + 6 * (l & 15); + rr = 10 + 6 * (r & 15); + break; + + case MT_4MUTEMONO: + rl = 55 - 3 * (l & 15); + if (r & 0x10) + rl += 45; + rr = rl; + r = l; + break; + + case MT_5MUTE: + default: + rl = 100 - 3 * (l & 31); + rr = 100 - 3 * (r & 31); + break; + + case MT_6MUTE: + rl = 100 - 3 * (l & 63) / 2; + rr = 100 - 3 * (r & 63) / 2; + break; + } + if (l & 0x80) + rl = 0; + if (r & 0x80) + rr = 0; + return put_user((rr << 8) | rl, arg); +} + +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + [SOUND_MIXER_RECLEV] = 1, + [SOUND_MIXER_LINE1] = 2, + [SOUND_MIXER_CD] = 3, + [SOUND_MIXER_LINE] = 4, + [SOUND_MIXER_MIC] = 5, + [SOUND_MIXER_SYNTH] = 6, + [SOUND_MIXER_LINE2] = 7, + [SOUND_MIXER_VOLUME] = 8, + [SOUND_MIXER_PCM] = 9 +}; + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static unsigned mixer_recmask(struct sv_state *s) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&s->lock, flags); + j = rdindir(s, SV_CIMIX_ADCINL) >> 5; + spin_unlock_irqrestore(&s->lock, flags); + j &= 7; + for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); + return 1 << i; +} + +static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + int __user *p = (int __user *)arg; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.id, "SonicVibes", sizeof(info.id)); + strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + memset(&info, 0, sizeof(info)); + strlcpy(info.id, "SonicVibes", sizeof(info.id)); + strlcpy(info.name, "S3 SonicVibes", sizeof(info.name)); + if (copy_to_user((void __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, p); + if (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ + if (get_user(val, p)) + return -EFAULT; + spin_lock_irqsave(&s->lock, flags); + if (val & 1) { + if (val & 2) { + l = 4 - ((val >> 2) & 7); + if (l & ~3) + l = 4; + r = 4 - ((val >> 5) & 7); + if (r & ~3) + r = 4; + wrindir(s, SV_CISRSSPACE, l); + wrindir(s, SV_CISRSCENTER, r); + } else + wrindir(s, SV_CISRSSPACE, 0x80); + } + l = rdindir(s, SV_CISRSSPACE); + r = rdindir(s, SV_CISRSCENTER); + spin_unlock_irqrestore(&s->lock, flags); + if (l & 0x80) + return put_user(0, p); + return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, p); + } + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), p); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, p); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, p); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return return_mixval(s, i, p); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + return put_user(s->mix.vol[volidx[i]-1], p); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } + } + if (_SIOC_DIR(cmd) != (_SIOC_READ|_SIOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (get_user(val, p)) + return -EFAULT; + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~mixer_recmask(s); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (mixtable[i].rec) + break; + } + if (i == SOUND_MIXER_NRDEVICES) + return 0; + spin_lock_irqsave(&s->lock, flags); + frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); + frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + l = val & 0xff; + r = (val >> 8) & 0xff; + if (mixtable[i].type == MT_4MUTEMONO) + l = (r + l) / 2; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rr = 0; + if (l < 10) + rl = 0x80; + else { + if (l >= 55) { + rr = 0x10; + l -= 45; + } + rl = (55 - l) / 3; + } + wrindir(s, mixtable[i].left, rl); + frobindir(s, mixtable[i].right, ~0x10, rr); + break; + + case MT_5MUTE: + if (l < 7) + rl = 0x80; + else + rl = (100 - l) / 3; + if (r < 7) + rr = 0x80; + else + rr = (100 - r) / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x80; + else + rl = (100 - l) * 2 / 3; + if (r < 6) + rr = 0x80; + else + rr = (100 - r) * 2 / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return return_mixval(s, i, p); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + s->mix.vol[volidx[i]-1] = val; + return put_user(s->mix.vol[volidx[i]-1], p); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } +} + +/* --------------------------------------------------------------------- */ + +static int sv_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + return nonseekable_open(inode, file); +} + +static int sv_release_mixdev(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + +static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations sv_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = sv_ioctl_mixdev, + .open = sv_open_mixdev, + .release = sv_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct sv_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; + tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "sv: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "sv: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_adc.enabled) + start_adc(s); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static ssize_t sv_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + if (!schedule_timeout(HZ)) { + printk(KERN_DEBUG "sv: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (s->dma_dac.enabled) + start_dac(s); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf(s, 1)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf(s, 0)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct dmabuf *db; + int ret = -EINVAL; + unsigned long size; + + VALIDATE_STATE(s); + lock_kernel(); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + goto out; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + goto out; + db = &s->dma_adc; + } else + goto out; + ret = -EINVAL; + if (vma->vm_pgoff != 0) + goto out; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + goto out; + ret = -EAGAIN; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + goto out; + db->mapped = 1; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + unsigned char fmtm, fmtd; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, p); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, p)) + return -EFAULT; + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, p); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(argp, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, p); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, p); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? 16 : 8, p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int sv_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned char fmtm = ~0, fmts = 0; + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int sv_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + mutex_lock(&s->open_mutex); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = sv_read, + .write = sv_write, + .poll = sv_poll, + .ioctl = sv_ioctl, + .mmap = sv_mmap, + .open = sv_open, + .release = sv_release, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t sv_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.iwait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); + return ret; +} + +static ssize_t sv_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + if (count == 0) + return 0; + ret = 0; + add_wait_queue(&s->midi.owait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) { + __set_current_state(TASK_INTERRUPTIBLE); + sv_handle_midi(s); + } + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int sv_midi_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_midi == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); + outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ + wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ + outb(0xff, s->iomidi+1); /* reset command */ + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = sv_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int sv_midi_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + unlock_kernel(); + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "sv: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + set_current_state(TASK_RUNNING); + } + mutex_lock(&s->open_mutex); + s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE)); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_midi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = sv_midi_read, + .write = sv_midi_write, + .poll = sv_midi_poll, + .open = sv_midi_open, + .release = sv_midi_release, +}; + +/* --------------------------------------------------------------------- */ + +static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct sv_state *s = (struct sv_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void __user *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void __user *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *__user )arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + + default: + return -EINVAL; + } +} + +static int sv_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + DECLARE_WAITQUEUE(wait, current); + struct list_head *list; + struct sv_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct sv_state, devs); + if (s->dev_dmfm == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + mutex_lock(&s->open_mutex); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&s->open_mutex); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&s->open_mutex); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + mutex_lock(&s->open_mutex); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + mutex_unlock(&s->open_mutex); + return nonseekable_open(inode, file); +} + +static int sv_dmfm_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + lock_kernel(); + mutex_lock(&s->open_mutex); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + wake_up(&s->open_wait); + mutex_unlock(&s->open_mutex); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations sv_dmfm_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = sv_dmfm_ioctl, + .open = sv_dmfm_open, + .release = sv_dmfm_release, +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int reverb[NR_DEVICE]; + +#if 0 +static int wavetable[NR_DEVICE]; +#endif + +static unsigned int devindex; + +module_param_array(reverb, bool, NULL, 0); +MODULE_PARM_DESC(reverb, "if 1 enables the reverb circuitry. NOTE: your card must have the reverb RAM"); +#if 0 +MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled"); +#endif + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("S3 SonicVibes Driver"); +MODULE_LICENSE("GPL"); + + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __devinitdata = { + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 } +}; + +#define RSRCISIOREGION(dev,num) (pci_resource_start((dev), (num)) != 0 && \ + (pci_resource_flags((dev), (num)) & IORESOURCE_IO)) + +#ifdef SUPPORT_JOYSTICK +static int __devinit sv_register_gameport(struct sv_state *s, int io_port) +{ + struct gameport *gp; + + if (!request_region(io_port, SV_EXTENT_GAME, "S3 SonicVibes Gameport")) { + printk(KERN_ERR "sv: gameport io ports are in use\n"); + return -EBUSY; + } + + s->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR "sv: can not allocate memory for gameport\n"); + release_region(io_port, SV_EXTENT_GAME); + return -ENOMEM; + } + + gameport_set_name(gp, "S3 SonicVibes Gameport"); + gameport_set_phys(gp, "isa%04x/gameport0", io_port); + gp->dev.parent = &s->dev->dev; + gp->io = io_port; + + gameport_register_port(gp); + + return 0; +} + +static inline void sv_unregister_gameport(struct sv_state *s) +{ + if (s->gameport) { + int gpio = s->gameport->io; + gameport_unregister_port(s->gameport); + release_region(gpio, SV_EXTENT_GAME); + } +} +#else +static inline int sv_register_gameport(struct sv_state *s, int io_port) { return -ENOSYS; } +static inline void sv_unregister_gameport(struct sv_state *s) { } +#endif /* SUPPORT_JOYSTICK */ + +static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid) +{ + static char __devinitdata sv_ddma_name[] = "S3 Inc. SonicVibes DDMA Controller"; + struct sv_state *s; + mm_segment_t fs; + int i, val, ret; + int gpio; + char *ddmaname; + unsigned ddmanamelen; + + if ((ret=pci_enable_device(pcidev))) + return ret; + + if (!RSRCISIOREGION(pcidev, RESOURCE_SB) || + !RSRCISIOREGION(pcidev, RESOURCE_ENH) || + !RSRCISIOREGION(pcidev, RESOURCE_SYNTH) || + !RSRCISIOREGION(pcidev, RESOURCE_MIDI) || + !RSRCISIOREGION(pcidev, RESOURCE_GAME)) + return -ENODEV; + if (pcidev->irq == 0) + return -ENODEV; + if (pci_set_dma_mask(pcidev, DMA_24BIT_MASK)) { + printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n"); + return -ENODEV; + } + /* try to allocate a DDMA resource if not already available */ + if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) { + pcidev->resource[RESOURCE_DDMA].start = 0; + pcidev->resource[RESOURCE_DDMA].end = 2*SV_EXTENT_DMA-1; + pcidev->resource[RESOURCE_DDMA].flags = PCI_BASE_ADDRESS_SPACE_IO | IORESOURCE_IO; + ddmanamelen = strlen(sv_ddma_name)+1; + if (!(ddmaname = kmalloc(ddmanamelen, GFP_KERNEL))) + return -1; + memcpy(ddmaname, sv_ddma_name, ddmanamelen); + pcidev->resource[RESOURCE_DDMA].name = ddmaname; + if (pci_assign_resource(pcidev, RESOURCE_DDMA)) { + pcidev->resource[RESOURCE_DDMA].name = NULL; + kfree(ddmaname); + printk(KERN_ERR "sv: cannot allocate DDMA controller io ports\n"); + return -EBUSY; + } + } + if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { + printk(KERN_WARNING "sv: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct sv_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + mutex_init(&s->open_mutex); + spin_lock_init(&s->lock); + s->magic = SV_MAGIC; + s->dev = pcidev; + s->iosb = pci_resource_start(pcidev, RESOURCE_SB); + s->ioenh = pci_resource_start(pcidev, RESOURCE_ENH); + s->iosynth = pci_resource_start(pcidev, RESOURCE_SYNTH); + s->iomidi = pci_resource_start(pcidev, RESOURCE_MIDI); + s->iodmaa = pci_resource_start(pcidev, RESOURCE_DDMA); + s->iodmac = pci_resource_start(pcidev, RESOURCE_DDMA) + SV_EXTENT_DMA; + gpio = pci_resource_start(pcidev, RESOURCE_GAME); + pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ + pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ + printk(KERN_DEBUG "sv: io ports: %#lx %#lx %#lx %#lx %#x %#x %#x\n", + s->iosb, s->ioenh, s->iosynth, s->iomidi, gpio, s->iodmaa, s->iodmac); + s->irq = pcidev->irq; + + /* hack */ + pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ + + ret = -EBUSY; + if (!request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); + goto err_region5; + } + if (!request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA")) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); + goto err_region4; + } + if (!request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC")) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); + goto err_region3; + } + if (!request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); + goto err_region2; + } + if (!request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth")) { + printk(KERN_ERR "sv: io ports %#lx-%#lx in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); + goto err_region1; + } + + /* initialize codec registers */ + outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ + udelay(50); + outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ + udelay(50); + outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE */ + | (reverb[devindex] ? SV_CCTRL_REVERB : 0), s->ioenh + SV_CODEC_CONTROL); + inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ + wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ + wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ + outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); + /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ + /* outb(0xff, s->iodmac + SV_DMA_RESET); */ + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ + wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ + wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ + setpll(s, SV_CIADCPLLM, 8000); + wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ + wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); + wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); + wrindir(s, SV_CIADCOUTPUT, 0); + /* request irq */ + if ((ret=request_irq(s->irq,sv_interrupt,IRQF_SHARED,"S3 SonicVibes",s))) { + printk(KERN_ERR "sv: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n", + s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) { + ret = s->dev_audio; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) { + ret = s->dev_midi; + goto err_dev3; + } + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) { + ret = s->dev_dmfm; + goto err_dev4; + } + pci_set_master(pcidev); /* enable bus mastering */ + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* register gameport */ + sv_register_gameport(s, gpio); + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "sv: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->iosynth, SV_EXTENT_SYNTH); + err_region1: + release_region(s->iomidi, SV_EXTENT_MIDI); + err_region2: + release_region(s->iodmac, SV_EXTENT_DMA); + err_region3: + release_region(s->iodmaa, SV_EXTENT_DMA); + err_region4: + release_region(s->ioenh, SV_EXTENT_ENH); + err_region5: + kfree(s); + return ret; +} + +static void __devexit sv_remove(struct pci_dev *dev) +{ + struct sv_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); + outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ + synchronize_irq(s->irq); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ + /*outb(0, s->iodmaa + SV_DMA_RESET);*/ + /*outb(0, s->iodmac + SV_DMA_RESET);*/ + free_irq(s->irq, s); + sv_unregister_gameport(s); + release_region(s->iodmac, SV_EXTENT_DMA); + release_region(s->iodmaa, SV_EXTENT_DMA); + release_region(s->ioenh, SV_EXTENT_ENH); + release_region(s->iomidi, SV_EXTENT_MIDI); + release_region(s->iosynth, SV_EXTENT_SYNTH); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree(s); + pci_set_drvdata(dev, NULL); +} + +static struct pci_device_id id_table[] = { + { PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver sv_driver = { + .name = "sonicvibes", + .id_table = id_table, + .probe = sv_probe, + .remove = __devexit_p(sv_remove), +}; + +static int __init init_sonicvibes(void) +{ + printk(KERN_INFO "sv: version v0.31 time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + return pci_register_driver(&sv_driver); +} + +static void __exit cleanup_sonicvibes(void) +{ + printk(KERN_INFO "sv: unloading\n"); + pci_unregister_driver(&sv_driver); + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); +} + +module_init(init_sonicvibes); +module_exit(cleanup_sonicvibes); + +/* --------------------------------------------------------------------- */ + +#ifndef MODULE + +/* format is: sonicvibes=[reverb] sonicvibesdmaio=dmaioaddr */ + +static int __init sonicvibes_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; +#if 0 + if (get_option(&str, &reverb[nr_dev]) == 2) + (void)get_option(&str, &wavetable[nr_dev]); +#else + (void)get_option(&str, &reverb[nr_dev]); +#endif + + nr_dev++; + return 1; +} + +__setup("sonicvibes=", sonicvibes_setup); + +#endif /* MODULE */ diff --git a/trunk/sound/oss/sound_calls.h b/trunk/sound/oss/sound_calls.h index 87d8ad4a0340..1ae07509664f 100644 --- a/trunk/sound/oss/sound_calls.h +++ b/trunk/sound/oss/sound_calls.h @@ -13,6 +13,8 @@ int DMAbuf_move_wrpointer(int dev, int l); void DMAbuf_init(int dev, int dma1, int dma2); void DMAbuf_deinit(int dev); int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); +int DMAbuf_open_dma (int dev); +void DMAbuf_close_dma (int dev); void DMAbuf_inputintr(int dev); void DMAbuf_outputintr(int dev, int underflow_flag); struct dma_buffparms; @@ -71,6 +73,7 @@ unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait); int MIDIbuf_avail(int dev); void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); +void MIDIbuf_init(void); /* From soundcard.c */ diff --git a/trunk/sound/oss/sound_syms.c b/trunk/sound/oss/sound_syms.c new file mode 100644 index 000000000000..cb7c33fe5b05 --- /dev/null +++ b/trunk/sound/oss/sound_syms.c @@ -0,0 +1,50 @@ +/* + * The sound core exports the following symbols to the rest of + * modulespace. + * + * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL + * + * Thu May 27 1999 Andrew J. Kroll + * left out exported symbol... fixed + */ + +#include +#include "sound_config.h" +#include "sound_calls.h" + +char sound_syms_symbol; + +EXPORT_SYMBOL(mixer_devs); +EXPORT_SYMBOL(audio_devs); +EXPORT_SYMBOL(num_mixers); +EXPORT_SYMBOL(num_audiodevs); + +EXPORT_SYMBOL(midi_devs); +EXPORT_SYMBOL(num_midis); +EXPORT_SYMBOL(synth_devs); + +EXPORT_SYMBOL(sound_timer_devs); + +EXPORT_SYMBOL(sound_install_audiodrv); +EXPORT_SYMBOL(sound_install_mixer); +EXPORT_SYMBOL(sound_alloc_dma); +EXPORT_SYMBOL(sound_free_dma); +EXPORT_SYMBOL(sound_open_dma); +EXPORT_SYMBOL(sound_close_dma); +EXPORT_SYMBOL(sound_alloc_mididev); +EXPORT_SYMBOL(sound_alloc_mixerdev); +EXPORT_SYMBOL(sound_alloc_timerdev); +EXPORT_SYMBOL(sound_alloc_synthdev); +EXPORT_SYMBOL(sound_unload_audiodev); +EXPORT_SYMBOL(sound_unload_mididev); +EXPORT_SYMBOL(sound_unload_mixerdev); +EXPORT_SYMBOL(sound_unload_timerdev); +EXPORT_SYMBOL(sound_unload_synthdev); + +EXPORT_SYMBOL(load_mixer_volumes); + +EXPORT_SYMBOL(conf_printf); +EXPORT_SYMBOL(conf_printf2); + +MODULE_DESCRIPTION("OSS Sound subsystem"); +MODULE_AUTHOR("Hannu Savolainen, et al."); diff --git a/trunk/sound/oss/sound_timer.c b/trunk/sound/oss/sound_timer.c index f0f0c19fbff7..146bf85de958 100644 --- a/trunk/sound/oss/sound_timer.c +++ b/trunk/sound/oss/sound_timer.c @@ -76,7 +76,6 @@ void sound_timer_syncinterval(unsigned int new_usecs) tmr_ctr = 0; usecs_per_tmr = new_usecs; } -EXPORT_SYMBOL(sound_timer_syncinterval); static void tmr_reset(void) { @@ -301,7 +300,6 @@ void sound_timer_interrupt(void) } spin_unlock_irqrestore(&lock,flags); } -EXPORT_SYMBOL(sound_timer_interrupt); void sound_timer_init(struct sound_lowlev_timer *t, char *name) { @@ -323,5 +321,3 @@ void sound_timer_init(struct sound_lowlev_timer *t, char *name) strcpy(sound_timer.info.name, name); sound_timer_devs[n] = &sound_timer; } -EXPORT_SYMBOL(sound_timer_init); - diff --git a/trunk/sound/oss/soundcard.c b/trunk/sound/oss/soundcard.c index 2344d09c7114..683dc00a8d2b 100644 --- a/trunk/sound/oss/soundcard.c +++ b/trunk/sound/oss/soundcard.c @@ -107,7 +107,6 @@ int *load_mixer_volumes(char *name, int *levels, int present) mixer_vols[n].levels[i] = levels[i]; return mixer_vols[n].levels; } -EXPORT_SYMBOL(load_mixer_volumes); static int set_mixer_levels(void __user * arg) { @@ -542,6 +541,12 @@ static int __init oss_init(void) int err; int i, j; + /* drag in sound_syms.o */ + { + extern char sound_syms_symbol; + sound_syms_symbol = 0; + } + #ifdef CONFIG_PCI if(dmabug) isa_dma_bridge_buggy = dmabug; @@ -609,8 +614,6 @@ static void __exit oss_cleanup(void) module_init(oss_init); module_exit(oss_cleanup); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("OSS Sound subsystem"); -MODULE_AUTHOR("Hannu Savolainen, et al."); int sound_alloc_dma(int chn, char *deviceID) @@ -624,7 +627,6 @@ int sound_alloc_dma(int chn, char *deviceID) return 0; } -EXPORT_SYMBOL(sound_alloc_dma); int sound_open_dma(int chn, char *deviceID) { @@ -640,7 +642,6 @@ int sound_open_dma(int chn, char *deviceID) dma_alloc_map[chn] = DMA_MAP_BUSY; return 0; } -EXPORT_SYMBOL(sound_open_dma); void sound_free_dma(int chn) { @@ -651,7 +652,6 @@ void sound_free_dma(int chn) free_dma(chn); dma_alloc_map[chn] = DMA_MAP_UNAVAIL; } -EXPORT_SYMBOL(sound_free_dma); void sound_close_dma(int chn) { @@ -661,7 +661,6 @@ void sound_close_dma(int chn) } dma_alloc_map[chn] = DMA_MAP_FREE; } -EXPORT_SYMBOL(sound_close_dma); static void do_sequencer_timer(unsigned long dummy) { @@ -715,7 +714,6 @@ void conf_printf(char *name, struct address_info *hw_config) printk("\n"); #endif } -EXPORT_SYMBOL(conf_printf); void conf_printf2(char *name, int base, int irq, int dma, int dma2) { @@ -736,5 +734,3 @@ void conf_printf2(char *name, int base, int irq, int dma, int dma2) printk("\n"); #endif } -EXPORT_SYMBOL(conf_printf2); - diff --git a/trunk/sound/oss/tuning.h b/trunk/sound/oss/tuning.h index a73e3dd39f9a..858e1fe6c618 100644 --- a/trunk/sound/oss/tuning.h +++ b/trunk/sound/oss/tuning.h @@ -1,11 +1,13 @@ -static unsigned short semitone_tuning[24] = +#ifdef SEQUENCER_C + +unsigned short semitone_tuning[24] = { /* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, /* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, /* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 }; -static unsigned short cent_tuning[100] = +unsigned short cent_tuning[100] = { /* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, /* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, @@ -21,3 +23,7 @@ static unsigned short cent_tuning[100] = /* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, /* 96 */ 10570, 10576, 10582, 10589 }; +#else +extern unsigned short semitone_tuning[24]; +extern unsigned short cent_tuning[100]; +#endif diff --git a/trunk/sound/oss/wavfront.c b/trunk/sound/oss/wavfront.c new file mode 100644 index 000000000000..38d9aa0f16a5 --- /dev/null +++ b/trunk/sound/oss/wavfront.c @@ -0,0 +1,3553 @@ +/* -*- linux-c -*- + * + * sound/wavfront.c + * + * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + * It also provides support for the ICS emulation of an MPU-401. Full + * support for the ICS emulation's "virtual MIDI mode" is provided in + * wf_midi.c. + * + * Support is also provided for the Tropez Plus' onboard FX processor, + * a Yamaha YSS225. Currently, code exists to configure the YSS225, + * and there is an interface allowing tweaking of any of its memory + * addresses. However, I have been unable to decipher the logical + * positioning of the configuration info for various effects, so for + * now, you just get the YSS225 in the same state as Turtle Beach's + * "SETUPSND.EXE" utility leaves it. + * + * The boards' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co], + * This chip also controls the configuration of the card: the wavefront + * synth is logical unit 4. + * + * + * Supported devices: + * + * /dev/dsp - using cs4232+ad1848 modules, OSS compatible + * /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible + * /dev/synth00 - raw synth interface + * + ********************************************************************** + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * Although the relevant code here is all new, the handling of + * sample/alias/multi- samples is entirely based on a driver by Matt + * Martin and Rutger Nijlunsing which demonstrated how to get things + * to work correctly. The GUS patch loading code has been almost + * unaltered by me, except to fit formatting and function names in the + * rest of the file. Many thanks to them. + * + * Appreciation and thanks to Hannu Savolainen for his early work on the Maui + * driver, and answering a few questions while this one was developed. + * + * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their + * complete lack of help in developing this driver, and in particular + * for their utter silence in response to questions about undocumented + * aspects of configuring a WaveFront soundcard, particularly the + * effects processor. + * + * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $ + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + * + * Changes: + * 11-10-2000 Bartlomiej Zolnierkiewicz + * Added some __init and __initdata to entries in yss225.c + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sound_config.h" + +#include + +#define _MIDI_SYNTH_C_ +#define MIDI_SYNTH_NAME "WaveFront MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +/* Compile-time control of the extent to which OSS is supported. + + I consider /dev/sequencer to be an anachronism, but given its + widespread usage by various Linux MIDI software, it seems worth + offering support to it if it's not too painful. Instead of using + /dev/sequencer, I recommend: + + for synth programming and patch loading: /dev/synthNN + for kernel-synchronized MIDI sequencing: the ALSA sequencer + for direct MIDI control: /dev/midiNN + + I have never tried static compilation into the kernel. The #if's + for this are really just notes to myself about what the code is + for. +*/ + +#define OSS_SUPPORT_SEQ 0x1 /* use of /dev/sequencer */ +#define OSS_SUPPORT_STATIC_INSTALL 0x2 /* static compilation into kernel */ + +#define OSS_SUPPORT_LEVEL 0x1 /* just /dev/sequencer for now */ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ +static int (*midi_load_patch) (int devno, int format, const char __user *addr, + int offs, int count, int pmgr_flag) = NULL; +#endif /* OSS_SUPPORT_SEQ */ + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +#ifdef WF_DEBUG + +/* Thank goodness for gcc's preprocessor ... */ + +#define DPRINT(cond, format, args...) \ + if ((dev.debug & (cond)) == (cond)) { \ + printk (KERN_DEBUG LOGNAME format, ## args); \ + } +#else +#define DPRINT(cond, format, args...) +#endif + +#define LOGNAME "WaveFront: " + +/* bitmasks for WaveFront status port value */ + +#define STAT_RINTR_ENABLED 0x01 +#define STAT_CAN_READ 0x02 +#define STAT_INTR_READ 0x04 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_CAN_WRITE 0x20 +#define STAT_INTR_WRITE 0x40 + +/*** Module-accessible parameters ***************************************/ + +static int wf_raw; /* we normally check for "raw state" to firmware + loading. if set, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +static int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +static int debug_default; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +static char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +static int wait_polls = 2000; /* This is a number of tries we poll the + status register before resorting to sleeping. + WaveFront being an ISA card each poll takes + about 1.2us. So before going to + sleep we wait up to 2.4ms in a loop. + */ + +static int sleep_length = HZ/100; /* This says how long we're going to + sleep between polls. + 10ms sounds reasonable for fast response. + */ + +static int sleep_tries = 50; /* Wait for status 0.5 seconds total. */ + +static int reset_time = 2; /* hundreths of a second we wait after a HW reset for + the expected interrupt. + */ + +static int ramcheck_time = 20; /* time in seconds to wait while ROM code + checks on-board RAM. + */ + +static int osrun_time = 10; /* time in seconds we wait for the OS to + start running. + */ + +module_param(wf_raw, int, 0); +module_param(fx_raw, int, 0); +module_param(debug_default, int, 0); +module_param(wait_polls, int, 0); +module_param(sleep_length, int, 0); +module_param(sleep_tries, int, 0); +module_param(ospath, charp, 0); +module_param(reset_time, int, 0); +module_param(ramcheck_time, int, 0); +module_param(osrun_time, int, 0); + +/***************************************************************************/ + +/* Note: because this module doesn't export any symbols, this really isn't + a global variable, even if it looks like one. I was quite confused by + this when I started writing this as a (newer) module -- pbd. +*/ + +struct wf_config { + int devno; /* device number from kernel */ + int irq; /* "you were one, one of the few ..." */ + int base; /* low i/o port address */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + volatile int irq_cnt; /* ditto */ + int opened; /* flag, holds open(2) mode */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + + int synth_dev; /* devno for "raw" synth */ + int mididev; /* devno for internal MIDI */ + int ext_mididev; /* devno for external MIDI */ + int fx_mididev; /* devno for FX MIDI interface */ +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + int oss_dev; /* devno for OSS sequencer synth */ +#endif /* OSS_SUPPORT_SEQ */ + + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char has_fx; /* has FX processor (Tropez+) */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_on; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + wait_queue_head_t interrupt_sleeper; +} dev; + +static DEFINE_SPINLOCK(lock); +static int detect_wffx(void); +static int wffx_ioctl (wavefront_fx_info *); +static int wffx_init (void); + +static int wavefront_delete_sample (int sampnum); +static int wavefront_find_free_sample (void); + +/* From wf_midi.c */ + +extern int virtual_midi_enable (void); +extern int virtual_midi_disable (void); +extern int detect_wf_mpu (int, int); +extern int install_wf_mpu (void); +extern int uninstall_wf_mpu (void); + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static inline int +wavefront_status (void) + +{ + return inb (dev.status_port); +} + +static int +wavefront_wait (int mask) + +{ + int i; + + for (i = 0; i < wait_polls; i++) + if (wavefront_status() & mask) + return 1; + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status() & mask) { + set_current_state(TASK_RUNNING); + return 1; + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(sleep_length); + if (signal_pending(current)) + break; + } + + set_current_state(TASK_RUNNING); + return 0; +} + +static int +wavefront_read (void) + +{ + if (wavefront_wait (STAT_CAN_READ)) + return inb (dev.data_port); + + DPRINT (WF_DEBUG_DATA, "read timeout.\n"); + + return -1; +} + +static int +wavefront_write (unsigned char data) + +{ + if (wavefront_wait (STAT_CAN_WRITE)) { + outb (data, dev.data_port); + return 0; + } + + DPRINT (WF_DEBUG_DATA, "write timeout.\n"); + + return -1; +} + +static int +wavefront_cmd (int cmd, unsigned char *rbuf, unsigned char *wbuf) + +{ + int ack; + int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + printk (KERN_WARNING LOGNAME "command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned int) rbuf; + rbuf = NULL; + } + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + + if (wavefront_write (cmd)) { + DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + } + + if (wfcmd->write_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (wavefront_write (wbuf[i])) { + DPRINT (WF_DEBUG_IO, "bad write for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + DPRINT (WF_DEBUG_DATA, "write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + + if (wfcmd->read_cnt > 0) { + DPRINT (WF_DEBUG_DATA, "reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read()) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for byte " + "%d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read ()) == -1) { + DPRINT (WF_DEBUG_IO, "bad read for " + "error byte at " + "read byte %d " + "of 0x%x [%s].\n", + i, cmd, + wfcmd->action); + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return (0); + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + DPRINT (WF_DEBUG_IO, "error %d (%s) " + "during " + "read for byte " + "%d of 0x%x " + "[%s].\n", + c, + wavefront_errorstr (c), + i, cmd, + wfcmd->action); + return 1; + + } + + } else { + rbuf[i] = c; + } + + DPRINT (WF_DEBUG_DATA, "read[%d] = 0x%x\n",i, rbuf[i]); + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + DPRINT (WF_DEBUG_CMD, "reading ACK for 0x%x\n", cmd); + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read()) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + DPRINT (WF_DEBUG_IO, "cannot read ack for " + "0x%x [%s].\n", + cmd, wfcmd->action); + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read ()) == -1) { + DPRINT (WF_DEBUG_DATA, + "cannot read err " + "for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + + DPRINT (WF_DEBUG_IO, "0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + + return -err; + } + } + + DPRINT (WF_DEBUG_DATA, "ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } else { + + DPRINT (WF_DEBUG_CMD, "0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + + return 0; + +} + +/*********************************************************************** +WaveFront: data munging + +Things here are weird. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8-32 bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +weird casting and worrying about bit-level offsets. + +**********************************************************************/ + +static +unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + int i; + + for (i = 0;i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = wavefront_cmd (WFC_DELETE_SAMPLE, NULL, wbuf)) == 0) { + dev.sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (wavefront_cmd (WFC_GET_NSAMPLES, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME "cannot request sample count.\n"); + return -1; + } + + sc_real = sc_alias = sc_multi = dev.samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (wavefront_cmd (WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot identify sample " + "type of slot %d\n", i); + dev.sample_status[i] = WF_ST_EMPTY; + continue; + } + + dev.sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + dev.sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + printk (KERN_WARNING LOGNAME "unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + dev.samples_used++; + } + } + + printk (KERN_INFO LOGNAME + "%d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", dev.samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - dev.samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (void) + +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = wavefront_cmd (WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + dev.patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + dev.sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + dev.patch_status[i] = 0; + } else { + printk (KERN_ERR LOGNAME "upload patch " + "error 0x%x\n", x); + dev.patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (dev.patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (dev.patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + printk (KERN_INFO LOGNAME + "%d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (void) + +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = wavefront_cmd (WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + dev.prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + dev.patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + dev.prog_status[i] = 0; + } else { + printk (KERN_ERR LOGNAME "upload program " + "error 0x%x\n", x); + dev.prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (dev.prog_status[i]) { + cnt++; + } + } + + printk (KERN_INFO LOGNAME "%d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", + header->number); + + dev.patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (wavefront_cmd (WFC_DOWNLOAD_PATCH, NULL, buf)) { + printk (KERN_ERR LOGNAME "download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", + header->number); + + dev.prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + dev.patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (wavefront_cmd (WFC_DOWNLOAD_PROGRAM, NULL, buf)) { + printk (KERN_WARNING LOGNAME "download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (void) + +{ + char rbuf[8]; + + if (wavefront_cmd (WFC_REPORT_FREE_MEMORY, rbuf, NULL)) { + printk (KERN_WARNING LOGNAME "can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (wavefront_patch_info *header, + UINT16 __user *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + UINT16 sample_short; + UINT32 length; + UINT16 __user *data_end = NULL; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + DPRINT (WF_DEBUG_LOAD_PATCH, "sample %sdownload for slot %d, " + "type %d, %d bytes from %p\n", + header->size ? "" : "header ", + header->number, header->subkey, + header->size, + header->dataptr); + + if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) { + int x; + + if ((x = wavefront_find_free_sample ()) < 0) { + return -ENOMEM; + } + printk (KERN_DEBUG LOGNAME "unspecified sample => %d\n", x); + header->number = x; + } + + if (header->size) { + + /* XXX it's a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little + crazy: we'd need 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (dev.rom_samples_rdonly) { + if (dev.sample_status[header->number] & WF_SLOT_ROM) { + printk (KERN_ERR LOGNAME "sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (header->number); + } + + if (header->size) { + dev.freemem = wavefront_freemem (); + + if (dev.freemem < header->size) { + printk (KERN_ERR LOGNAME + "insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { + printk (KERN_ERR LOGNAME "channel selection only " + "possible on 16-bit samples"); + return -(EINVAL); + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), + initial_skip, skip); + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly weird. What kind of weirdo decided that in + a system dominated by 16 and 32 bit integers, they would use + a just 12 bits ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero ? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (wavefront_cmd (header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + NULL, sample_hdr)) { + printk (KERN_WARNING LOGNAME "sample %sdownload refused.\n", + header->size ? "" : "header "); + return -(EIO); + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (wavefront_cmd (WFC_DOWNLOAD_BLOCK, NULL, NULL)) { + printk (KERN_WARNING LOGNAME "download block " + "request refused.\n"); + return -(EIO); + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + __get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { /* GUS ? */ + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, dev.block_port); + } else { + outw (sample_short, dev.last_block_port); + } + } + + /* Get "DMA page acknowledge", even though its really + nothing to do with DMA at all. + */ + + if ((dma_ack = wavefront_read ()) != WF_DMA_ACK) { + if (dma_ack == -1) { + printk (KERN_ERR LOGNAME "upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + printk (KERN_ERR LOGNAME "upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return (0); +} + +static int +wavefront_send_alias (wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + DPRINT (WF_DEBUG_LOAD_PATCH, "download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (wavefront_cmd (WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) { + printk (KERN_ERR LOGNAME "download alias failed.\n"); + return -(EIO); + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + DPRINT (WF_DEBUG_LOAD_PATCH, "multi %d with %d=%d samples\n", + header->number, + header->hdr.ms.NumberOfSamples, + num_samples); + + for (i = 0; i < num_samples; i++) { + DPRINT(WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA, "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (wavefront_cmd (WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) ((num_samples*2)+3), + msample_hdr)) { + printk (KERN_ERR LOGNAME "download of multisample failed.\n"); + return -(EIO); + } + + dev.sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (wavefront_cmd (WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + printk (KERN_ERR LOGNAME "upload multisample failed.\n"); + return -(EIO); + } + + DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", + header->number, log_ns[0]); + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + s8 d[2]; + + if ((d[0] = wavefront_read ()) == -1) { + printk (KERN_ERR LOGNAME "upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + if ((d[1] = wavefront_read ()) == -1) { + printk (KERN_ERR LOGNAME "upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + DPRINT (WF_DEBUG_DATA, "msample sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + + return (0); +} + + +static int +wavefront_send_drum (wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + DPRINT (WF_DEBUG_LOAD_PATCH, "downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (wavefront_cmd (WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) { + printk (KERN_ERR LOGNAME "download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (void) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(dev.sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING LOGNAME "no free sample slots!\n"); + return -1; +} + +static int +wavefront_find_free_patch (void) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(dev.patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING LOGNAME "no free patch slots!\n"); + return -1; +} + +static int +log2_2048(int n) + +{ + int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, + 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, + 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, + 9510, 9626, 9738, 9845, 9949, 10049, 10146}; + int i; + + /* Returns 2048*log2(n) */ + + /* FIXME: this is like doing integer math + on quantum particles (RuN) */ + + i=0; + while(n>=32*256) { + n>>=8; + i+=2048*8; + } + while(n>=32) { + n>>=1; + i+=2048; + } + i+=tbl[n]; + return(i); +} + +static int +wavefront_load_gus_patch (int devno, int format, const char __user *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info guspatch; + wavefront_patch_info *samp, *pat, *prog; + wavefront_patch *patp; + wavefront_sample *sampp; + wavefront_program *progp; + + int i,base_note; + long sizeof_patch; + int rc = -ENOMEM; + + samp = kmalloc(3 * sizeof(wavefront_patch_info), GFP_KERNEL); + if (!samp) + goto free_fail; + pat = samp + 1; + prog = pat + 1; + + /* Copy in the header of the GUS patch */ + + sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; + if (copy_from_user(&((char *) &guspatch)[offs], + &(addr)[offs], sizeof_patch - offs)) { + rc = -EFAULT; + goto free_fail; + } + + if ((i = wavefront_find_free_patch ()) == -1) { + rc = -EBUSY; + goto free_fail; + } + pat->number = i; + pat->subkey = WF_ST_PATCH; + patp = &pat->hdr.p; + + if ((i = wavefront_find_free_sample ()) == -1) { + rc = -EBUSY; + goto free_fail; + } + samp->number = i; + samp->subkey = WF_ST_SAMPLE; + samp->size = guspatch.len; + sampp = &samp->hdr.s; + + prog->number = guspatch.instr_no; + progp = &prog->hdr.pr; + + /* Setup the patch structure */ + + patp->amplitude_bias=guspatch.volume; + patp->portamento=0; + patp->sample_number= samp->number & 0xff; + patp->sample_msb= samp->number >> 8; + patp->pitch_bend= /*12*/ 0; + patp->mono=1; + patp->retrigger=1; + patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; + patp->frequency_bias=0; + patp->restart=0; + patp->reuse=0; + patp->reset_lfo=1; + patp->fm_src2=0; + patp->fm_src1=WF_MOD_MOD_WHEEL; + patp->am_src=WF_MOD_PRESSURE; + patp->am_amount=127; + patp->fc1_mod_amount=0; + patp->fc2_mod_amount=0; + patp->fm_amount1=0; + patp->fm_amount2=0; + patp->envelope1.attack_level=127; + patp->envelope1.decay1_level=127; + patp->envelope1.decay2_level=127; + patp->envelope1.sustain_level=127; + patp->envelope1.release_level=0; + patp->envelope2.attack_velocity=127; + patp->envelope2.attack_level=127; + patp->envelope2.decay1_level=127; + patp->envelope2.decay2_level=127; + patp->envelope2.sustain_level=127; + patp->envelope2.release_level=0; + patp->envelope2.attack_velocity=127; + patp->randomizer=0; + + /* Program for this patch */ + + progp->layer[0].patch_number= pat->number; /* XXX is this right ? */ + progp->layer[0].mute=1; + progp->layer[0].pan_or_mod=1; + progp->layer[0].pan=7; + progp->layer[0].mix_level=127 /* guspatch.volume */; + progp->layer[0].split_type=0; + progp->layer[0].split_point=0; + progp->layer[0].play_below=0; + + for (i = 1; i < 4; i++) { + progp->layer[i].mute=0; + } + + /* Sample data */ + + sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); + + for (base_note=0; + note_to_freq (base_note) < guspatch.base_note; + base_note++); + + if ((guspatch.base_note-note_to_freq(base_note)) + >(note_to_freq(base_note)-guspatch.base_note)) + base_note++; + + printk(KERN_DEBUG "ref freq=%d,base note=%d\n", + guspatch.base_freq, + base_note); + + sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) + + base_note*171); + printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); + sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; + sampp->sampleStartOffset.Fraction=0; + sampp->sampleStartOffset.Integer=0; + sampp->loopStartOffset.Fraction=0; + sampp->loopStartOffset.Integer=guspatch.loop_start + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->loopEndOffset.Fraction=0; + sampp->loopEndOffset.Integer=guspatch.loop_end + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->sampleEndOffset.Fraction=0; + sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); + sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; + sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; + + /* Now ship it down */ + + wavefront_send_sample (samp, + (unsigned short __user *) &(addr)[sizeof_patch], + (guspatch.mode & WAVE_UNSIGNED) ? 1:0); + wavefront_send_patch (pat); + wavefront_send_program (prog); + + /* Now pan as best we can ... use the slave/internal MIDI device + number if it exists (since it talks to the WaveFront), or the + master otherwise. + */ + + if (dev.mididev > 0) { + midi_synth_controller (dev.mididev, guspatch.instr_no, 10, + ((guspatch.panning << 4) > 127) ? + 127 : (guspatch.panning << 4)); + } + rc = 0; + +free_fail: + kfree(samp); + return rc; +} + +static int +wavefront_load_patch (const char __user *addr) + + +{ + wavefront_patch_info header; + + if (copy_from_user (&header, addr, sizeof(wavefront_patch_info) - + sizeof(wavefront_any))) { + printk (KERN_WARNING LOGNAME "bad address for load patch.\n"); + return -EFAULT; + } + + DPRINT (WF_DEBUG_LOAD_PATCH, "download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + if (copy_from_user((unsigned char *) &header.hdr.s, + (unsigned char __user *) header.hdrptr, + sizeof (wavefront_sample))) + return -EFAULT; + + return wavefront_send_sample (&header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + if (copy_from_user(&header.hdr.s, header.hdrptr, + sizeof(wavefront_multisample))) + return -EFAULT; + + return wavefront_send_multisample (&header); + + + case WF_ST_ALIAS: + + if (copy_from_user(&header.hdr.a, header.hdrptr, + sizeof (wavefront_alias))) + return -EFAULT; + + return wavefront_send_alias (&header); + + case WF_ST_DRUM: + if (copy_from_user(&header.hdr.d, header.hdrptr, + sizeof (wavefront_drum))) + return -EFAULT; + + return wavefront_send_drum (&header); + + case WF_ST_PATCH: + if (copy_from_user(&header.hdr.p, header.hdrptr, + sizeof (wavefront_patch))) + return -EFAULT; + + return wavefront_send_patch (&header); + + case WF_ST_PROGRAM: + if (copy_from_user(&header.hdr.pr, header.hdrptr, + sizeof (wavefront_program))) + return -EFAULT; + + return wavefront_send_program (&header); + + default: + printk (KERN_ERR LOGNAME "unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces +***********************************************************************/ + +static void +process_sample_hdr (UCHAR8 *buf) + +{ + wavefront_sample s; + UCHAR8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (int cmd, wavefront_control *wc) + +{ + unsigned char patchnumbuf[2]; + int i; + + DPRINT (WF_DEBUG_CMD, "synth control with " + "cmd 0x%x\n", wc->cmd); + + /* Pre-handling of or for various commands */ + + switch (wc->cmd) { + case WFC_DISABLE_INTERRUPTS: + printk (KERN_INFO LOGNAME "interrupts disabled.\n"); + outb (0x80|0x20, dev.control_port); + dev.interrupts_on = 0; + return 0; + + case WFC_ENABLE_INTERRUPTS: + printk (KERN_INFO LOGNAME "interrupts enabled.\n"); + outb (0x80|0x40|0x20, dev.control_port); + dev.interrupts_on = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc->rbuf[0] = dev.interrupts_on; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + dev.rom_samples_rdonly = wc->wbuf[0]; + wc->status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc->wbuf[0] | (wc->wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + printk (KERN_WARNING LOGNAME "invalid slot ID %d\n", + i); + wc->status = EINVAL; + return 0; + } + wc->rbuf[0] = dev.sample_status[i]; + wc->status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + dev.debug = wc->wbuf[0]; + printk (KERN_INFO LOGNAME "debug = 0x%x\n", dev.debug); + return 0; + + case WFC_FX_IOCTL: + wffx_ioctl ((wavefront_fx_info *) &wc->wbuf[0]); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((UINT32 *) wc->wbuf), patchnumbuf, 2); + memcpy (wc->wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + /* multisamples have to be handled differently, and + cannot be dealt with properly by wavefront_cmd() alone. + */ + wc->status = wavefront_fetch_multisample + ((wavefront_patch_info *) wc->rbuf); + return 0; + + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO LOGNAME "support for sample alias upload " + "being considered.\n"); + wc->status = EINVAL; + return -EINVAL; + } + + wc->status = wavefront_cmd (wc->cmd, wc->rbuf, wc->wbuf); + + /* Post-handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc->status == 0) { + switch (wc->cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + dev.freemem = demunge_int32 (wc->rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc->rbuf, wc->rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc->rbuf, wc->rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc->rbuf); + break; + + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO LOGNAME "support for " + "sample aliases still " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + if (virtual_midi_disable () < 0) { + return -(EIO); + } + break; + + case WFC_VMIDI_ON: + if (virtual_midi_enable () < 0) { + return -(EIO); + } + break; + } + } + + return 0; +} + + +/***********************************************************************/ +/* WaveFront: Linux file system interface (for access via raw synth) */ +/***********************************************************************/ + +static int +wavefront_open (struct inode *inode, struct file *file) +{ + /* XXX fix me */ + dev.opened = file->f_flags; + return 0; +} + +static int +wavefront_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + dev.opened = 0; + dev.debug = 0; + unlock_kernel(); + return 0; +} + +static int +wavefront_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + wavefront_control wc; + int err; + + switch (cmd) { + + case WFCTL_WFCMD: + if (copy_from_user(&wc, (void __user *) arg, sizeof (wc))) + return -EFAULT; + + if ((err = wavefront_synth_control (cmd, &wc)) == 0) { + if (copy_to_user ((void __user *) arg, &wc, sizeof (wc))) + return -EFAULT; + } + + return err; + + case WFCTL_LOAD_SPP: + return wavefront_load_patch ((const char __user *) arg); + + default: + printk (KERN_WARNING LOGNAME "invalid ioctl %#x\n", cmd); + return -(EINVAL); + + } + return 0; +} + +static /*const*/ struct file_operations wavefront_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = wavefront_ioctl, + .open = wavefront_open, + .release = wavefront_release, +}; + + +/***********************************************************************/ +/* WaveFront: OSS installation and support interface */ +/***********************************************************************/ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + +static struct synth_info wavefront_info = +{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, + 0, 32, 0, 0, SYNTH_CAP_INPUT}; + +static int +wavefront_oss_open (int devno, int mode) + +{ + dev.opened = mode; + return 0; +} + +static void +wavefront_oss_close (int devno) + +{ + dev.opened = 0; + dev.debug = 0; + return; +} + +static int +wavefront_oss_ioctl (int devno, unsigned int cmd, void __user * arg) + +{ + wavefront_control wc; + int err; + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if(copy_to_user(arg, &wavefront_info, sizeof (wavefront_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: +// printk (KERN_WARNING LOGNAME "driver cannot reset samples.\n"); + return 0; /* don't force an error */ + + case SNDCTL_SEQ_PERCMODE: + return 0; /* don't force an error */ + + case SNDCTL_SYNTH_MEMAVL: + if ((dev.freemem = wavefront_freemem ()) < 0) { + printk (KERN_ERR LOGNAME "cannot get memory size\n"); + return -EIO; + } else { + return dev.freemem; + } + break; + + case SNDCTL_SYNTH_CONTROL: + if(copy_from_user (&wc, arg, sizeof (wc))) + err = -EFAULT; + else if ((err = wavefront_synth_control (cmd, &wc)) == 0) { + if(copy_to_user (arg, &wc, sizeof (wc))) + err = -EFAULT; + } + + return err; + + default: + return -(EINVAL); + } +} + +static int +wavefront_oss_load_patch (int devno, int format, const char __user *addr, + int offs, int count, int pmgr_flag) +{ + + if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ + if (midi_load_patch == NULL) { + printk (KERN_ERR LOGNAME + "SYSEX not loadable: " + "no midi patch loader!\n"); + return -(EINVAL); + } + + return midi_load_patch (devno, format, addr, + offs, count, pmgr_flag); + + } else if (format == GUS_PATCH) { + return wavefront_load_gus_patch (devno, format, + addr, offs, count, pmgr_flag); + + } else if (format != WAVEFRONT_PATCH) { + printk (KERN_ERR LOGNAME "unknown patch format %d\n", format); + return -(EINVAL); + } + + if (count < sizeof (wavefront_patch_info)) { + printk (KERN_ERR LOGNAME "sample header too short\n"); + return -(EINVAL); + } + + /* "addr" points to a user-space wavefront_patch_info */ + + return wavefront_load_patch (addr); +} + +static struct synth_operations wavefront_operations = +{ + .owner = THIS_MODULE, + .id = "WaveFront", + .info = &wavefront_info, + .midi_dev = 0, + .synth_type = SYNTH_TYPE_SAMPLE, + .synth_subtype = SAMPLE_TYPE_WAVEFRONT, + .open = wavefront_oss_open, + .close = wavefront_oss_close, + .ioctl = wavefront_oss_ioctl, + .kill_note = midi_synth_kill_note, + .start_note = midi_synth_start_note, + .set_instr = midi_synth_set_instr, + .reset = midi_synth_reset, + .load_patch = midi_synth_load_patch, + .aftertouch = midi_synth_aftertouch, + .controller = midi_synth_controller, + .panning = midi_synth_panning, + .bender = midi_synth_bender, + .setup_voice = midi_synth_setup_voice +}; +#endif /* OSS_SUPPORT_SEQ */ + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_STATIC_INSTALL + +static void __init attach_wavefront (struct address_info *hw_config) +{ + (void) install_wavefront (); +} + +static int __init probe_wavefront (struct address_info *hw_config) +{ + return !detect_wavefront (hw_config->irq, hw_config->io_base); +} + +static void __exit unload_wavefront (struct address_info *hw_config) +{ + (void) uninstall_wavefront (); +} + +#endif /* OSS_SUPPORT_STATIC_INSTALL */ + +/***********************************************************************/ +/* WaveFront: Linux modular sound kernel installation interface */ +/***********************************************************************/ + +static irqreturn_t +wavefrontintr(int irq, void *dev_id, struct pt_regs *dummy) +{ + struct wf_config *hw = dev_id; + + /* + Some comments on interrupts. I attempted a version of this + driver that used interrupts throughout the code instead of + doing busy and/or sleep-waiting. Alas, it appears that once + the Motorola firmware is downloaded, the card *never* + generates an RX interrupt. These are successfully generated + during firmware loading, and after that wavefront_status() + reports that an interrupt is pending on the card from time + to time, but it never seems to be delivered to this + driver. Note also that wavefront_status() continues to + report that RX interrupts are enabled, suggesting that I + didn't goof up and disable them by mistake. + + Thus, I stepped back to a prior version of + wavefront_wait(), the only place where this really + matters. Its sad, but I've looked through the code to check + on things, and I really feel certain that the Motorola + firmware prevents RX-ready interrupts. + */ + + if ((wavefront_status() & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { + return IRQ_NONE; + } + + hw->irq_ok = 1; + hw->irq_cnt++; + wake_up_interruptible (&hw->interrupt_sleeper); + return IRQ_HANDLED; +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused +*/ + +static int +wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk (KERN_WARNING LOGNAME "invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +static void +wavefront_should_cause_interrupt (int val, int port, int timeout) + +{ + unsigned long flags; + + /* this will not help on SMP - but at least it compiles */ + spin_lock_irqsave(&lock, flags); + dev.irq_ok = 0; + outb (val,port); + interruptible_sleep_on_timeout (&dev.interrupt_sleeper, timeout); + spin_unlock_irqrestore(&lock,flags); +} + +static int __init wavefront_hw_reset (void) +{ + int bits; + int hwv[2]; + unsigned long irq_mask; + short reported_irq; + + /* IRQ already checked in init_module() */ + + bits = wavefront_interrupt_bits (dev.irq); + + printk (KERN_DEBUG LOGNAME "autodetecting WaveFront IRQ\n"); + + irq_mask = probe_irq_on (); + + outb (0x0, dev.control_port); + outb (0x80 | 0x40 | bits, dev.data_port); + wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, + dev.control_port, + (reset_time*HZ)/100); + + reported_irq = probe_irq_off (irq_mask); + + if (reported_irq != dev.irq) { + if (reported_irq == 0) { + printk (KERN_ERR LOGNAME + "No unassigned interrupts detected " + "after h/w reset\n"); + } else if (reported_irq < 0) { + printk (KERN_ERR LOGNAME + "Multiple unassigned interrupts detected " + "after h/w reset\n"); + } else { + printk (KERN_ERR LOGNAME "autodetected IRQ %d not the " + "value provided (%d)\n", reported_irq, + dev.irq); + } + dev.irq = -1; + return 1; + } else { + printk (KERN_INFO LOGNAME "autodetected IRQ at %d\n", + reported_irq); + } + + if (request_irq (dev.irq, wavefrontintr, + IRQF_DISABLED|IRQF_SHARED, + "wavefront synth", &dev) < 0) { + printk (KERN_WARNING LOGNAME "IRQ %d not available!\n", + dev.irq); + return 1; + } + + /* try reset of port */ + + outb (0x0, dev.control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the + serial MIDI source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + outb (0x80 | 0x40 | bits, dev.data_port); + + /* CONTROL REGISTER + + 0 Host Rx Interrupt Enable (1=Enabled) 0x1 + 1 Unused 0x2 + 2 Unused 0x4 + 3 Unused 0x8 + 4 Host Tx Interrupt Enable 0x10 + 5 Mute (0=Mute; 1=Play) 0x20 + 6 Master Interrupt Enable (1=Enabled) 0x40 + 7 Master Reset (0=Reset; 1=Run) 0x80 + + Take us out of reset, mute output, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. + */ + + wavefront_should_cause_interrupt(0x80|0x40|0x10|0x1, + dev.control_port, + (reset_time*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + dev.interrupts_on = 1; + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + XXX Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(WFC_HARDWARE_VERSION, + dev.data_port, ramcheck_time*HZ); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!wavefront_wait (STAT_CAN_READ)) { + printk (KERN_WARNING LOGNAME + "no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME + "board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME "on-board RAM test failed " + "(bad error code).\n"); + } else { + printk (KERN_WARNING LOGNAME "on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read ()) == -1) { + printk (KERN_WARNING LOGNAME "incorrect h/w response.\n"); + goto gone_bad; + } + + printk (KERN_INFO LOGNAME "hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + if (dev.irq >= 0) { + free_irq (dev.irq, &dev); + dev.irq = -1; + } + return (1); +} + +static int __init detect_wavefront (int irq, int io_base) +{ + unsigned char rbuf[4], wbuf[4]; + + /* TB docs say the device takes up 8 ports, but we know that + if there is an FX device present (i.e. a Tropez+) it really + consumes 16. + */ + + if (!request_region (io_base, 16, "wavfront")) { + printk (KERN_ERR LOGNAME "IO address range 0x%x - 0x%x " + "already in use - ignored\n", dev.base, + dev.base+15); + return -1; + } + + dev.irq = irq; + dev.base = io_base; + dev.israw = 0; + dev.debug = debug_default; + dev.interrupts_on = 0; + dev.irq_cnt = 0; + dev.rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ + + if (wavefront_cmd (WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + dev.fw_version[0] = rbuf[0]; + dev.fw_version[1] = rbuf[1]; + printk (KERN_INFO LOGNAME + "firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (wavefront_cmd (WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + dev.hw_version[0] = rbuf[0]; + dev.hw_version[1] = rbuf[1]; + } else { + printk (KERN_WARNING LOGNAME "not raw, but no " + "hardware version!\n"); + release_region (io_base, 16); + return 0; + } + + if (!wf_raw) { + /* will re-acquire region in install_wavefront() */ + release_region (io_base, 16); + return 1; + } else { + printk (KERN_INFO LOGNAME + "reloading firmware anyway.\n"); + dev.israw = 1; + } + + } else { + + dev.israw = 1; + printk (KERN_INFO LOGNAME + "no response to firmware probe, assume raw.\n"); + + } + + init_waitqueue_head (&dev.interrupt_sleeper); + + if (wavefront_hw_reset ()) { + printk (KERN_WARNING LOGNAME "hardware reset failed\n"); + release_region (io_base, 16); + return 0; + } + + /* Check for FX device, present only on Tropez+ */ + + dev.has_fx = (detect_wffx () == 0); + + /* will re-acquire region in install_wavefront() */ + release_region (io_base, 16); + return 1; +} + +#include "os.h" +#include +#include +#include +#include + + +static int +wavefront_download_firmware (char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + mm_segment_t fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = sys_open (path, 0, 0)) < 0) { + printk (KERN_WARNING LOGNAME "Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = sys_read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + printk (KERN_ERR LOGNAME "firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (sys_read (fd, section, section_length) != section_length) { + printk (KERN_ERR LOGNAME "firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (wavefront_write (WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (wavefront_write (section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (STAT_CAN_READ)) { + + if ((c = inb (dev.data_port)) != WF_ACK) { + + printk (KERN_ERR LOGNAME "download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } + + } else { + printk (KERN_ERR LOGNAME "time out for firmware ACK.\n"); + goto failure; + } + + } + + sys_close (fd); + set_fs (fs); + return 0; + + failure: + sys_close (fd); + set_fs (fs); + printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); + return 1; +} + +static int __init wavefront_config_midi (void) +{ + unsigned char rbuf[4], wbuf[4]; + + if (detect_wf_mpu (dev.irq, dev.base) < 0) { + printk (KERN_WARNING LOGNAME + "could not find working MIDI device\n"); + return -1; + } + + if ((dev.mididev = install_wf_mpu ()) < 0) { + printk (KERN_WARNING LOGNAME + "MIDI interfaces not configured\n"); + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (wavefront_cmd (WFC_MISYNTH_ON, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot enable MIDI-IN to synth routing.\n"); + /* XXX error ? */ + } + + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + /* Get the regular MIDI patch loading function, so we can + use it if we ever get handed a SYSEX patch. This is + unlikely, because its so damn slow, but we may as well + leave this functionality from maui.c behind, since it + could be useful for sequencer applications that can + only use MIDI to do patch loading. + */ + + if (midi_devs[dev.mididev]->converter != NULL) { + midi_load_patch = midi_devs[dev.mididev]->converter->load_patch; + midi_devs[dev.mididev]->converter->load_patch = + &wavefront_oss_load_patch; + } + +#endif /* OSS_SUPPORT_SEQ */ + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (wavefront_cmd (WFC_VMIDI_OFF, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + if ((dev.ext_mididev = virtual_midi_enable ()) < 0) { + printk (KERN_WARNING LOGNAME "no virtual MIDI access.\n"); + } else { + if (wavefront_cmd (WFC_VMIDI_ON, rbuf, wbuf)) { + printk (KERN_WARNING LOGNAME + "cannot enable virtual MIDI mode.\n"); + virtual_midi_disable (); + } + } + + return 0; +} + +static int __init wavefront_do_reset (int atboot) +{ + char voices[1]; + + if (!atboot && wavefront_hw_reset ()) { + printk (KERN_WARNING LOGNAME "hw reset failed.\n"); + goto gone_bad; + } + + if (dev.israw) { + if (wavefront_download_firmware (ospath)) { + goto gone_bad; + } + + dev.israw = 0; + + /* Wait for the OS to get running. The protocol for + this is non-obvious, and was determined by + using port-IO tracing in DOSemu and some + experimentation here. + + Rather than using timed waits, use interrupts creatively. + */ + + wavefront_should_cause_interrupt (WFC_NOOP, + dev.data_port, + (osrun_time*HZ)); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (WFC_NOOP, + dev.data_port, (10*HZ)); + + if (!dev.irq_ok) { + printk (KERN_WARNING LOGNAME + "no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + in effect. + */ + + outb (0x80|0x40, dev.control_port); + + /* No need for the IRQ anymore */ + + free_irq (dev.irq, &dev); + + } + + if (dev.has_fx && fx_raw) { + wffx_init (); + } + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((dev.freemem = wavefront_freemem ()) < 0) { + goto gone_bad; + } + + printk (KERN_INFO LOGNAME "available DRAM %dk\n", dev.freemem / 1024); + + if (wavefront_write (0xf0) || + wavefront_write (1) || + (wavefront_read () < 0)) { + dev.debug = 0; + printk (KERN_WARNING LOGNAME "MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (wavefront_cmd (WFC_SET_NVOICES, NULL, voices)) { + printk (KERN_WARNING LOGNAME + "cannot set number of voices to 32.\n"); + goto gone_bad; + } + + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, dev.control_port); + dev.interrupts_on = 0; + if (dev.irq >= 0) { + free_irq (dev.irq, &dev); + } + return 1; +} + +static int __init wavefront_init (int atboot) +{ + int samples_are_from_rom; + + if (dev.israw) { + samples_are_from_rom = 1; + } else { + /* XXX is this always true ? */ + samples_are_from_rom = 0; + } + + if (dev.israw || fx_raw) { + if (wavefront_do_reset (atboot)) { + return -1; + } + } + + wavefront_get_sample_status (samples_are_from_rom); + wavefront_get_program_status (); + wavefront_get_patch_status (); + + /* Start normal operation: unreset, master interrupt enabled, no mute + */ + + outb (0x80|0x40|0x20, dev.control_port); + + return (0); +} + +static int __init install_wavefront (void) +{ + if (!request_region (dev.base+2, 6, "wavefront synth")) + return -1; + + if (dev.has_fx) { + if (!request_region (dev.base+8, 8, "wavefront fx")) { + release_region (dev.base+2, 6); + return -1; + } + } + + if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) { + printk (KERN_ERR LOGNAME "cannot register raw synth\n"); + goto err_out; + } + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + if ((dev.oss_dev = sound_alloc_synthdev()) == -1) { + printk (KERN_ERR LOGNAME "Too many sequencers\n"); + /* FIXME: leak: should unregister sound synth */ + goto err_out; + } else { + synth_devs[dev.oss_dev] = &wavefront_operations; + } +#endif /* OSS_SUPPORT_SEQ */ + + if (wavefront_init (1) < 0) { + printk (KERN_WARNING LOGNAME "initialization failed.\n"); + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + sound_unload_synthdev (dev.oss_dev); +#endif /* OSS_SUPPORT_SEQ */ + + goto err_out; + } + + if (wavefront_config_midi ()) { + printk (KERN_WARNING LOGNAME "could not initialize MIDI.\n"); + } + + return dev.oss_dev; + +err_out: + release_region (dev.base+2, 6); + if (dev.has_fx) + release_region (dev.base+8, 8); + return -1; +} + +static void __exit uninstall_wavefront (void) +{ + /* the first two i/o addresses are freed by the wf_mpu code */ + release_region (dev.base+2, 6); + + if (dev.has_fx) { + release_region (dev.base+8, 8); + } + + unregister_sound_synth (dev.synth_dev); + +#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ + sound_unload_synthdev (dev.oss_dev); +#endif /* OSS_SUPPORT_SEQ */ + uninstall_wf_mpu (); +} + +/***********************************************************************/ +/* WaveFront FX control */ +/***********************************************************************/ + +#include "yss225.h" + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static int +wffx_idle (void) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (dev.fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + printk (KERN_ERR LOGNAME "FX device never idle.\n"); + return 0; + } + + return (1); +} + +int __init detect_wffx (void) +{ + /* This is a crude check, but its the best one I have for now. + Certainly on the Maui and the Tropez, wffx_idle() will + report "never idle", which suggests that this test should + work OK. + */ + + if (inb (dev.fx_status) & 0x80) { + printk (KERN_INFO LOGNAME "Hmm, probably a Maui or Tropez.\n"); + return -1; + } + + return 0; +} + +static void +wffx_mute (int onoff) + +{ + if (!wffx_idle()) { + return; + } + + outb (onoff ? 0x02 : 0x00, dev.fx_op); +} + +static int +wffx_memset (int page, + int addr, int cnt, unsigned short *data) +{ + if (page < 0 || page > 7) { + printk (KERN_ERR LOGNAME "FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + printk (KERN_ERR LOGNAME "FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (page, dev.fx_dsp_page); + outb (addr, dev.fx_dsp_addr); + outb ((data[0] >> 8), dev.fx_dsp_msb); + outb ((data[0] & 0xff), dev.fx_dsp_lsb); + + printk (KERN_INFO LOGNAME "FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (page, dev.fx_dsp_page); + outb (addr, dev.fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), dev.fx_dsp_msb); + outb ((data[i] & 0xff), dev.fx_dsp_lsb); + if (!wffx_idle ()) { + break; + } + } + + if (i != cnt) { + printk (KERN_WARNING LOGNAME + "FX memset " + "(0x%x, 0x%x, %p, %d) incomplete\n", + page, addr, data, cnt); + return -(EIO); + } + } + + return 0; +} + +static int +wffx_ioctl (wavefront_fx_info *r) + +{ + unsigned short page_data[256]; + unsigned short *pd; + + switch (r->request) { + case WFFX_MUTE: + wffx_mute (r->data[0]); + return 0; + + case WFFX_MEMSET: + + if (r->data[2] <= 0) { + printk (KERN_ERR LOGNAME "cannot write " + "<= 0 bytes to FX\n"); + return -(EINVAL); + } else if (r->data[2] == 1) { + pd = (unsigned short *) &r->data[3]; + } else { + if (r->data[2] > sizeof (page_data)) { + printk (KERN_ERR LOGNAME "cannot write " + "> 255 bytes to FX\n"); + return -(EINVAL); + } + if (copy_from_user(page_data, + (unsigned char __user *)r->data[3], + r->data[2])) + return -EFAULT; + pd = page_data; + } + + return wffx_memset (r->data[0], /* page */ + r->data[1], /* addr */ + r->data[2], /* cnt */ + pd); + + default: + printk (KERN_WARNING LOGNAME + "FX: ioctl %d not yet supported\n", + r->request); + return -(EINVAL); + } +} + +/* YSS225 initialization. + + This code was developed using DOSEMU. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEMU enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. Its really pretty weird. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + +static int __init wffx_init (void) +{ + int i; + int j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle ()) { + return (-1); + } + + outb (i, dev.fx_mod_addr); + outb (0x0, dev.fx_mod_data); + } + } + + if (!wffx_idle()) return (-1); + outb (0x02, dev.fx_op); /* mute on */ + + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x44, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x42, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x43, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x7c, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x7e, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x46, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x49, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x47, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x4a, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle ()) { + return (-1); + } + + outb (i, dev.fx_mod_addr); + outb (0x0, dev.fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x00, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], dev.fx_dsp_msb); + outb (page_zero[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x01, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], dev.fx_dsp_msb); + outb (page_one[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x02, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x03, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x04, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x06, dev.fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], dev.fx_dsp_addr); + outb (page_six[i+1], dev.fx_dsp_msb); + outb (page_six[i+2], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x07, dev.fx_dsp_page); + outb (0x00, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], dev.fx_dsp_msb); + outb (page_seven[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev.fx_mod_addr); + outb (i, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0x02, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, dev.fx_mod_addr); + outb (0xff, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x1e, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, dev.fx_mod_addr); + outb (0xff, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x2e, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x3f, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x4e, dev.fx_mod_addr); + outb (0x0e, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0x4f, dev.fx_mod_addr); + outb (0x0e, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x6c, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6d, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6e, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + outb (0x6f, dev.fx_mod_addr); + outb (0x40, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, dev.fx_mod_addr); + outb (0xc0, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0xde, dev.fx_mod_addr); + outb (0x10, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xdf, dev.fx_mod_addr); + outb (0x10, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, dev.fx_mod_addr); + outb (0x00, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, dev.fx_mod_addr); + outb (i, dev.fx_mod_data); + outb (0x02, dev.fx_mod_addr); + outb (0x01, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + outb (0x02, dev.fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], dev.fx_dsp_page); + outb (coefficients[i+1], dev.fx_dsp_addr); + outb (coefficients[i+2], dev.fx_dsp_msb); + outb (coefficients[i+3], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wffx_idle()) return (-1); + outb (0x1e, dev.fx_mod_addr); + outb (0x14, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xde, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + if (!wffx_idle()) return (-1); + outb (0xdf, dev.fx_mod_addr); + outb (0x20, dev.fx_mod_data); + + /* some more coefficients */ + + if (!wffx_idle()) return (-1); + outb (0x06, dev.fx_dsp_page); + outb (0x78, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x40, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x03, dev.fx_dsp_addr); + outb (0x0f, dev.fx_dsp_msb); + outb (0xff, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x0b, dev.fx_dsp_addr); + outb (0x0f, dev.fx_dsp_msb); + outb (0xff, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x02, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x0a, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x46, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + outb (0x07, dev.fx_dsp_page); + outb (0x49, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x00, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], dev.fx_dsp_msb); + outb (page_zero_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x01, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], dev.fx_dsp_msb); + outb (page_one_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + if (!wffx_idle()) return (-1); + if (!wffx_idle()) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x02, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x03, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x04, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x06, dev.fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, dev.fx_dsp_addr); + outb (0x00, dev.fx_dsp_msb); + outb (0x00, dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, dev.fx_lcr); + outb (0x07, dev.fx_dsp_page); + outb (0x10, dev.fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], dev.fx_dsp_msb); + outb (page_seven_v2[i+1], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], dev.fx_mod_addr); + outb (mod_v2[i+1], dev.fx_mod_data); + if (!wffx_idle()) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], dev.fx_dsp_page); + outb (coefficients2[i+1], dev.fx_dsp_addr); + outb (coefficients2[i+2], dev.fx_dsp_msb); + outb (coefficients2[i+3], dev.fx_dsp_lsb); + if (!wffx_idle()) return (-1); + } + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, dev.fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, dev.fx_dsp_addr); + outb (coefficients3[i], dev.fx_dsp_msb); + outb (coefficients3[i+1], dev.fx_dsp_lsb); + } + + outb (0x00, dev.fx_op); /* mute off */ + if (!wffx_idle()) return (-1); + + return (0); +} + +static int io = -1; +static int irq = -1; + +MODULE_AUTHOR ("Paul Barton-Davis "); +MODULE_DESCRIPTION ("Turtle Beach WaveFront Linux Driver"); +MODULE_LICENSE("GPL"); +module_param (io, int, 0); +module_param (irq, int, 0); + +static int __init init_wavfront (void) +{ + printk ("Turtle Beach WaveFront Driver\n" + "Copyright (C) by Hannu Solvainen, " + "Paul Barton-Davis 1993-1998.\n"); + + /* XXX t'would be lovely to ask the CS4232 for these values, eh ? */ + + if (io == -1 || irq == -1) { + printk (KERN_INFO LOGNAME "irq and io options must be set.\n"); + return -EINVAL; + } + + if (wavefront_interrupt_bits (irq) < 0) { + printk (KERN_INFO LOGNAME + "IRQ must be 9, 5, 12 or 15 (not %d)\n", irq); + return -ENODEV; + } + + if (detect_wavefront (irq, io) < 0) { + return -ENODEV; + } + + if (install_wavefront () < 0) { + return -EIO; + } + + return 0; +} + +static void __exit cleanup_wavfront (void) +{ + uninstall_wavefront (); +} + +module_init(init_wavfront); +module_exit(cleanup_wavfront); diff --git a/trunk/sound/oss/wf_midi.c b/trunk/sound/oss/wf_midi.c new file mode 100644 index 000000000000..75c0c143a759 --- /dev/null +++ b/trunk/sound/oss/wf_midi.c @@ -0,0 +1,880 @@ +/* + * sound/oss/wf_midi.c + * + * The low level driver for the WaveFront ICS2115 MIDI interface(s) + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez Plus. This code has nothing + * to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct + * midi devices (/dev/midiNN and /dev/midiNN+1) to be used + * completely independently, giving 32 channels of MIDI routing, + * 16 to the WaveFront synth and 16 to the external MIDI bus. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +/* + * Copyright (C) by Paul Barton-Davis 1998 + * Some portions of this file are derived from work that is: + * + * CopyriGht (C) by Hannu Savolainen 1993-1996 + * + * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + +#include +#include +#include +#include "sound_config.h" + +#include + +#ifdef MODULE + +struct wf_mpu_config { + int base; +#define DATAPORT(d) (d)->base +#define COMDPORT(d) (d)->base+1 +#define STATPORT(d) (d)->base+1 + + int irq; + int opened; + int devno; + int synthno; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + + void (*inputintr) (int dev, unsigned char data); + char isvirtual; /* do virtual I/O stuff */ +}; + +static struct wf_mpu_config devs[2]; +static struct wf_mpu_config *phys_dev = &devs[0]; +static struct wf_mpu_config *virt_dev = &devs[1]; + +static void start_uart_mode (void); +static DEFINE_SPINLOCK(lock); + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define UART_MODE_ON 0x3F + +static inline int wf_mpu_status (void) +{ + return inb (STATPORT (phys_dev)); +} + +static inline int input_avail (void) +{ + return !(wf_mpu_status() & INPUT_AVAIL); +} + +static inline int output_ready (void) +{ + return !(wf_mpu_status() & OUTPUT_READY); +} + +static inline int read_data (void) +{ + return inb (DATAPORT (phys_dev)); +} + +static inline void write_data (unsigned char byte) +{ + outb (byte, DATAPORT (phys_dev)); +} + +/* + * States for the input scanner (should be in dev_table.h) + */ + +#define MST_SYSMSG 100 /* System message (sysx etc). */ +#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define MST_SONGSEL 103 /* Song select */ +#define MST_SONGPOS 104 /* Song position pointer */ +#define MST_TIMED 105 /* Leading timing byte rcvd */ + +/* buffer space check for input scanner */ + +#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ +{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ + mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} + +static unsigned char len_tab[] = /* # of data bytes following a status + */ +{ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ +}; + +static int +wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) + +{ + struct midi_input_info *mi = &midi_devs[devno]->in_info; + + switch (mi->m_state) { + case MST_INIT: + switch (midic) { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + break; + + case 0xfd: + /* XXX do something useful with this. If there is + an external MIDI timer (e.g. a hardware sequencer, + a useful timer can be derived ... + + For now, no timer support. + */ + break; + + case 0xfe: + return MPU_ACK; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + break; + + case 0xf9: + break; + + case 0xff: + mi->m_state = MST_SYSMSG; + break; + + default: + if (midic <= 0xef) { + mi->m_state = MST_TIMED; + } + else + printk (KERN_ERR " ", + midic); + } + break; + + case MST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + mi->m_state = MST_DATA; + + if (msg < 8) { /* Data byte */ + + msg = ((int) (mi->m_prev_status & 0xf0) >> 4); + msg -= 8; + mi->m_left = len_tab[msg] - 1; + + mi->m_ptr = 2; + mi->m_buf[0] = mi->m_prev_status; + mi->m_buf[1] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + } else if (msg == 0xf) { /* MPU MARK */ + + mi->m_state = MST_INIT; + + switch (midic) { + case 0xf8: + break; + + case 0xf9: + break; + + case 0xfc: + break; + + default: + break; + } + } else { + mi->m_prev_status = midic; + msg -= 8; + mi->m_left = len_tab[msg]; + + mi->m_ptr = 1; + mi->m_buf[0] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + } + } + break; + + case MST_SYSMSG: + switch (midic) { + case 0xf0: + mi->m_state = MST_SYSEX; + break; + + case 0xf1: + mi->m_state = MST_MTC; + break; + + case 0xf2: + mi->m_state = MST_SONGPOS; + mi->m_ptr = 0; + break; + + case 0xf3: + mi->m_state = MST_SONGSEL; + break; + + case 0xf6: + mi->m_state = MST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xfA: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFB: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFC: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFE: + /* active sensing */ + mi->m_state = MST_INIT; + break; + + case 0xff: + mi->m_state = MST_INIT; + break; + + default: + printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); + mi->m_state = MST_INIT; + } + break; + + case MST_MTC: + mi->m_state = MST_INIT; + break; + + case MST_SYSEX: + if (midic == 0xf7) { + mi->m_state = MST_INIT; + } else { + /* XXX fix me */ + } + break; + + case MST_SONGPOS: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if (mi->m_ptr == 2) { + mi->m_state = MST_INIT; + mi->m_ptr = 0; + /* XXX need ext MIDI timer support */ + } + break; + + case MST_DATA: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if ((--mi->m_left) <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + break; + + default: + printk (KERN_ERR "Bad state %d ", mi->m_state); + mi->m_state = MST_INIT; + } + + return 1; +} + +static irqreturn_t +wf_mpuintr(int irq, void *dev_id, struct pt_regs *dummy) + +{ + struct wf_mpu_config *physical_dev = dev_id; + static struct wf_mpu_config *input_dev; + struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info; + int n; + + if (!input_avail()) { /* not for us */ + return IRQ_NONE; + } + + if (mi->m_busy) + return IRQ_HANDLED; + spin_lock(&lock); + mi->m_busy = 1; + + if (!input_dev) { + input_dev = physical_dev; + } + + n = 50; /* XXX why ? */ + + do { + unsigned char c = read_data (); + + if (phys_dev->isvirtual) { + + if (c == WF_EXTERNAL_SWITCH) { + input_dev = virt_dev; + continue; + } else if (c == WF_INTERNAL_SWITCH) { + input_dev = phys_dev; + continue; + } /* else just leave it as it is */ + + } else { + input_dev = phys_dev; + } + + if (input_dev->mode == MODE_SYNTH) { + + wf_mpu_input_scanner (input_dev->devno, + input_dev->synthno, c); + + } else if (input_dev->opened & OPEN_READ) { + + if (input_dev->inputintr) { + input_dev->inputintr (input_dev->devno, c); + } + } + + } while (input_avail() && n-- > 0); + + mi->m_busy = 0; + spin_unlock(&lock); + return IRQ_HANDLED; +} + +static int +wf_mpu_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) + ) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return -(ENXIO); + + if (phys_dev->devno == dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return -(EINVAL); + } + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_MIDI; + devc->opened = mode; + devc->synthno = 0; + + devc->inputintr = input; + return 0; +} + +static void +wf_mpu_close (int dev) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return; + + if (phys_dev->devno == dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return; + } + + devc->mode = 0; + devc->inputintr = NULL; + devc->opened = 0; +} + +static int +wf_mpu_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + static int lastoutdev = -1; + unsigned char switchch; + + if (phys_dev->isvirtual && lastoutdev != dev) { + + if (dev == phys_dev->devno) { + switchch = WF_INTERNAL_SWITCH; + } else if (dev == virt_dev->devno) { + switchch = WF_EXTERNAL_SWITCH; + } else { + printk (KERN_ERR "WF-MPU: bad device number %d", dev); + return (0); + } + + /* XXX fix me */ + + for (timeout = 30000; timeout > 0 && !output_ready (); + timeout--); + + spin_lock_irqsave(&lock,flags); + + if (!output_ready ()) { + printk (KERN_WARNING "WF-MPU: Send switch " + "byte timeout\n"); + spin_unlock_irqrestore(&lock,flags); + return 0; + } + + write_data (switchch); + spin_unlock_irqrestore(&lock,flags); + } + + lastoutdev = dev; + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + /* XXX fix me */ + + for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); + + spin_lock_irqsave(&lock,flags); + if (!output_ready ()) { + spin_unlock_irqrestore(&lock,flags); + printk (KERN_WARNING "WF-MPU: Send data timeout\n"); + return 0; + } + + write_data (midi_byte); + spin_unlock_irqrestore(&lock,flags); + + return 1; +} + +static inline int wf_mpu_start_read (int dev) { + return 0; +} + +static inline int wf_mpu_end_read (int dev) { + return 0; +} + +static int wf_mpu_ioctl (int dev, unsigned cmd, void __user *arg) +{ + printk (KERN_WARNING + "WF-MPU: Intelligent mode not supported by hardware.\n"); + return -(EINVAL); +} + +static int wf_mpu_buffer_status (int dev) +{ + return 0; +} + +static struct synth_operations wf_mpu_synth_operations[2]; +static struct midi_operations wf_mpu_midi_operations[2]; + +static struct midi_operations wf_mpu_midi_proto = +{ + .owner = THIS_MODULE, + .info = {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + .in_info = {0}, /* in_info */ + .open = wf_mpu_open, + .close = wf_mpu_close, + .ioctl = wf_mpu_ioctl, + .outputc = wf_mpu_out, + .start_read = wf_mpu_start_read, + .end_read = wf_mpu_end_read, + .buffer_status = wf_mpu_buffer_status, +}; + +static struct synth_info wf_mpu_synth_info_proto = +{"WaveFront MPU-401 interface", 0, + SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info wf_mpu_synth_info[2]; + +static int +wf_mpu_synth_ioctl (int dev, unsigned int cmd, void __user *arg) +{ + int midi_dev; + int index; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) + return -(ENXIO); + + if (midi_dev == phys_dev->devno) { + index = 0; + } else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) { + index = 1; + } else { + return -(EINVAL); + } + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + if (copy_to_user(arg, + &wf_mpu_synth_info[index], + sizeof (struct synth_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + default: + return -EINVAL; + } +} + +static int +wf_mpu_synth_open (int dev, int mode) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { + return -(ENXIO); + } + + if (phys_dev->devno == midi_dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return -(EINVAL); + } + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_SYNTH; + devc->synthno = dev; + devc->opened = mode; + devc->inputintr = NULL; + return 0; +} + +static void +wf_mpu_synth_close (int dev) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (phys_dev->devno == midi_dev) { + devc = phys_dev; + } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { + devc = virt_dev; + } else { + printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); + return; + } + + devc->inputintr = NULL; + devc->opened = 0; + devc->mode = 0; +} + +#define _MIDI_SYNTH_C_ +#define MIDI_SYNTH_NAME "WaveFront (MIDI)" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations wf_mpu_synth_proto = +{ + .owner = THIS_MODULE, + .id = "WaveFront (ICS2115)", + .info = NULL, /* info field, filled in during configuration */ + .midi_dev = 0, /* MIDI dev XXX should this be -1 ? */ + .synth_type = SYNTH_TYPE_MIDI, + .synth_subtype = SAMPLE_TYPE_WAVEFRONT, + .open = wf_mpu_synth_open, + .close = wf_mpu_synth_close, + .ioctl = wf_mpu_synth_ioctl, + .kill_note = midi_synth_kill_note, + .start_note = midi_synth_start_note, + .set_instr = midi_synth_set_instr, + .reset = midi_synth_reset, + .hw_control = midi_synth_hw_control, + .load_patch = midi_synth_load_patch, + .aftertouch = midi_synth_aftertouch, + .controller = midi_synth_controller, + .panning = midi_synth_panning, + .bender = midi_synth_bender, + .setup_voice = midi_synth_setup_voice, + .send_sysex = midi_synth_send_sysex +}; + +static int +config_wf_mpu (struct wf_mpu_config *dev) + +{ + int is_external; + char *name; + int index; + + if (dev == phys_dev) { + name = "WaveFront internal MIDI"; + is_external = 0; + index = 0; + memcpy ((char *) &wf_mpu_synth_operations[index], + (char *) &wf_mpu_synth_proto, + sizeof (struct synth_operations)); + } else { + name = "WaveFront external MIDI"; + is_external = 1; + index = 1; + /* no synth operations for an external MIDI interface */ + } + + memcpy ((char *) &wf_mpu_synth_info[dev->devno], + (char *) &wf_mpu_synth_info_proto, + sizeof (struct synth_info)); + + strcpy (wf_mpu_synth_info[index].name, name); + + wf_mpu_synth_operations[index].midi_dev = dev->devno; + wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index]; + + memcpy ((char *) &wf_mpu_midi_operations[index], + (char *) &wf_mpu_midi_proto, + sizeof (struct midi_operations)); + + if (is_external) { + wf_mpu_midi_operations[index].converter = NULL; + } else { + wf_mpu_midi_operations[index].converter = + &wf_mpu_synth_operations[index]; + } + + strcpy (wf_mpu_midi_operations[index].info.name, name); + + midi_devs[dev->devno] = &wf_mpu_midi_operations[index]; + midi_devs[dev->devno]->in_info.m_busy = 0; + midi_devs[dev->devno]->in_info.m_state = MST_INIT; + midi_devs[dev->devno]->in_info.m_ptr = 0; + midi_devs[dev->devno]->in_info.m_left = 0; + midi_devs[dev->devno]->in_info.m_prev_status = 0; + + devs[index].opened = 0; + devs[index].mode = 0; + + return (0); +} + +int virtual_midi_enable (void) + +{ + if ((virt_dev->devno < 0) && + (virt_dev->devno = sound_alloc_mididev()) == -1) { + printk (KERN_ERR + "WF-MPU: too many midi devices detected\n"); + return -1; + } + + config_wf_mpu (virt_dev); + + phys_dev->isvirtual = 1; + return virt_dev->devno; +} + +int +virtual_midi_disable (void) + +{ + unsigned long flags; + + spin_lock_irqsave(&lock,flags); + + wf_mpu_close (virt_dev->devno); + /* no synth on virt_dev, so no need to call wf_mpu_synth_close() */ + phys_dev->isvirtual = 0; + + spin_unlock_irqrestore(&lock,flags); + + return 0; +} + +int __init detect_wf_mpu (int irq, int io_base) +{ + if (!request_region(io_base, 2, "wavefront midi")) { + printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", + io_base); + return -1; + } + + phys_dev->base = io_base; + phys_dev->irq = irq; + phys_dev->devno = -1; + virt_dev->devno = -1; + + return 0; +} + +int __init install_wf_mpu (void) +{ + if ((phys_dev->devno = sound_alloc_mididev()) < 0){ + + printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); + release_region(phys_dev->base, 2); + return -1; + } + + phys_dev->isvirtual = 0; + + if (config_wf_mpu (phys_dev)) { + + printk (KERN_WARNING + "WF-MPU: configuration for MIDI device %d failed\n", + phys_dev->devno); + sound_unload_mididev (phys_dev->devno); + + } + + /* OK, now we're configured to handle an interrupt ... */ + + if (request_irq (phys_dev->irq, wf_mpuintr, IRQF_DISABLED|IRQF_SHARED, + "wavefront midi", phys_dev) < 0) { + + printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", + phys_dev->irq); + return -1; + + } + + /* This being a WaveFront (ICS-2115) emulated MPU-401, we have + to switch it into UART (dumb) mode, because otherwise, it + won't do anything at all. + */ + + start_uart_mode (); + + return phys_dev->devno; +} + +void +uninstall_wf_mpu (void) + +{ + release_region (phys_dev->base, 2); + free_irq (phys_dev->irq, phys_dev); + sound_unload_mididev (phys_dev->devno); + + if (virt_dev->devno >= 0) { + sound_unload_mididev (virt_dev->devno); + } +} + +static void +start_uart_mode (void) + +{ + int ok, i; + unsigned long flags; + + spin_lock_irqsave(&lock,flags); + + /* XXX fix me */ + + for (i = 0; i < 30000 && !output_ready (); i++); + + outb (UART_MODE_ON, COMDPORT(phys_dev)); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail ()) { + if (read_data () == MPU_ACK) { + ok = 1; + } + } + } + + spin_unlock_irqrestore(&lock,flags); +} +#endif diff --git a/trunk/sound/oss/ymfpci.c b/trunk/sound/oss/ymfpci.c new file mode 100644 index 000000000000..4c89af9ea03c --- /dev/null +++ b/trunk/sound/oss/ymfpci.c @@ -0,0 +1,2691 @@ +/* + * Copyright 1999 Jaroslav Kysela + * Copyright 2000 Alan Cox + * Copyright 2001 Kai Germaschewski + * Copyright 2002 Pete Zaitcev + * + * Yamaha YMF7xx driver. + * + * This code is a result of high-speed collision + * between ymfpci.c of ALSA and cs46xx.c of Linux. + * -- Pete Zaitcev ; 2000/09/18 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * TODO: + * - Use P44Slot for 44.1 playback (beware of idle buzzing in P44Slot). + * - 96KHz playback for DVD - use pitch of 2.0. + * - Retain DMA buffer on close, do not wait the end of frame. + * - Resolve XXX tagged questions. + * - Cannot play 5133Hz. + * - 2001/01/07 Consider if we can remove voice_lock, like so: + * : Allocate/deallocate voices in open/close under semafore. + * : We access voices in interrupt, that only for pcms that open. + * voice_lock around playback_prepare closes interrupts for insane duration. + * - Revisit the way voice_alloc is done - too confusing, overcomplicated. + * Should support various channel types, however. + * - Remove prog_dmabuf from read/write, leave it in open. + * - 2001/01/07 Replace the OPL3 part of CONFIG_SOUND_YMFPCI_LEGACY code with + * native synthesizer through a playback slot. + * - 2001/11/29 ac97_save_state + * Talk to Kai to remove ac97_save_state before it's too late! + * - Second AC97 + * - Restore S/PDIF - Toshibas have it. + * + * Kai used pci_alloc_consistent for DMA buffer, which sounds a little + * unconventional. However, given how small our fragments can be, + * a little uncached access is perhaps better than endless flushing. + * On i386 and other I/O-coherent architectures pci_alloc_consistent + * is entirely harmless. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY +# include "sound_config.h" +# include "mpu401.h" +#endif +#include "ymfpci.h" + +/* + * I do not believe in debug levels as I never can guess what + * part of the code is going to be problematic in the future. + * Don't forget to run your klogd with -c 8. + * + * Example (do not remove): + * #define YMFDBG(fmt, arg...) do{ printk(KERN_DEBUG fmt, ##arg); }while(0) + */ +#define YMFDBGW(fmt, arg...) /* */ /* write counts */ +#define YMFDBGI(fmt, arg...) /* */ /* interrupts */ +#define YMFDBGX(fmt, arg...) /* */ /* ioctl */ + +static int ymf_playback_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); +static void ymf_capture_trigger(ymfpci_t *unit, struct ymf_pcm *ypcm, int cmd); +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice); +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank); +static int ymf_playback_prepare(struct ymf_state *state); +static int ymf_capture_prepare(struct ymf_state *state); +static struct ymf_state *ymf_state_alloc(ymfpci_t *unit); + +static void ymfpci_aclink_reset(struct pci_dev * pci); +static void ymfpci_disable_dsp(ymfpci_t *unit); +static void ymfpci_download_image(ymfpci_t *codec); +static void ymf_memload(ymfpci_t *unit); + +static DEFINE_SPINLOCK(ymf_devs_lock); +static LIST_HEAD(ymf_devs); + +/* + * constants + */ + +static struct pci_device_id ymf_id_tbl[] = { +#define DEV(dev, data) \ + { PCI_VENDOR_ID_YAMAHA, dev, PCI_ANY_ID, PCI_ANY_ID, 0, 0, \ + (unsigned long)data } + DEV (PCI_DEVICE_ID_YAMAHA_724, "YMF724"), + DEV (PCI_DEVICE_ID_YAMAHA_724F, "YMF724F"), + DEV (PCI_DEVICE_ID_YAMAHA_740, "YMF740"), + DEV (PCI_DEVICE_ID_YAMAHA_740C, "YMF740C"), + DEV (PCI_DEVICE_ID_YAMAHA_744, "YMF744"), + DEV (PCI_DEVICE_ID_YAMAHA_754, "YMF754"), +#undef DEV + { } +}; +MODULE_DEVICE_TABLE(pci, ymf_id_tbl); + +/* + * common I/O routines + */ + +static inline void ymfpci_writeb(ymfpci_t *codec, u32 offset, u8 val) +{ + writeb(val, codec->reg_area_virt + offset); +} + +static inline u16 ymfpci_readw(ymfpci_t *codec, u32 offset) +{ + return readw(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writew(ymfpci_t *codec, u32 offset, u16 val) +{ + writew(val, codec->reg_area_virt + offset); +} + +static inline u32 ymfpci_readl(ymfpci_t *codec, u32 offset) +{ + return readl(codec->reg_area_virt + offset); +} + +static inline void ymfpci_writel(ymfpci_t *codec, u32 offset, u32 val) +{ + writel(val, codec->reg_area_virt + offset); +} + +static int ymfpci_codec_ready(ymfpci_t *codec, int secondary, int sched) +{ + signed long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = jiffies + 3 * (HZ / 4); + do { + if ((ymfpci_readw(codec, reg) & 0x8000) == 0) + return 0; + if (sched) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + } while (end_time - (signed long)jiffies >= 0); + printk(KERN_ERR "ymfpci_codec_ready: codec %i is not ready [0x%x]\n", + secondary, ymfpci_readw(codec, reg)); + return -EBUSY; +} + +static void ymfpci_codec_write(struct ac97_codec *dev, u8 reg, u16 val) +{ + ymfpci_t *codec = dev->private_data; + u32 cmd; + + spin_lock(&codec->ac97_lock); + /* XXX Do make use of dev->id */ + ymfpci_codec_ready(codec, 0, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + ymfpci_writel(codec, YDSXGR_AC97CMDDATA, cmd); + spin_unlock(&codec->ac97_lock); +} + +static u16 _ymfpci_codec_read(ymfpci_t *unit, u8 reg) +{ + int i; + + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + ymfpci_writew(unit, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (ymfpci_codec_ready(unit, 0, 0)) + return ~0; + if (unit->pci->device == PCI_DEVICE_ID_YAMAHA_744 && unit->rev < 2) { + for (i = 0; i < 600; i++) + ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); + } + return ymfpci_readw(unit, YDSXGR_PRISTATUSDATA); +} + +static u16 ymfpci_codec_read(struct ac97_codec *dev, u8 reg) +{ + ymfpci_t *unit = dev->private_data; + u16 ret; + + spin_lock(&unit->ac97_lock); + ret = _ymfpci_codec_read(unit, reg); + spin_unlock(&unit->ac97_lock); + + return ret; +} + +/* + * Misc routines + */ + +/* + * Calculate the actual sampling rate relatetively to the base clock (48kHz). + */ +static u32 ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 48000) << 12; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 ymf_calc_lend(u32 rate) +{ + return (rate * YMF_SAMPF) / 48000; +} + +/* + * We ever allow only a few formats, but let's be generic, for smaller surprise. + */ +static int ymf_pcm_format_width(int format) +{ + static int mask16 = AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE; + + if ((format & (format-1)) != 0) { + printk(KERN_ERR "ymfpci: format 0x%x is not a power of 2\n", format); + return 8; + } + + if (format == AFMT_IMA_ADPCM) return 4; + if ((format & mask16) != 0) return 16; + return 8; +} + +static void ymf_pcm_update_shift(struct ymf_pcm_format *f) +{ + f->shift = 0; + if (f->voices == 2) + f->shift++; + if (ymf_pcm_format_width(f->format) == 16) + f->shift++; +} + +/* Are you sure 32K is not too much? See if mpg123 skips on loaded systems. */ +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* + * Allocate DMA buffer + */ +static int alloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + void *rawbuf = NULL; + dma_addr_t dma_addr; + int order; + struct page *map, *mapend; + + /* alloc as big a chunk as we can */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + rawbuf = pci_alloc_consistent(unit->pci, PAGE_SIZE << order, &dma_addr); + if (rawbuf) + break; + } + if (!rawbuf) + return -ENOMEM; + +#if 0 + printk(KERN_DEBUG "ymfpci: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->dma_addr = dma_addr; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */ + mapend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); + for (map = virt_to_page(rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &map->flags); + + return 0; +} + +/* + * Free DMA buffer + */ +static void dealloc_dmabuf(ymfpci_t *unit, struct ymf_dmabuf *dmabuf) +{ + struct page *map, *mapend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + mapend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = virt_to_page(dmabuf->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &map->flags); + + pci_free_consistent(unit->pci, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_addr); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct ymf_state *state, int rec) +{ + struct ymf_dmabuf *dmabuf; + int w_16; + unsigned bufsize; + unsigned long flags; + int redzone, redfrags; + int ret; + + w_16 = ymf_pcm_format_width(state->format.format) == 16; + dmabuf = rec ? &state->rpcm.dmabuf : &state->wpcm.dmabuf; + + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state->unit, dmabuf))) + return ret; + + /* + * Create fake fragment sizes and numbers for OSS ioctls. + * Import what Doom might have set with SNDCTL_DSP_SETFRAGMENT. + */ + bufsize = PAGE_SIZE << dmabuf->buforder; + /* By default we give 4 big buffers. */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2); + if (dmabuf->ossfragshift > 3 && + dmabuf->ossfragshift < dmabuf->fragshift) { + /* If OSS set smaller fragments, give more smaller buffers. */ + dmabuf->fragshift = dmabuf->ossfragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + if (dmabuf->ossmaxfrags >= 2) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + redfrags = (redzone + dmabuf->fragsize-1) >> dmabuf->fragshift; + + if (dmabuf->ossmaxfrags + redfrags < dmabuf->numfrag) { + dmabuf->numfrag = dmabuf->ossmaxfrags + redfrags; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + } + } + + memset(dmabuf->rawbuf, w_16 ? 0 : 0x80, dmabuf->dmasize); + + /* + * Now set up the ring + */ + + /* XXX ret = rec? cap_pre(): pbk_pre(); */ + spin_lock_irqsave(&state->unit->voice_lock, flags); + if (rec) { + if ((ret = ymf_capture_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } else { + if ((ret = ymf_playback_prepare(state)) != 0) { + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + return ret; + } + } + spin_unlock_irqrestore(&state->unit->voice_lock, flags); + + /* set the ready flag for the dma buffer (this comment is not stupid) */ + dmabuf->ready = 1; + +#if 0 + printk(KERN_DEBUG "prog_dmabuf: rate %d format 0x%x," + " numfrag %d fragsize %d dmasize %d\n", + state->format.rate, state->format.format, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +static void ymf_start_dac(struct ymf_state *state) +{ + ymf_playback_trigger(state->unit, &state->wpcm, 1); +} + +// static void ymf_start_adc(struct ymf_state *state) +// { +// ymf_capture_trigger(state->unit, &state->rpcm, 1); +// } + +/* + * Wait until output is drained. + * This does not kill the hardware for the sake of ioctls. + */ +static void ymf_wait_dac(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + struct ymf_pcm *ypcm = &state->wpcm; + DECLARE_WAITQUEUE(waita, current); + unsigned long flags; + + add_wait_queue(&ypcm->dmabuf.wait, &waita); + + spin_lock_irqsave(&unit->reg_lock, flags); + if (ypcm->dmabuf.count != 0 && !ypcm->running) { + ymf_playback_trigger(unit, ypcm, 1); + } + +#if 0 + if (file->f_flags & O_NONBLOCK) { + /* + * XXX Our mistake is to attach DMA buffer to state + * rather than to some per-device structure. + * Cannot skip waiting, can only make it shorter. + */ + } +#endif + + set_current_state(TASK_UNINTERRUPTIBLE); + while (ypcm->running) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + spin_lock_irqsave(&unit->reg_lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&ypcm->dmabuf.wait, &waita); + + /* + * This function may take up to 4 seconds to reach this point + * (32K circular buffer, 8000 Hz). User notices. + */ +} + +/* Can just stop, without wait. Or can we? */ +static void ymf_stop_adc(struct ymf_state *state) +{ + struct ymf_unit *unit = state->unit; + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + ymf_capture_trigger(unit, &state->rpcm, 0); + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Hardware start management + */ + +static void ymfpci_hw_start(ymfpci_t *unit) +{ + unsigned long flags; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->start_count++ == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) | 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +static void ymfpci_hw_stop(ymfpci_t *unit) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (--unit->start_count == 0) { + ymfpci_writel(unit, YDSXGR_MODE, + ymfpci_readl(unit, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) + break; + } + } + spin_unlock_irqrestore(&unit->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t *rvoice[]) +{ + ymfpci_voice_t *voice, *voice2; + int idx; + + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &codec->voices[idx]; + voice2 = pair ? &codec->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + ymfpci_hw_start(codec); + rvoice[0] = voice; + if (voice2) { + ymfpci_hw_start(codec); + rvoice[1] = voice2; + } + return 0; + } + return -EBUSY; /* Your audio channel is open by someone else. */ +} + +static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice) +{ + ymfpci_hw_stop(unit); + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; +} + +/* + */ + +static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice) +{ + struct ymf_pcm *ypcm; + int redzone; + int pos, delta, swptr; + int played, distance; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + char silence; + + if ((ypcm = voice->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&codec->reg_lock); + if (ypcm->running) { + YMFDBGI("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", + voice->number, codec->active_bank, dmabuf->count, + le32_to_cpu(voice->bank[0].start), + le32_to_cpu(voice->bank[1].start)); + silence = (ymf_pcm_format_width(state->format.format) == 16) ? + 0 : 0x80; + /* We need actual left-hand-side redzone size here. */ + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + swptr = dmabuf->swptr; + + pos = le32_to_cpu(voice->bank[codec->active_bank].start); + pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway voice %d: hwptr %d=>%d dmasize %d\n", + codec->dev_audio, voice->number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + delta += pos; + memset(dmabuf->rawbuf, silence, pos); + } else { + delta = pos - dmabuf->hwptr; + memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); + } + dmabuf->hwptr = pos; + + if (dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: %d: strain: hwptr %d\n", + codec->dev_audio, voice->number, dmabuf->hwptr); + ymf_playback_trigger(codec, ypcm, 0); + } + + if (swptr <= pos) { + distance = pos - swptr; + } else { + distance = dmabuf->dmasize - (swptr - pos); + } + if (distance < redzone) { + /* + * hwptr inside redzone => DMA ran out of samples. + */ + if (delta < dmabuf->count) { + /* + * Lost interrupt or other screwage. + */ + printk(KERN_ERR "ymfpci%d: %d: lost: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } else { + /* + * Normal end of DMA. + */ + YMFDBGI("ymfpci%d: %d: done: delta %d" + " hwptr %d swptr %d distance %d count %d\n", + codec->dev_audio, voice->number, delta, + dmabuf->hwptr, swptr, distance, dmabuf->count); + } + played = dmabuf->count; + if (ypcm->running) { + ymf_playback_trigger(codec, ypcm, 0); + } + } else { + /* + * hwptr is chipping away towards a remote swptr. + * Calculate other distance and apply it to count. + */ + if (swptr >= pos) { + distance = swptr - pos; + } else { + distance = dmabuf->dmasize - (pos - swptr); + } + if (distance < dmabuf->count) { + played = dmabuf->count - distance; + } else { + played = 0; + } + } + + dmabuf->total_bytes += played; + dmabuf->count -= played; + if (dmabuf->count < dmabuf->dmasize / 2) { + wake_up(&dmabuf->wait); + } + } + spin_unlock(&codec->reg_lock); +} + +static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap) +{ + struct ymf_pcm *ypcm; + int redzone; + struct ymf_state *state; + struct ymf_dmabuf *dmabuf; + int pos, delta; + int cnt; + + if ((ypcm = cap->ypcm) == NULL) { + return; + } + if ((state = ypcm->state) == NULL) { + ypcm->running = 0; // lock it + return; + } + dmabuf = &ypcm->dmabuf; + spin_lock(&unit->reg_lock); + if (ypcm->running) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= (state->format.shift + 1); + + pos = le32_to_cpu(cap->bank[unit->active_bank].start); + // pos <<= state->format.shift; + if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ + printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n", + unit->dev_audio, ypcm->capture_bank_number, + dmabuf->hwptr, pos, dmabuf->dmasize); + pos = 0; + } + if (pos < dmabuf->hwptr) { + delta = dmabuf->dmasize - dmabuf->hwptr; + delta += pos; + } else { + delta = pos - dmabuf->hwptr; + } + dmabuf->hwptr = pos; + + cnt = dmabuf->count; + cnt += delta; + if (cnt + redzone > dmabuf->dmasize) { + /* Overflow - bump swptr */ + dmabuf->count = dmabuf->dmasize - redzone; + dmabuf->swptr = dmabuf->hwptr + redzone; + if (dmabuf->swptr >= dmabuf->dmasize) { + dmabuf->swptr -= dmabuf->dmasize; + } + } else { + dmabuf->count = cnt; + } + + dmabuf->total_bytes += delta; + if (dmabuf->count) { /* && is_sleeping XXX */ + wake_up(&dmabuf->wait); + } + } + spin_unlock(&unit->reg_lock); +} + +static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + + if (ypcm->voices[0] == NULL) { + return -EINVAL; + } + if (cmd != 0) { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = + cpu_to_le32(ypcm->voices[0]->bank_ba); + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = + cpu_to_le32(ypcm->voices[1]->bank_ba); + ypcm->running = 1; + } else { + codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL) + codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + } + return 0; +} + +static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd) +{ + u32 tmp; + + if (cmd != 0) { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + } else { + tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + } +} + +static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices) +{ + struct ymf_unit *unit; + int err; + + unit = ypcm->state->unit; + if (ypcm->voices[1] != NULL && voices < 2) { + ymfpci_voice_free(unit, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + ymfpci_voice_free(unit, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[1]->ypcm = ypcm; + } else { + if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + } + return 0; +} + +static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, + int rate, int w_16, unsigned long addr, unsigned int end, int spdif) +{ + u32 format; + u32 delta = ymfpci_calc_delta(rate); + u32 lpfQ = ymfpci_calc_lpfQ(rate); + u32 lpfK = ymfpci_calc_lpfK(rate); + ymfpci_playback_bank_t *bank; + int nbank; + + /* + * The gain is a floating point number. According to the manual, + * bit 31 indicates a sign bit, bit 30 indicates an integer part, + * and bits [29:15] indicate a decimal fraction part. Thus, + * for a gain of 1.0 the constant of 0x40000000 is loaded. + */ + unsigned default_gain = cpu_to_le32(0x40000000); + + format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + if (stereo) + end >>= 1; + if (w_16) + end >>= 1; + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + bank->format = cpu_to_le32(format); + bank->loop_default = 0; /* 0-loops forever, otherwise count */ + bank->base = cpu_to_le32(addr); + bank->loop_start = 0; + bank->loop_end = cpu_to_le32(end); + bank->loop_frac = 0; + bank->eg_gain_end = default_gain; + bank->lpfQ = cpu_to_le32(lpfQ); + bank->status = 0; + bank->num_of_frames = 0; + bank->loop_count = 0; + bank->start = 0; + bank->start_frac = 0; + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = default_gain; + bank->lpfD1 = + bank->lpfD2 = 0; + + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = + bank->eff1_gain = + bank->eff2_gain = + bank->eff3_gain = + bank->eff1_gain_end = + bank->eff2_gain_end = + bank->eff3_gain_end = 0; + + if (!stereo) { + if (!spdif) { + bank->left_gain = + bank->right_gain = + bank->left_gain_end = + bank->right_gain_end = default_gain; + } else { + bank->eff2_gain = + bank->eff2_gain_end = + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } else { + if (!spdif) { + if ((voice->number & 1) == 0) { + bank->left_gain = + bank->left_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->right_gain = + bank->right_gain_end = default_gain; + } + } else { + if ((voice->number & 1) == 0) { + bank->eff2_gain = + bank->eff2_gain_end = default_gain; + } else { + bank->format |= cpu_to_le32(1); + bank->eff3_gain = + bank->eff3_gain_end = default_gain; + } + } + } + } +} + +/* + * XXX Capture channel allocation is entirely fake at the moment. + * We use only one channel and mark it busy as required. + */ +static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank) +{ + struct ymf_capture *cap; + int cbank; + + cbank = 1; /* Only ADC slot is used for now. */ + cap = &unit->capture[cbank]; + if (cap->use) + return -EBUSY; + cap->use = 1; + *pbank = cbank; + return 0; +} + +static int ymf_playback_prepare(struct ymf_state *state) +{ + struct ymf_pcm *ypcm = &state->wpcm; + int err, nvoice; + + if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { + /* Somebody started 32 mpg123's in parallel? */ + printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", + state->unit->dev_audio); + return err; + } + + for (nvoice = 0; nvoice < state->format.voices; nvoice++) { + ymf_pcm_init_voice(ypcm->voices[nvoice], + state->format.voices == 2, state->format.rate, + ymf_pcm_format_width(state->format.format) == 16, + ypcm->dmabuf.dma_addr, ypcm->dmabuf.dmasize, + ypcm->spdif); + } + return 0; +} + +static int ymf_capture_prepare(struct ymf_state *state) +{ + ymfpci_t *unit = state->unit; + struct ymf_pcm *ypcm = &state->rpcm; + ymfpci_capture_bank_t * bank; + /* XXX This is confusing, gotta rename one of them banks... */ + int nbank; /* flip-flop bank */ + int cbank; /* input [super-]bank */ + struct ymf_capture *cap; + u32 rate, format; + + if (ypcm->capture_bank_number == -1) { + if (ymf_capture_alloc(unit, &cbank) != 0) + return -EBUSY; + + ypcm->capture_bank_number = cbank; + + cap = &unit->capture[cbank]; + cap->bank = unit->bank_capture[cbank][0]; + cap->ypcm = ypcm; + ymfpci_hw_start(unit); + } + + // ypcm->frag_size = snd_pcm_lib_transfer_fragment(substream); + // frag_size is replaced with nonfragged byte-aligned rolling buffer + rate = ((48000 * 4096) / state->format.rate) - 1; + format = 0; + if (state->format.voices == 2) + format |= 2; + if (ymf_pcm_format_width(state->format.format) == 8) + format |= 1; + switch (ypcm->capture_bank_number) { + case 0: + ymfpci_writel(unit, YDSXGR_RECFORMAT, format); + ymfpci_writel(unit, YDSXGR_RECSLOTSR, rate); + break; + case 1: + ymfpci_writel(unit, YDSXGR_ADCFORMAT, format); + ymfpci_writel(unit, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = unit->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(ypcm->dmabuf.dma_addr); + // bank->loop_end = ypcm->dmabuf.dmasize >> state->format.shift; + bank->loop_end = cpu_to_le32(ypcm->dmabuf.dmasize); + bank->start = 0; + bank->num_of_loops = 0; + } +#if 0 /* s/pdif */ + if (state->digital.dig_valid) + /*state->digital.type == SND_PCM_DIG_AES_IEC958*/ + ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, + state->digital.dig_status[0] | (state->digital.dig_status[1] << 8)); +#endif + return 0; +} + +static irqreturn_t ymf_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ymfpci_t *codec = dev_id; + u32 status, nvoice, mode; + struct ymf_voice *voice; + struct ymf_capture *cap; + + status = ymfpci_readl(codec, YDSXGR_STATUS); + if (status & 0x80000000) { + codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; + spin_lock(&codec->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &codec->voices[nvoice]; + if (voice->use) + ymf_pcm_interrupt(codec, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + cap = &codec->capture[nvoice]; + if (cap->use) + ymf_cap_interrupt(codec, cap); + } + spin_unlock(&codec->voice_lock); + spin_lock(&codec->reg_lock); + ymfpci_writel(codec, YDSXGR_STATUS, 0x80000000); + mode = ymfpci_readl(codec, YDSXGR_MODE) | 2; + ymfpci_writel(codec, YDSXGR_MODE, mode); + spin_unlock(&codec->reg_lock); + } + + status = ymfpci_readl(codec, YDSXGR_INTFLAG); + if (status & 1) { + /* timer handler */ + ymfpci_writel(codec, YDSXGR_INTFLAG, ~0); + } + return IRQ_HANDLED; +} + +static void ymf_pcm_free_substream(struct ymf_pcm *ypcm) +{ + unsigned long flags; + struct ymf_unit *unit; + + unit = ypcm->state->unit; + + if (ypcm->type == PLAYBACK_VOICE) { + spin_lock_irqsave(&unit->voice_lock, flags); + if (ypcm->voices[1]) + ymfpci_voice_free(unit, ypcm->voices[1]); + if (ypcm->voices[0]) + ymfpci_voice_free(unit, ypcm->voices[0]); + spin_unlock_irqrestore(&unit->voice_lock, flags); + } else { + if (ypcm->capture_bank_number != -1) { + unit->capture[ypcm->capture_bank_number].use = 0; + ypcm->capture_bank_number = -1; + ymfpci_hw_stop(unit); + } + } +} + +static struct ymf_state *ymf_state_alloc(ymfpci_t *unit) +{ + struct ymf_pcm *ypcm; + struct ymf_state *state; + + if ((state = kmalloc(sizeof(struct ymf_state), GFP_KERNEL)) == NULL) { + goto out0; + } + memset(state, 0, sizeof(struct ymf_state)); + + ypcm = &state->wpcm; + ypcm->state = state; + ypcm->type = PLAYBACK_VOICE; + ypcm->capture_bank_number = -1; + init_waitqueue_head(&ypcm->dmabuf.wait); + + ypcm = &state->rpcm; + ypcm->state = state; + ypcm->type = CAPTURE_AC97; + ypcm->capture_bank_number = -1; + init_waitqueue_head(&ypcm->dmabuf.wait); + + state->unit = unit; + + state->format.format = AFMT_U8; + state->format.rate = 8000; + state->format.voices = 1; + ymf_pcm_update_shift(&state->format); + + return state; + +out0: + return NULL; +} + +/* AES/IEC958 channel status bits */ +#define SND_PCM_AES0_PROFESSIONAL (1<<0) /* 0 = consumer, 1 = professional */ +#define SND_PCM_AES0_NONAUDIO (1<<1) /* 0 = audio, 1 = non-audio */ +#define SND_PCM_AES0_PRO_EMPHASIS (7<<2) /* mask - emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_NOTID (0<<2) /* emphasis not indicated */ +#define SND_PCM_AES0_PRO_EMPHASIS_NONE (1<<2) /* none emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_5015 (3<<2) /* 50/15us emphasis */ +#define SND_PCM_AES0_PRO_EMPHASIS_CCITT (7<<2) /* CCITT J.17 emphasis */ +#define SND_PCM_AES0_PRO_FREQ_UNLOCKED (1<<5) /* source sample frequency: 0 = locked, 1 = unlocked */ +#define SND_PCM_AES0_PRO_FS (3<<6) /* mask - sample frequency */ +#define SND_PCM_AES0_PRO_FS_NOTID (0<<6) /* fs not indicated */ +#define SND_PCM_AES0_PRO_FS_44100 (1<<6) /* 44.1kHz */ +#define SND_PCM_AES0_PRO_FS_48000 (2<<6) /* 48kHz */ +#define SND_PCM_AES0_PRO_FS_32000 (3<<6) /* 32kHz */ +#define SND_PCM_AES0_CON_NOT_COPYRIGHT (1<<2) /* 0 = copyright, 1 = not copyright */ +#define SND_PCM_AES0_CON_EMPHASIS (7<<3) /* mask - emphasis */ +#define SND_PCM_AES0_CON_EMPHASIS_NONE (0<<3) /* none emphasis */ +#define SND_PCM_AES0_CON_EMPHASIS_5015 (1<<3) /* 50/15us emphasis */ +#define SND_PCM_AES0_CON_MODE (3<<6) /* mask - mode */ +#define SND_PCM_AES1_PRO_MODE (15<<0) /* mask - channel mode */ +#define SND_PCM_AES1_PRO_MODE_NOTID (0<<0) /* not indicated */ +#define SND_PCM_AES1_PRO_MODE_STEREOPHONIC (2<<0) /* stereophonic - ch A is left */ +#define SND_PCM_AES1_PRO_MODE_SINGLE (4<<0) /* single channel */ +#define SND_PCM_AES1_PRO_MODE_TWO (8<<0) /* two channels */ +#define SND_PCM_AES1_PRO_MODE_PRIMARY (12<<0) /* primary/secondary */ +#define SND_PCM_AES1_PRO_MODE_BYTE3 (15<<0) /* vector to byte 3 */ +#define SND_PCM_AES1_PRO_USERBITS (15<<4) /* mask - user bits */ +#define SND_PCM_AES1_PRO_USERBITS_NOTID (0<<4) /* not indicated */ +#define SND_PCM_AES1_PRO_USERBITS_192 (8<<4) /* 192-bit structure */ +#define SND_PCM_AES1_PRO_USERBITS_UDEF (12<<4) /* user defined application */ +#define SND_PCM_AES1_CON_CATEGORY 0x7f +#define SND_PCM_AES1_CON_GENERAL 0x00 +#define SND_PCM_AES1_CON_EXPERIMENTAL 0x40 +#define SND_PCM_AES1_CON_SOLIDMEM_MASK 0x0f +#define SND_PCM_AES1_CON_SOLIDMEM_ID 0x08 +#define SND_PCM_AES1_CON_BROADCAST1_MASK 0x07 +#define SND_PCM_AES1_CON_BROADCAST1_ID 0x04 +#define SND_PCM_AES1_CON_DIGDIGCONV_MASK 0x07 +#define SND_PCM_AES1_CON_DIGDIGCONV_ID 0x02 +#define SND_PCM_AES1_CON_ADC_COPYRIGHT_MASK 0x1f +#define SND_PCM_AES1_CON_ADC_COPYRIGHT_ID 0x06 +#define SND_PCM_AES1_CON_ADC_MASK 0x1f +#define SND_PCM_AES1_CON_ADC_ID 0x16 +#define SND_PCM_AES1_CON_BROADCAST2_MASK 0x0f +#define SND_PCM_AES1_CON_BROADCAST2_ID 0x0e +#define SND_PCM_AES1_CON_LASEROPT_MASK 0x07 +#define SND_PCM_AES1_CON_LASEROPT_ID 0x01 +#define SND_PCM_AES1_CON_MUSICAL_MASK 0x07 +#define SND_PCM_AES1_CON_MUSICAL_ID 0x05 +#define SND_PCM_AES1_CON_MAGNETIC_MASK 0x07 +#define SND_PCM_AES1_CON_MAGNETIC_ID 0x03 +#define SND_PCM_AES1_CON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x00) +#define SND_PCM_AES1_CON_NON_IEC908_CD (SND_PCM_AES1_CON_LASEROPT_ID|0x08) +#define SND_PCM_AES1_CON_PCM_CODER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x00) +#define SND_PCM_AES1_CON_SAMPLER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x20) +#define SND_PCM_AES1_CON_MIXER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x10) +#define SND_PCM_AES1_CON_RATE_CONVERTER (SND_PCM_AES1_CON_DIGDIGCONV_ID|0x18) +#define SND_PCM_AES1_CON_SYNTHESIZER (SND_PCM_AES1_CON_MUSICAL_ID|0x00) +#define SND_PCM_AES1_CON_MICROPHONE (SND_PCM_AES1_CON_MUSICAL_ID|0x08) +#define SND_PCM_AES1_CON_DAT (SND_PCM_AES1_CON_MAGNETIC_ID|0x00) +#define SND_PCM_AES1_CON_VCR (SND_PCM_AES1_CON_MAGNETIC_ID|0x08) +#define SND_PCM_AES1_CON_ORIGINAL (1<<7) /* this bits depends on the category code */ +#define SND_PCM_AES2_PRO_SBITS (7<<0) /* mask - sample bits */ +#define SND_PCM_AES2_PRO_SBITS_20 (2<<0) /* 20-bit - coordination */ +#define SND_PCM_AES2_PRO_SBITS_24 (4<<0) /* 24-bit - main audio */ +#define SND_PCM_AES2_PRO_SBITS_UDEF (6<<0) /* user defined application */ +#define SND_PCM_AES2_PRO_WORDLEN (7<<3) /* mask - source word length */ +#define SND_PCM_AES2_PRO_WORDLEN_NOTID (0<<3) /* not indicated */ +#define SND_PCM_AES2_PRO_WORDLEN_22_18 (2<<3) /* 22-bit or 18-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_23_19 (4<<3) /* 23-bit or 19-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_24_20 (5<<3) /* 24-bit or 20-bit */ +#define SND_PCM_AES2_PRO_WORDLEN_20_16 (6<<3) /* 20-bit or 16-bit */ +#define SND_PCM_AES2_CON_SOURCE (15<<0) /* mask - source number */ +#define SND_PCM_AES2_CON_SOURCE_UNSPEC (0<<0) /* unspecified */ +#define SND_PCM_AES2_CON_CHANNEL (15<<4) /* mask - channel number */ +#define SND_PCM_AES2_CON_CHANNEL_UNSPEC (0<<4) /* unspecified */ +#define SND_PCM_AES3_CON_FS (15<<0) /* mask - sample frequency */ +#define SND_PCM_AES3_CON_FS_44100 (0<<0) /* 44.1kHz */ +#define SND_PCM_AES3_CON_FS_48000 (2<<0) /* 48kHz */ +#define SND_PCM_AES3_CON_FS_32000 (3<<0) /* 32kHz */ +#define SND_PCM_AES3_CON_CLOCK (3<<4) /* mask - clock accuracy */ +#define SND_PCM_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */ +#define SND_PCM_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */ +#define SND_PCM_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */ + +/* + * User interface + */ + +/* + * in this loop, dmabuf.count signifies the amount of data that is + * waiting to be copied to the user's buffer. it is filled by the dma + * machine and drained by this loop. + */ +static ssize_t +ymf_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->rpcm.dmabuf; + struct ymf_unit *unit = state->unit; + DECLARE_WAITQUEUE(waita, current); + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; /* This many to go in this revolution */ + + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + ret = 0; + + add_wait_queue(&dmabuf->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while (count > 0) { + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + spin_unlock_irqrestore(&unit->reg_lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (!state->rpcm.running) { + ymf_capture_trigger(state->unit, &state->rpcm, 1); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + break; + } + /* This isnt strictly right for the 810 but it'll do */ + tmo = (dmabuf->dmasize * HZ) / (state->format.rate * 2); + tmo >>= state->format.shift; + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + tmo = schedule_timeout(tmo); + spin_lock_irqsave(&state->unit->reg_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tmo == 0 && dmabuf->count == 0) { + printk(KERN_ERR "ymfpci%d: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + state->unit->dev_audio, + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + break; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + break; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + // spin_unlock_irqrestore(&unit->reg_lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + // spin_lock_irqsave(&unit->reg_lock, flags); + if (!state->rpcm.running) { + ymf_capture_trigger(unit, &state->rpcm, 1); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + return ret; +} + +static ssize_t +ymf_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + struct ymf_unit *unit = state->unit; + DECLARE_WAITQUEUE(waita, current); + ssize_t ret; + unsigned long flags; + unsigned int swptr; + int cnt; /* This many to go in this revolution */ + int redzone; + int delay; + + YMFDBGW("ymf_write: count %d\n", count); + + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + ret = 0; + + /* + * Alan's cs46xx works without a red zone - marvel of ingenuity. + * We are not so brilliant... Red zone does two things: + * 1. allows for safe start after a pause as we have no way + * to know what the actual, relentlessly advancing, hwptr is. + * 2. makes computations in ymf_pcm_interrupt simpler. + */ + redzone = ymf_calc_lend(state->format.rate) << state->format.shift; + redzone *= 3; /* 2 redzone + 1 possible uncertainty reserve. */ + + add_wait_queue(&dmabuf->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while (count > 0) { + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -EAGAIN; + break; + } + continue; + } + if (dmabuf->count < 0) { + printk(KERN_ERR + "ymf_write: count %d, was legal in cs46xx\n", + dmabuf->count); + dmabuf->count = 0; + } + if (dmabuf->count == 0) { + swptr = dmabuf->hwptr; + if (state->wpcm.running) { + /* + * Add uncertainty reserve. + */ + cnt = ymf_calc_lend(state->format.rate); + cnt <<= state->format.shift; + if ((swptr += cnt) >= dmabuf->dmasize) { + swptr -= dmabuf->dmasize; + } + } + dmabuf->swptr = swptr; + } else { + /* + * XXX This is not right if dmabuf->count is small - + * about 2*x frame size or less. We cannot count on + * on appending and not causing an artefact. + * Should use a variation of the count==0 case above. + */ + swptr = dmabuf->swptr; + } + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize - redzone) + cnt = (dmabuf->dmasize - redzone) - dmabuf->count; + spin_unlock_irqrestore(&unit->reg_lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + YMFDBGW("ymf_write: full, count %d swptr %d\n", + dmabuf->count, dmabuf->swptr); + /* + * buffer is full, start the dma machine and + * wait for data to be played + */ + spin_lock_irqsave(&unit->reg_lock, flags); + if (!state->wpcm.running) { + ymf_playback_trigger(unit, &state->wpcm, 1); + } + spin_unlock_irqrestore(&unit->reg_lock, flags); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + break; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + break; + } + + if ((swptr += cnt) >= dmabuf->dmasize) { + swptr -= dmabuf->dmasize; + } + + spin_lock_irqsave(&unit->reg_lock, flags); + if (unit->suspended) { + spin_unlock_irqrestore(&unit->reg_lock, flags); + continue; + } + dmabuf->swptr = swptr; + dmabuf->count += cnt; + + /* + * Start here is a bad idea - may cause startup click + * in /bin/play when dmabuf is not full yet. + * However, some broken applications do not make + * any use of SNDCTL_DSP_SYNC (Doom is the worst). + * One frame is about 5.3ms, Doom write size is 46ms. + */ + delay = state->format.rate / 20; /* 50ms */ + delay <<= state->format.shift; + if (dmabuf->count >= delay && !state->wpcm.running) { + ymf_playback_trigger(unit, &state->wpcm, 1); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&dmabuf->wait, &waita); + + YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, dmabuf->count); + return ret; +} + +static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf; + int redzone; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &state->wpcm.dmabuf.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &state->rpcm.dmabuf.wait, wait); + + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (file->f_mode & FMODE_READ) { + dmabuf = &state->rpcm.dmabuf; + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + + dmabuf = &state->wpcm.dmabuf; + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + /* + * Don't select unless a full fragment is available. + * Otherwise artsd does GETOSPACE, sees 0, and loops. + */ + if (dmabuf->count + redzone + dmabuf->fragsize + <= dmabuf->dmasize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + + return mask; +} + +static int ymf_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf = &state->wpcm.dmabuf; + int ret; + unsigned long size; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(state, 0)) != 0) + return ret; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(state, 1)) != 0) + return ret; + } else + return -EINVAL; + + if (vma->vm_pgoff != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + return -EINVAL; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + return -EAGAIN; + dmabuf->mapped = 1; + +/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n"); + return 0; +} + +static int ymf_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + struct ymf_dmabuf *dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int redzone; + int val; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + switch (cmd) { + case OSS_GETVERSION: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg); + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_RESET: + YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr; + dmabuf->count = dmabuf->total_bytes = 0; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + return 0; + + case SNDCTL_DSP_SYNC: + YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + dmabuf = &state->wpcm.dmabuf; + if (file->f_flags & O_NONBLOCK) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (dmabuf->count != 0 && !state->wpcm.running) { + ymf_start_dac(state); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } else { + ymf_wait_dac(state); + } + } + /* XXX What does this do for reading? dmabuf->count=0; ? */ + return 0; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + if (get_user(val, p)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val); + if (val >= 8000 && val <= 48000) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.rate = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.rate = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + return put_user(state->format.rate, p); + + /* + * OSS manual does not mention SNDCTL_DSP_STEREO at all. + * All channels are mono and if you want stereo, you + * play into two channels with SNDCTL_DSP_CHANNELS. + * However, mpg123 calls it. I wonder, why Michael Hipp used it. + */ + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + if (get_user(val, p)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val); + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.voices = val ? 2 : 1; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.voices = val ? 2 : 1; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd); + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(state, 0))) + return val; + val = state->wpcm.dmabuf.fragsize; + YMFDBGX("ymf_ioctl: GETBLK w %d\n", val); + return put_user(val, p); + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + return val; + val = state->rpcm.dmabuf.fragsize; + YMFDBGX("ymf_ioctl: GETBLK r %d\n", val); + return put_user(val, p); + } + return -EINVAL; + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd); + return put_user(AFMT_S16_LE|AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + if (get_user(val, p)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val); + if (val == AFMT_S16_LE || val == AFMT_U8) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.format = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf->ready = 0; + state->format.format = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + return put_user(state->format.format, p); + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, val); + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + ymf_wait_dac(state); + if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf = &state->wpcm.dmabuf; + dmabuf->ready = 0; + state->format.voices = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + if (file->f_mode & FMODE_READ) { + ymf_stop_adc(state); + if (val == 1 || val == 2) { + spin_lock_irqsave(&state->unit->reg_lock, flags); + dmabuf = &state->rpcm.dmabuf; + dmabuf->ready = 0; + state->format.voices = val; + ymf_pcm_update_shift(&state->format); + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + } + } + } + return put_user(state->format.voices, p); + + case SNDCTL_DSP_POST: + YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd); + /* + * Quoting OSS PG: + * The ioctl SNDCTL_DSP_POST is a lightweight version of + * SNDCTL_DSP_SYNC. It just tells to the driver that there + * is likely to be a pause in the output. This makes it + * possible for the device to handle the pause more + * intelligently. This ioctl doesn't block the application. + * + * The paragraph above is a clumsy way to say "flush ioctl". + * This ioctl is used by mpg123. + */ + spin_lock_irqsave(&state->unit->reg_lock, flags); + if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) { + ymf_start_dac(state); + } + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n", + cmd, + (val >> 16) & 0xFFFF, val & 0xFFFF, + (val >> 16) & 0xFFFF, val & 0xFFFF); + dmabuf = &state->wpcm.dmabuf; + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (dmabuf->ossfragshift < 4) + dmabuf->ossfragshift = 4; + if (dmabuf->ossfragshift > 15) + dmabuf->ossfragshift = 15; + return 0; + + case SNDCTL_DSP_GETOSPACE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + dmabuf = &state->wpcm.dmabuf; + if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) + return val; + redzone = ymf_calc_lend(state->format.rate); + redzone <<= state->format.shift; + redzone *= 3; + spin_lock_irqsave(&state->unit->reg_lock, flags); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + dmabuf = &state->rpcm.dmabuf; + if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->unit->reg_lock, flags); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd); + /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + p); */ + return put_user(0, p); + + case SNDCTL_DSP_GETIPTR: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + dmabuf = &state->rpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n", + cinfo.ptr, cinfo.bytes); + return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + dmabuf = &state->wpcm.dmabuf; + spin_lock_irqsave(&state->unit->reg_lock, flags); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + spin_unlock_irqrestore(&state->unit->reg_lock, flags); + YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n", + cinfo.ptr, cinfo.bytes); + return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_SETDUPLEX: + YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd); + return 0; /* Always duplex */ + + case SOUND_PCM_READ_RATE: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd); + return put_user(state->format.rate, p); + + case SOUND_PCM_READ_CHANNELS: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd); + return put_user(state->format.voices, p); + + case SOUND_PCM_READ_BITS: + YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd); + return put_user(AFMT_S16_LE, p); + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd); + return -ENOTTY; + + default: + /* + * Some programs mix up audio devices and ioctls + * or perhaps they expect "universal" ioctls, + * for instance we get SNDCTL_TMR_CONTINUE here. + * (mpg123 -g 100 ends here too - to be fixed.) + */ + YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd); + break; + } + return -ENOTTY; +} + +/* + * open(2) + * We use upper part of the minor to distinguish between soundcards. + * Channels are opened with a clone open. + */ +static int ymf_open(struct inode *inode, struct file *file) +{ + struct list_head *list; + ymfpci_t *unit = NULL; + int minor; + struct ymf_state *state; + int err; + + minor = iminor(inode); + if ((minor & 0x0F) == 3) { /* /dev/dspN */ + ; + } else { + return -ENXIO; + } + + unit = NULL; /* gcc warns */ + spin_lock(&ymf_devs_lock); + list_for_each(list, &ymf_devs) { + unit = list_entry(list, ymfpci_t, ymf_devs); + if (((unit->dev_audio ^ minor) & ~0x0F) == 0) + break; + } + spin_unlock(&ymf_devs_lock); + if (unit == NULL) + return -ENODEV; + + mutex_lock(&unit->open_mutex); + + if ((state = ymf_state_alloc(unit)) == NULL) { + mutex_unlock(&unit->open_mutex); + return -ENOMEM; + } + list_add_tail(&state->chain, &unit->states); + + file->private_data = state; + + /* + * ymf_read and ymf_write that we borrowed from cs46xx + * allocate buffers with prog_dmabuf(). We call prog_dmabuf + * here so that in case of DMA memory exhaustion open + * fails rather than write. + * + * XXX prog_dmabuf allocates voice. Should allocate explicitly, above. + */ + if (file->f_mode & FMODE_WRITE) { + if (!state->wpcm.dmabuf.ready) { + if ((err = prog_dmabuf(state, 0)) != 0) { + goto out_nodma; + } + } + } + if (file->f_mode & FMODE_READ) { + if (!state->rpcm.dmabuf.ready) { + if ((err = prog_dmabuf(state, 1)) != 0) { + goto out_nodma; + } + } + } + +#if 0 /* test if interrupts work */ + ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ + ymfpci_writeb(unit, YDSXGR_TIMERCTRL, + (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN)); +#endif + mutex_unlock(&unit->open_mutex); + + return nonseekable_open(inode, file); + +out_nodma: + /* + * XXX Broken custom: "goto out_xxx" in other place is + * a nestable exception, but here it is not nestable due to semaphore. + * XXX Doubtful technique of self-describing objects.... + */ + dealloc_dmabuf(unit, &state->wpcm.dmabuf); + dealloc_dmabuf(unit, &state->rpcm.dmabuf); + ymf_pcm_free_substream(&state->wpcm); + ymf_pcm_free_substream(&state->rpcm); + + list_del(&state->chain); + kfree(state); + + mutex_unlock(&unit->open_mutex); + return err; +} + +static int ymf_release(struct inode *inode, struct file *file) +{ + struct ymf_state *state = (struct ymf_state *)file->private_data; + ymfpci_t *unit = state->unit; + +#if 0 /* test if interrupts work */ + ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0); +#endif + + mutex_lock(&unit->open_mutex); + + /* + * XXX Solve the case of O_NONBLOCK close - don't deallocate here. + * Deallocate when unloading the driver and we can wait. + */ + ymf_wait_dac(state); + ymf_stop_adc(state); /* fortunately, it's immediate */ + dealloc_dmabuf(unit, &state->wpcm.dmabuf); + dealloc_dmabuf(unit, &state->rpcm.dmabuf); + ymf_pcm_free_substream(&state->wpcm); + ymf_pcm_free_substream(&state->rpcm); + + list_del(&state->chain); + file->private_data = NULL; /* Can you tell I programmed Solaris */ + kfree(state); + + mutex_unlock(&unit->open_mutex); + + return 0; +} + +/* + * Mixer operations are based on cs46xx. + */ +static int ymf_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct list_head *list; + ymfpci_t *unit; + int i; + + spin_lock(&ymf_devs_lock); + list_for_each(list, &ymf_devs) { + unit = list_entry(list, ymfpci_t, ymf_devs); + for (i = 0; i < NR_AC97; i++) { + if (unit->ac97_codec[i] != NULL && + unit->ac97_codec[i]->dev_mixer == minor) { + spin_unlock(&ymf_devs_lock); + goto match; + } + } + } + spin_unlock(&ymf_devs_lock); + return -ENODEV; + + match: + file->private_data = unit->ac97_codec[i]; + + return nonseekable_open(inode, file); +} + +static int ymf_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int ymf_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + +static /*const*/ struct file_operations ymf_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ymf_read, + .write = ymf_write, + .poll = ymf_poll, + .ioctl = ymf_ioctl, + .mmap = ymf_mmap, + .open = ymf_open, + .release = ymf_release, +}; + +static /*const*/ struct file_operations ymf_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = ymf_ioctl_mixdev, + .open = ymf_open_mixdev, + .release = ymf_release_mixdev, +}; + +/* + */ + +static int ymf_suspend(struct pci_dev *pcidev, pm_message_t unused) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct ymf_dmabuf *dmabuf; + struct list_head *p; + struct ymf_state *state; + struct ac97_codec *codec; + int i; + + spin_lock_irqsave(&unit->reg_lock, flags); + + unit->suspended = 1; + + for (i = 0; i < NR_AC97; i++) { + if ((codec = unit->ac97_codec[i]) != NULL) + ac97_save_state(codec); + } + + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + + dmabuf = &state->wpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + + dmabuf = &state->rpcm.dmabuf; + dmabuf->hwptr = dmabuf->swptr = 0; + dmabuf->total_bytes = 0; + dmabuf->count = 0; + } + + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); + ymfpci_disable_dsp(unit); + + spin_unlock_irqrestore(&unit->reg_lock, flags); + + return 0; +} + +static int ymf_resume(struct pci_dev *pcidev) +{ + struct ymf_unit *unit = pci_get_drvdata(pcidev); + unsigned long flags; + struct list_head *p; + struct ymf_state *state; + struct ac97_codec *codec; + int i; + + ymfpci_aclink_reset(unit->pci); + ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */ + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + /* XXX At this time the legacy registers are probably deprogrammed. */ +#endif + + ymfpci_download_image(unit); + + ymf_memload(unit); + + spin_lock_irqsave(&unit->reg_lock, flags); + + if (unit->start_count) { + ymfpci_writel(unit, YDSXGR_MODE, 3); + unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; + } + + for (i = 0; i < NR_AC97; i++) { + if ((codec = unit->ac97_codec[i]) != NULL) + ac97_restore_state(codec); + } + + unit->suspended = 0; + list_for_each(p, &unit->states) { + state = list_entry(p, struct ymf_state, chain); + wake_up(&state->wpcm.dmabuf.wait); + wake_up(&state->rpcm.dmabuf.wait); + } + + spin_unlock_irqrestore(&unit->reg_lock, flags); + return 0; +} + +/* + * initialization routines + */ + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + +static int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev) +{ + int v; + int mpuio = -1, oplio = -1; + + switch (unit->iomidi) { + case 0x330: + mpuio = 0; + break; + case 0x300: + mpuio = 1; + break; + case 0x332: + mpuio = 2; + break; + case 0x334: + mpuio = 3; + break; + default: ; + } + + switch (unit->iosynth) { + case 0x388: + oplio = 0; + break; + case 0x398: + oplio = 1; + break; + case 0x3a0: + oplio = 2; + break; + case 0x3a8: + oplio = 3; + break; + default: ; + } + + if (mpuio >= 0 || oplio >= 0) { + /* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */ + v = 0x001e; + pci_write_config_word(pcidev, PCIR_LEGCTRL, v); + + switch (pcidev->device) { + case PCI_DEVICE_ID_YAMAHA_724: + case PCI_DEVICE_ID_YAMAHA_740: + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + v = 0x8800; + if (mpuio >= 0) { v |= mpuio<<4; } + if (oplio >= 0) { v |= oplio; } + pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); + break; + + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + v = 0x8800; + pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); + if (oplio >= 0) { + pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth); + } + if (mpuio >= 0) { + pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi); + } + break; + + default: + printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n", + pcidev->device); + return -EINVAL; + } + } + + return 0; +} +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + +static void ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + /* + * In the 744, 754 only 0x01 exists, 0x02 is undefined. + * It does not seem to hurt to trip both regardless of revision. + */ + pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); + + pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0); +} + +static void ymfpci_enable_dsp(ymfpci_t *codec) +{ + ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001); +} + +static void ymfpci_disable_dsp(ymfpci_t *codec) +{ + u32 val; + int timeout = 1000; + + val = ymfpci_readl(codec, YDSXGR_CONFIG); + if (val) + ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = ymfpci_readl(codec, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#include "ymfpci_image.h" + +static void ymfpci_download_image(ymfpci_t *codec) +{ + int i, ver_1e; + u16 ctrl; + + ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + ymfpci_disable_dsp(codec); + ymfpci_writel(codec, YDSXGR_MODE, 0x00010000); + ymfpci_writel(codec, YDSXGR_MODE, 0x00000000); + ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000); + ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000); + ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000); + ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000); + ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); + + switch (codec->pci->device) { + case PCI_DEVICE_ID_YAMAHA_724F: + case PCI_DEVICE_ID_YAMAHA_740C: + case PCI_DEVICE_ID_YAMAHA_744: + case PCI_DEVICE_ID_YAMAHA_754: + ver_1e = 1; + break; + default: + ver_1e = 0; + } + + if (ver_1e) { + /* setup control instruction code */ + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]); + } else { + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]); + } + + ymfpci_enable_dsp(codec); + + /* 0.02s sounds not too bad, we may do schedule_timeout() later. */ + mdelay(20); /* seems we need some delay after downloading image.. */ +} + +static int ymfpci_memalloc(ymfpci_t *codec) +{ + unsigned int playback_ctrl_size; + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int size; + unsigned int off; + char *ptr; + dma_addr_t pba; + int voice, bank; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2; + bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2; + bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2; + codec->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ((playback_ctrl_size + 0x00ff) & ~0x00ff) + + ((bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) + + ((bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) + + ((bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) + + codec->work_size; + + ptr = pci_alloc_consistent(codec->pci, size + 0xff, &pba); + if (ptr == NULL) + return -ENOMEM; + codec->dma_area_va = ptr; + codec->dma_area_ba = pba; + codec->dma_area_size = size + 0xff; + + off = (unsigned long)ptr & 0xff; + if (off) { + ptr += 0x100 - off; + pba += 0x100 - off; + } + + /* + * Hardware requires only ptr[playback_ctrl_size] zeroed, + * but in our judgement it is a wrong kind of savings, so clear it all. + */ + memset(ptr, 0, size); + + codec->ctrl_playback = (u32 *)ptr; + codec->ctrl_playback_ba = pba; + codec->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; + pba += (playback_ctrl_size + 0x00ff) & ~0x00ff; + + off = 0; + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + codec->voices[voice].number = voice; + codec->voices[voice].bank = + (ymfpci_playback_bank_t *) (ptr + off); + codec->voices[voice].bank_ba = pba + off; + off += 2 * bank_size_playback; /* 2 banks */ + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + off = 0; + codec->bank_base_capture = pba; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + codec->bank_capture[voice][bank] = + (ymfpci_capture_bank_t *) (ptr + off); + off += bank_size_capture; + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + off = 0; + codec->bank_base_effect = pba; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + codec->bank_effect[voice][bank] = + (ymfpci_effect_bank_t *) (ptr + off); + off += bank_size_effect; + } + off = (off + 0xff) & ~0xff; + ptr += off; + pba += off; + + codec->work_base = pba; + + return 0; +} + +static void ymfpci_memfree(ymfpci_t *codec) +{ + ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0); + ymfpci_writel(codec, YDSXGR_WORKBASE, 0); + ymfpci_writel(codec, YDSXGR_WORKSIZE, 0); + pci_free_consistent(codec->pci, + codec->dma_area_size, codec->dma_area_va, codec->dma_area_ba); +} + +static void ymf_memload(ymfpci_t *unit) +{ + + ymfpci_writel(unit, YDSXGR_PLAYCTRLBASE, unit->ctrl_playback_ba); + ymfpci_writel(unit, YDSXGR_RECCTRLBASE, unit->bank_base_capture); + ymfpci_writel(unit, YDSXGR_EFFCTRLBASE, unit->bank_base_effect); + ymfpci_writel(unit, YDSXGR_WORKBASE, unit->work_base); + ymfpci_writel(unit, YDSXGR_WORKSIZE, unit->work_size >> 2); + + /* S/PDIF output initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFOUTCTRL, 0); + ymfpci_writew(unit, YDSXGR_SPDIFOUTSTATUS, + SND_PCM_AES0_CON_EMPHASIS_NONE | + (SND_PCM_AES1_CON_ORIGINAL << 8) | + (SND_PCM_AES1_CON_PCM_CODER << 8)); + + /* S/PDIF input initialization */ + ymfpci_writew(unit, YDSXGR_SPDIFINCTRL, 0); + + /* move this volume setup to mixer */ + ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_BUF441OUTVOL, 0); + ymfpci_writel(unit, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + ymfpci_writel(unit, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); +} + +static int ymf_ac97_init(ymfpci_t *unit, int num_ac97) +{ + struct ac97_codec *codec; + u16 eid; + + if ((codec = ac97_alloc_codec()) == NULL) + return -ENOMEM; + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = unit; + codec->id = num_ac97; + + codec->codec_read = ymfpci_codec_read; + codec->codec_write = ymfpci_codec_write; + + if (ac97_probe_codec(codec) == 0) { + printk(KERN_ERR "ymfpci: ac97_probe_codec failed\n"); + goto out_kfree; + } + + eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); + if (eid==0xFFFF) { + printk(KERN_WARNING "ymfpci: no codec attached ?\n"); + goto out_kfree; + } + + unit->ac97_features = eid; + + if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) { + printk(KERN_ERR "ymfpci: couldn't register mixer!\n"); + goto out_kfree; + } + + unit->ac97_codec[num_ac97] = codec; + + return 0; + out_kfree: + ac97_release_codec(codec); + return -ENODEV; +} + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY +# ifdef MODULE +static int mpu_io; +static int synth_io; +module_param(mpu_io, int, 0); +module_param(synth_io, int, 0); +# else +static int mpu_io = 0x330; +static int synth_io = 0x388; +# endif +static int assigned; +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + +static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + u16 ctrl; + unsigned long base; + ymfpci_t *codec; + + int err; + + if ((err = pci_enable_device(pcidev)) != 0) { + printk(KERN_ERR "ymfpci: pci_enable_device failed\n"); + return err; + } + base = pci_resource_start(pcidev, 0); + + if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "ymfpci: no core\n"); + return -ENOMEM; + } + memset(codec, 0, sizeof(*codec)); + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_lock); + spin_lock_init(&codec->ac97_lock); + mutex_init(&codec->open_mutex); + INIT_LIST_HEAD(&codec->states); + codec->pci = pcidev; + + pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); + + if (request_mem_region(base, 0x8000, "ymfpci") == NULL) { + printk(KERN_ERR "ymfpci: unable to request mem region\n"); + goto out_free; + } + + if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) { + printk(KERN_ERR "ymfpci: unable to map registers\n"); + goto out_release_region; + } + + pci_set_master(pcidev); + + printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", + (char *)ent->driver_data, base, pcidev->irq); + + ymfpci_aclink_reset(pcidev); + if (ymfpci_codec_ready(codec, 0, 1) < 0) + goto out_unmap; + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + if (assigned == 0) { + codec->iomidi = mpu_io; + codec->iosynth = synth_io; + if (ymfpci_setup_legacy(codec, pcidev) < 0) + goto out_unmap; + assigned = 1; + } +#endif + + ymfpci_download_image(codec); + + if (ymfpci_memalloc(codec) < 0) + goto out_disable_dsp; + ymf_memload(codec); + + if (request_irq(pcidev->irq, ymf_interrupt, IRQF_SHARED, "ymfpci", codec) != 0) { + printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", + pcidev->irq); + goto out_memfree; + } + + /* register /dev/dsp */ + if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) { + printk(KERN_ERR "ymfpci: unable to register dsp\n"); + goto out_free_irq; + } + + /* + * Poke just the primary for the moment. + */ + if ((err = ymf_ac97_init(codec, 0)) != 0) + goto out_unregister_sound_dsp; + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + codec->opl3_data.name = "ymfpci"; + codec->mpu_data.name = "ymfpci"; + + codec->opl3_data.io_base = codec->iosynth; + codec->opl3_data.irq = -1; + + codec->mpu_data.io_base = codec->iomidi; + codec->mpu_data.irq = -1; /* May be different from our PCI IRQ. */ + + if (codec->iomidi) { + if (!probe_uart401(&codec->mpu_data, THIS_MODULE)) { + codec->iomidi = 0; /* XXX kludge */ + } + } +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ + + /* put it into driver list */ + spin_lock(&ymf_devs_lock); + list_add_tail(&codec->ymf_devs, &ymf_devs); + spin_unlock(&ymf_devs_lock); + pci_set_drvdata(pcidev, codec); + + return 0; + + out_unregister_sound_dsp: + unregister_sound_dsp(codec->dev_audio); + out_free_irq: + free_irq(pcidev->irq, codec); + out_memfree: + ymfpci_memfree(codec); + out_disable_dsp: + ymfpci_disable_dsp(codec); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + ymfpci_writel(codec, YDSXGR_STATUS, ~0); + out_unmap: + iounmap(codec->reg_area_virt); + out_release_region: + release_mem_region(pci_resource_start(pcidev, 0), 0x8000); + out_free: + if (codec->ac97_codec[0]) + ac97_release_codec(codec->ac97_codec[0]); + return -ENODEV; +} + +static void __devexit ymf_remove_one(struct pci_dev *pcidev) +{ + __u16 ctrl; + ymfpci_t *codec = pci_get_drvdata(pcidev); + + /* remove from list of devices */ + spin_lock(&ymf_devs_lock); + list_del(&codec->ymf_devs); + spin_unlock(&ymf_devs_lock); + + unregister_sound_mixer(codec->ac97_codec[0]->dev_mixer); + ac97_release_codec(codec->ac97_codec[0]); + unregister_sound_dsp(codec->dev_audio); + free_irq(pcidev->irq, codec); + ymfpci_memfree(codec); + ymfpci_writel(codec, YDSXGR_STATUS, ~0); + ymfpci_disable_dsp(codec); + ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); + ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + iounmap(codec->reg_area_virt); + release_mem_region(pci_resource_start(pcidev, 0), 0x8000); +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + if (codec->iomidi) { + unload_uart401(&codec->mpu_data); + } +#endif /* CONFIG_SOUND_YMFPCI_LEGACY */ +} + +MODULE_AUTHOR("Jaroslav Kysela"); +MODULE_DESCRIPTION("Yamaha YMF7xx PCI Audio"); +MODULE_LICENSE("GPL"); + +static struct pci_driver ymfpci_driver = { + .name = "ymfpci", + .id_table = ymf_id_tbl, + .probe = ymf_probe_one, + .remove = __devexit_p(ymf_remove_one), + .suspend = ymf_suspend, + .resume = ymf_resume +}; + +static int __init ymf_init_module(void) +{ + return pci_register_driver(&ymfpci_driver); +} + +static void __exit ymf_cleanup_module (void) +{ + pci_unregister_driver(&ymfpci_driver); +} + +module_init(ymf_init_module); +module_exit(ymf_cleanup_module); diff --git a/trunk/sound/oss/ymfpci.h b/trunk/sound/oss/ymfpci.h new file mode 100644 index 000000000000..75a751fb9966 --- /dev/null +++ b/trunk/sound/oss/ymfpci.h @@ -0,0 +1,360 @@ +#ifndef __YMFPCI_H +#define __YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include + +/* + * Direct registers + */ + +/* #define YMFREG(codec, reg) (codec->port + YDSXGR_##reg) */ + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCTRL_TEN 0x0001 +#define YDSXGR_TIMERCTRL_TIEN 0x0002 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_SPDIFOUTVOL 0x0088 +#define YDSXGR_SPDIFOUTVOLL 0x0088 +#define YDSXGR_SPDIFOUTVOLR 0x008A +#define YDSXGR_AC3OUTVOL 0x008C +#define YDSXGR_AC3OUTVOLL 0x008C +#define YDSXGR_AC3OUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_SPDIFLOOPVOL 0x009C +#define YDSXGR_SPDIFLOOPVOLL 0x009E +#define YDSXGR_SPDIFLOOPVOLR 0x009E +#define YDSXGR_AC3LOOPVOL 0x00A0 +#define YDSXGR_AC3LOOPVOLL 0x00A0 +#define YDSXGR_AC3LOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL2 0x00B8 +#define YDSXGR_SPDIFOUTVOL2L 0x00B8 +#define YDSXGR_SPDIFOUTVOL2R 0x00BA +#define YDSXGR_SPDIFLOOPVOL2 0x00BC +#define YDSXGR_SPDIFLOOPVOL2L 0x00BC +#define YDSXGR_SPDIFLOOPVOL2R 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_LEGCTRL 0x40 +#define PCIR_ELEGCTRL 0x42 +#define PCIR_DSXGCTRL 0x48 +#define PCIR_DSXPWRCTRL1 0x4a +#define PCIR_DSXPWRCTRL2 0x4e +#define PCIR_OPLADR 0x60 +#define PCIR_SBADR 0x62 +#define PCIR_MPUADR 0x64 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +#define YMF_SAMPF 256 /* Samples per frame @48000 */ + +/* + * The slot/voice control bank (2 of these per voice) + */ + +typedef struct stru_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; /* P3: Always 0 for some reason. */ + u32 num_of_frames; + u32 loop_count; + u32 start; /* P3: J. reads this to know where chip is. */ + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; +} ymfpci_playback_bank_t; + +typedef struct stru_ymfpci_capture_bank { + u32 base; /* 32-bit address (aligned at 4) */ + u32 loop_end; /* size in BYTES (aligned at 4) */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +} ymfpci_capture_bank_t; + +typedef struct stru_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +} ymfpci_effect_bank_t; + +typedef struct ymf_voice ymfpci_voice_t; +/* + * Throughout the code Yaroslav names YMF unit pointer "codec" + * even though it does not correspond to any codec. Must be historic. + * We replace it with "unit" over time. + * AC97 parts use "codec" to denote a codec, naturally. + */ +typedef struct ymf_unit ymfpci_t; + +typedef enum { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +} ymfpci_voice_type_t; + +struct ymf_voice { + // ymfpci_t *codec; + int number; + char use, pcm, synth, midi; // bool + ymfpci_playback_bank_t *bank; + struct ymf_pcm *ypcm; + dma_addr_t bank_ba; +}; + +struct ymf_capture { + // struct ymf_unit *unit; + int use; + ymfpci_capture_bank_t *bank; + struct ymf_pcm *ypcm; +}; + +struct ymf_unit { + u8 rev; /* PCI revision */ + void __iomem *reg_area_virt; + void *dma_area_va; + dma_addr_t dma_area_ba; + unsigned int dma_area_size; + + dma_addr_t bank_base_capture; + dma_addr_t bank_base_effect; + dma_addr_t work_base; + unsigned int work_size; + + u32 *ctrl_playback; + dma_addr_t ctrl_playback_ba; + ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + ymfpci_capture_bank_t *bank_capture[YDSXG_CAPTURE_VOICES][2]; + ymfpci_effect_bank_t *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + int suspended; + + u32 active_bank; + struct ymf_voice voices[YDSXG_PLAYBACK_VOICES]; + struct ymf_capture capture[YDSXG_CAPTURE_VOICES]; + + struct ac97_codec *ac97_codec[NR_AC97]; + u16 ac97_features; + + struct pci_dev *pci; + +#ifdef CONFIG_SOUND_YMFPCI_LEGACY + /* legacy hardware resources */ + unsigned int iosynth, iomidi; + struct address_info opl3_data, mpu_data; +#endif + + spinlock_t reg_lock; + spinlock_t voice_lock; + spinlock_t ac97_lock; + + /* soundcore stuff */ + int dev_audio; + struct mutex open_mutex; + + struct list_head ymf_devs; + struct list_head states; /* List of states for this unit */ +}; + +struct ymf_dmabuf { + dma_addr_t dma_addr; + void *rawbuf; + unsigned buforder; + + /* OSS buffer management stuff */ + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started */ + unsigned swptr; /* where driver last clear/filled */ + int count; /* fill count */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; /* Total rawbuf[] size */ + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +}; + +struct ymf_pcm_format { + int format; /* OSS format */ + int rate; /* rate in Hz */ + int voices; /* number of voices */ + int shift; /* redundant, computed from the above */ +}; + +typedef enum { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +} ymfpci_pcm_type_t; + +/* This is variant record, but we hate unions. Little waste on pointers []. */ +struct ymf_pcm { + ymfpci_pcm_type_t type; + struct ymf_state *state; + + ymfpci_voice_t *voices[2]; + int capture_bank_number; + + struct ymf_dmabuf dmabuf; + int running; + int spdif; +}; + +/* + * "Software" or virtual channel, an instance of opened /dev/dsp. + * It may have two physical channels (pcms) for duplex operations. + */ + +struct ymf_state { + struct list_head chain; + struct ymf_unit *unit; /* backpointer */ + struct ymf_pcm rpcm, wpcm; + struct ymf_pcm_format format; +}; + +#endif /* __YMFPCI_H */ diff --git a/trunk/sound/oss/ymfpci_image.h b/trunk/sound/oss/ymfpci_image.h new file mode 100644 index 000000000000..112f2fff6c8e --- /dev/null +++ b/trunk/sound/oss/ymfpci_image.h @@ -0,0 +1,1565 @@ +#ifndef _HWMCODE_ +#define _HWMCODE_ + +static u32 DspInst[YDSXG_DSPLENGTH / 4] = { + 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, + 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, + 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, + 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static u32 CntrlInst[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x001A82, 0x032D0D, 0x000810, 0x10043A, + 0x02D38D, 0x000810, 0x18043A, 0x00010D, + 0x020015, 0x0000FD, 0x000020, 0x038860, + 0x039060, 0x038060, 0x038040, 0x038040, + 0x038040, 0x018040, 0x000A7D, 0x038040, + 0x038040, 0x018040, 0x200402, 0x000882, + 0x08001A, 0x000904, 0x015986, 0x000007, + 0x260007, 0x000007, 0x000007, 0x018A06, + 0x000007, 0x030C8D, 0x000810, 0x18043A, + 0x260007, 0x00087D, 0x018042, 0x00160A, + 0x04A206, 0x000007, 0x00218D, 0x000810, + 0x08043A, 0x21C206, 0x000007, 0x0007FD, + 0x018042, 0x08000A, 0x000904, 0x029386, + 0x000195, 0x090D04, 0x000007, 0x000820, + 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, + 0x032206, 0x018040, 0x000A7D, 0x038042, + 0x13804A, 0x18000A, 0x001820, 0x059060, + 0x058860, 0x018040, 0x0000FD, 0x018042, + 0x70000A, 0x000115, 0x071144, 0x032386, + 0x030000, 0x007020, 0x034A06, 0x018040, + 0x00348D, 0x000810, 0x08043A, 0x21EA06, + 0x000007, 0x02D38D, 0x000810, 0x18043A, + 0x018206, 0x000007, 0x240007, 0x000F8D, + 0x000810, 0x00163A, 0x002402, 0x005C02, + 0x0028FD, 0x000020, 0x018040, 0x08000D, + 0x000815, 0x510984, 0x000007, 0x00004D, + 0x000E5D, 0x000E02, 0x00418D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00008D, + 0x000924, 0x000F02, 0x00458D, 0x000810, + 0x08043A, 0x2C8A06, 0x000007, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x018386, 0x000007, 0x01AA06, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x218086, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x055A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x034986, 0x000007, 0x002104, 0x034986, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x06C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00688D, 0x000810, 0x08043A, 0x288A06, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x060206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x215886, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x212086, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x07DA86, 0x00057D, 0x002820, + 0x03B060, 0x07F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x07FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x008D8D, 0x000810, + 0x08043A, 0x288A06, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x095186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x007FBD, 0x383DC4, + 0x000007, 0x001A7D, 0x001375, 0x018042, + 0x09004A, 0x10000A, 0x0B8D04, 0x139504, + 0x000007, 0x000820, 0x019060, 0x001104, + 0x212086, 0x010040, 0x0017FD, 0x018042, + 0x08000A, 0x000904, 0x212286, 0x000007, + 0x00197D, 0x038042, 0x09804A, 0x10000A, + 0x000924, 0x001664, 0x0011FD, 0x038042, + 0x2B804A, 0x19804A, 0x00008D, 0x218944, + 0x000007, 0x002244, 0x0AE186, 0x000007, + 0x001A64, 0x002A24, 0x00197D, 0x080102, + 0x100122, 0x000820, 0x039060, 0x018040, + 0x003DFD, 0x00008D, 0x000820, 0x018040, + 0x001375, 0x001A7D, 0x010042, 0x09804A, + 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, + 0x309144, 0x000007, 0x00060D, 0x000A15, + 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, + 0x000464, 0x01B3E4, 0x0232E4, 0x000464, + 0x000464, 0x000464, 0x000464, 0x00040D, + 0x08B1C4, 0x000007, 0x000820, 0x000BF5, + 0x030040, 0x00197D, 0x038042, 0x09804A, + 0x000A24, 0x08000A, 0x080E64, 0x000007, + 0x100122, 0x000820, 0x031060, 0x010040, + 0x0064AC, 0x00027D, 0x000020, 0x018040, + 0x00107D, 0x018042, 0x0011FD, 0x3B804A, + 0x09804A, 0x20000A, 0x000095, 0x1A1144, + 0x00A144, 0x0D2086, 0x00040D, 0x00B984, + 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, + 0x09804A, 0x28000A, 0x000095, 0x010924, + 0x002A64, 0x0D1186, 0x000007, 0x002904, + 0x0D2286, 0x000007, 0x0D2A06, 0x080002, + 0x00008D, 0x00387D, 0x000820, 0x018040, + 0x00127D, 0x018042, 0x10000A, 0x003904, + 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, + 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, + 0x000015, 0x00082D, 0x02C78D, 0x000820, + 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, + 0x0E7186, 0x400025, 0x00008D, 0x110944, + 0x000007, 0x00018D, 0x109504, 0x000007, + 0x009164, 0x000424, 0x000424, 0x000424, + 0x100102, 0x280002, 0x02C68D, 0x000820, + 0x0EC206, 0x00018D, 0x00042D, 0x00008D, + 0x109504, 0x000007, 0x00020D, 0x109184, + 0x000007, 0x02C70D, 0x000820, 0x00008D, + 0x0038FD, 0x018040, 0x003BFD, 0x001020, + 0x03A860, 0x000815, 0x313184, 0x212184, + 0x000007, 0x03B060, 0x03A060, 0x018040, + 0x0022FD, 0x000095, 0x010924, 0x000424, + 0x000424, 0x001264, 0x100102, 0x000820, + 0x039060, 0x018040, 0x001924, 0x00FB8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x000424, + 0x000424, 0x00117D, 0x018042, 0x08000A, + 0x000A24, 0x280502, 0x280C02, 0x09800D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x0022FD, 0x018042, 0x08000A, 0x000095, + 0x280DC4, 0x011924, 0x00197D, 0x018042, + 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, + 0x113144, 0x0A8D04, 0x000007, 0x080A44, + 0x129504, 0x000007, 0x0023FD, 0x001020, + 0x038040, 0x101244, 0x000007, 0x000820, + 0x039060, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x10FA86, 0x000007, + 0x003BFD, 0x000100, 0x000A10, 0x0B807A, + 0x13804A, 0x090984, 0x000007, 0x000095, + 0x013D04, 0x118086, 0x10000A, 0x100002, + 0x090984, 0x000007, 0x038042, 0x11804A, + 0x090D04, 0x000007, 0x10000A, 0x090D84, + 0x000007, 0x00257D, 0x000820, 0x018040, + 0x00010D, 0x000810, 0x28143A, 0x00127D, + 0x018042, 0x20000A, 0x00197D, 0x018042, + 0x00117D, 0x31804A, 0x10000A, 0x003124, + 0x01280D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x300102, 0x003124, 0x000424, 0x000424, + 0x001224, 0x280502, 0x001A4C, 0x130186, + 0x700002, 0x00002D, 0x030000, 0x00387D, + 0x018042, 0x10000A, 0x132A06, 0x002124, + 0x0000AD, 0x100002, 0x00010D, 0x000924, + 0x006B24, 0x01368D, 0x00397D, 0x000820, + 0x058040, 0x038042, 0x09844A, 0x000606, + 0x08040A, 0x003264, 0x00008D, 0x000A24, + 0x001020, 0x00227D, 0x018040, 0x013C0D, + 0x000810, 0x08043A, 0x29D206, 0x000007, + 0x002820, 0x00207D, 0x018040, 0x00117D, + 0x038042, 0x13804A, 0x33800A, 0x00387D, + 0x018042, 0x08000A, 0x000904, 0x163A86, + 0x000007, 0x00008D, 0x030964, 0x01478D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x380102, + 0x000424, 0x000424, 0x001224, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x14A286, + 0x000007, 0x280502, 0x001A4C, 0x163986, + 0x000007, 0x032164, 0x00632C, 0x003DFD, + 0x018042, 0x08000A, 0x000095, 0x090904, + 0x000007, 0x000820, 0x001A4C, 0x156186, + 0x018040, 0x030000, 0x157A06, 0x002124, + 0x00010D, 0x000924, 0x006B24, 0x015B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x003A64, + 0x000095, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x01628D, 0x000810, 0x08043A, 0x29D206, + 0x000007, 0x14D206, 0x000007, 0x007020, + 0x08010A, 0x10012A, 0x0020FD, 0x038860, + 0x039060, 0x018040, 0x00227D, 0x018042, + 0x003DFD, 0x08000A, 0x31844A, 0x000904, + 0x16D886, 0x18008B, 0x00008D, 0x189904, + 0x00312C, 0x17AA06, 0x000007, 0x00324C, + 0x173386, 0x000007, 0x001904, 0x173086, + 0x000007, 0x000095, 0x199144, 0x00222C, + 0x003124, 0x00636C, 0x000E3D, 0x001375, + 0x000BFD, 0x010042, 0x09804A, 0x10000A, + 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, + 0x000007, 0x00008D, 0x189904, 0x00226C, + 0x00322C, 0x30050A, 0x301DAB, 0x002083, + 0x0018FD, 0x018042, 0x08000A, 0x018924, + 0x300502, 0x001083, 0x001875, 0x010042, + 0x10000A, 0x00008D, 0x010924, 0x001375, + 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, + 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, + 0x305C8B, 0x006083, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x187A86, 0x000007, + 0x001E2D, 0x0005FD, 0x018042, 0x08000A, + 0x028924, 0x280502, 0x00060D, 0x000810, + 0x280C3A, 0x00008D, 0x000810, 0x28143A, + 0x0A808D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001275, 0x030042, 0x21004A, + 0x00008D, 0x1A0944, 0x000007, 0x01980D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0001F5, 0x030042, 0x0D004A, 0x10000A, + 0x089144, 0x000007, 0x000820, 0x010040, + 0x0025F5, 0x0A3144, 0x000007, 0x000820, + 0x032860, 0x030040, 0x00217D, 0x038042, + 0x0B804A, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00008D, 0x000124, 0x00012C, + 0x000E64, 0x001A64, 0x00636C, 0x08010A, + 0x10012A, 0x000820, 0x031060, 0x030040, + 0x0020FD, 0x018042, 0x08000A, 0x00227D, + 0x018042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x00197D, 0x018042, 0x08000A, + 0x0022FD, 0x038042, 0x10000A, 0x000820, + 0x031060, 0x030040, 0x090D04, 0x000007, + 0x000820, 0x030040, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x038042, 0x13804A, 0x19804A, 0x110D04, + 0x198D04, 0x000007, 0x08000A, 0x001020, + 0x031860, 0x030860, 0x030040, 0x00008D, + 0x0B0944, 0x000007, 0x000820, 0x010040, + 0x0005F5, 0x030042, 0x08000A, 0x000820, + 0x010040, 0x0000F5, 0x010042, 0x08000A, + 0x000904, 0x1C6086, 0x001E75, 0x030042, + 0x01044A, 0x000C0A, 0x1C7206, 0x000007, + 0x000402, 0x000C02, 0x00177D, 0x001AF5, + 0x018042, 0x03144A, 0x031C4A, 0x03244A, + 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, + 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, + 0x030042, 0x0B004A, 0x1B804A, 0x13804A, + 0x20000A, 0x089144, 0x19A144, 0x0389E4, + 0x0399EC, 0x005502, 0x005D0A, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x006502, 0x006D0A, 0x030042, 0x0B004A, + 0x19004A, 0x2B804A, 0x13804A, 0x21804A, + 0x30000A, 0x089144, 0x19A144, 0x2AB144, + 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, + 0x03A9E4, 0x000702, 0x00107D, 0x000415, + 0x018042, 0x08000A, 0x0109E4, 0x000F02, + 0x002AF5, 0x0019FD, 0x010042, 0x09804A, + 0x10000A, 0x000934, 0x001674, 0x0029F5, + 0x010042, 0x10000A, 0x00917C, 0x002075, + 0x010042, 0x08000A, 0x000904, 0x1ED286, + 0x0026F5, 0x0027F5, 0x030042, 0x09004A, + 0x10000A, 0x000A3C, 0x00167C, 0x001A75, + 0x000BFD, 0x010042, 0x51804A, 0x48000A, + 0x160007, 0x001075, 0x010042, 0x282C0A, + 0x281D12, 0x282512, 0x001F32, 0x1E0007, + 0x0E0007, 0x001975, 0x010042, 0x002DF5, + 0x0D004A, 0x10000A, 0x009144, 0x1FB286, + 0x010042, 0x28340A, 0x000E5D, 0x00008D, + 0x000375, 0x000820, 0x010040, 0x05D2F4, + 0x54D104, 0x00735C, 0x205386, 0x000007, + 0x0C0007, 0x080007, 0x0A0007, 0x02040D, + 0x000810, 0x08043A, 0x332206, 0x000007, + 0x205A06, 0x000007, 0x080007, 0x002275, + 0x010042, 0x20000A, 0x002104, 0x212086, + 0x001E2D, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x209286, 0x000007, 0x002010, + 0x30043A, 0x00057D, 0x0180C3, 0x08000A, + 0x028924, 0x280502, 0x280C02, 0x0A810D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x0004FD, 0x018042, 0x70000A, 0x030000, + 0x007020, 0x06FA06, 0x018040, 0x02180D, + 0x000810, 0x08043A, 0x2B2206, 0x000007, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x218A86, 0x000007, 0x01F206, 0x000007, + 0x000875, 0x0009FD, 0x00010D, 0x220A06, + 0x000295, 0x000B75, 0x00097D, 0x00000D, + 0x000515, 0x010042, 0x18000A, 0x001904, + 0x287886, 0x0006F5, 0x001020, 0x010040, + 0x0004F5, 0x000820, 0x010040, 0x000775, + 0x010042, 0x09804A, 0x10000A, 0x001124, + 0x000904, 0x22BA86, 0x000815, 0x080102, + 0x101204, 0x22DA06, 0x000575, 0x081204, + 0x000007, 0x100102, 0x000575, 0x000425, + 0x021124, 0x100102, 0x000820, 0x031060, + 0x010040, 0x001924, 0x287886, 0x00008D, + 0x000464, 0x009D04, 0x278886, 0x180102, + 0x000575, 0x010042, 0x28040A, 0x00018D, + 0x000924, 0x280D02, 0x00000D, 0x000924, + 0x281502, 0x10000D, 0x000820, 0x0002F5, + 0x010040, 0x200007, 0x001175, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x23C286, + 0x000007, 0x000100, 0x080B20, 0x130B60, + 0x1B0B60, 0x030A60, 0x010040, 0x050042, + 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, + 0x0006F5, 0x010042, 0x28140A, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x24CA86, 0x004015, 0x000095, 0x010D04, + 0x24B886, 0x100022, 0x10002A, 0x24E206, + 0x000007, 0x333104, 0x2AA904, 0x000007, + 0x032124, 0x280502, 0x001124, 0x000424, + 0x000424, 0x003224, 0x00292C, 0x00636C, + 0x25F386, 0x000007, 0x02B164, 0x000464, + 0x000464, 0x00008D, 0x000A64, 0x280D02, + 0x10008D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x00008D, 0x38B904, 0x000007, + 0x03296C, 0x30010A, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x25BA86, 0x000007, + 0x02312C, 0x28050A, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x267A86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x26C086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x26CA86, 0x000007, 0x003124, 0x300502, + 0x003924, 0x300583, 0x000883, 0x0005F5, + 0x010042, 0x28040A, 0x00008D, 0x008124, + 0x280D02, 0x00008D, 0x008124, 0x281502, + 0x10018D, 0x000820, 0x0002F5, 0x010040, + 0x220007, 0x001025, 0x000575, 0x030042, + 0x09004A, 0x10000A, 0x0A0904, 0x121104, + 0x000007, 0x001020, 0x050860, 0x050040, + 0x0006FD, 0x018042, 0x09004A, 0x10000A, + 0x0000A5, 0x0A0904, 0x121104, 0x000007, + 0x000820, 0x019060, 0x010040, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x284286, + 0x000007, 0x230A06, 0x000007, 0x000606, + 0x000007, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x289286, 0x000007, 0x000100, + 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, + 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, + 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, + 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, + 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, + 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, + 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, + 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, + 0x000606, 0x018040, 0x00008D, 0x000A64, + 0x280D02, 0x000A24, 0x00027D, 0x018042, + 0x10000A, 0x001224, 0x0003FD, 0x018042, + 0x08000A, 0x000904, 0x2A8286, 0x000007, + 0x00018D, 0x000A24, 0x000464, 0x000464, + 0x080102, 0x000924, 0x000424, 0x000424, + 0x100102, 0x02000D, 0x009144, 0x2AD986, + 0x000007, 0x0001FD, 0x018042, 0x08000A, + 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, + 0x000820, 0x0002FD, 0x018040, 0x200007, + 0x00027D, 0x001020, 0x000606, 0x018040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x2B2A86, 0x000007, 0x00037D, 0x018042, + 0x08000A, 0x000904, 0x2B5A86, 0x000007, + 0x000075, 0x002E7D, 0x010042, 0x0B804A, + 0x000020, 0x000904, 0x000686, 0x010040, + 0x31844A, 0x30048B, 0x000883, 0x00008D, + 0x000810, 0x28143A, 0x00008D, 0x000810, + 0x280C3A, 0x000675, 0x010042, 0x08000A, + 0x003815, 0x010924, 0x280502, 0x0B000D, + 0x000820, 0x0002F5, 0x010040, 0x000606, + 0x220007, 0x000464, 0x000464, 0x000606, + 0x000007, 0x000134, 0x007F8D, 0x00093C, + 0x281D12, 0x282512, 0x001F32, 0x0E0007, + 0x00010D, 0x00037D, 0x000820, 0x018040, + 0x05D2F4, 0x000007, 0x080007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2D0286, + 0x000007, 0x000606, 0x000007, 0x000007, + 0x000012, 0x100007, 0x320007, 0x600007, + 0x100080, 0x48001A, 0x004904, 0x2D6186, + 0x000007, 0x001210, 0x58003A, 0x000145, + 0x5C5D04, 0x000007, 0x000080, 0x48001A, + 0x004904, 0x2DB186, 0x000007, 0x001210, + 0x50003A, 0x005904, 0x2E0886, 0x000045, + 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, + 0x004224, 0x500102, 0x200502, 0x000082, + 0x40001A, 0x004104, 0x2E3986, 0x000007, + 0x003865, 0x40001A, 0x004020, 0x00104D, + 0x04C184, 0x301B86, 0x000040, 0x040007, + 0x000165, 0x000145, 0x004020, 0x000040, + 0x000765, 0x080080, 0x40001A, 0x004104, + 0x2EC986, 0x000007, 0x001210, 0x40003A, + 0x004104, 0x2F2286, 0x00004D, 0x0000CD, + 0x004810, 0x20043A, 0x000882, 0x40001A, + 0x004104, 0x2F3186, 0x000007, 0x004820, + 0x005904, 0x300886, 0x000040, 0x0007E5, + 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, + 0x4216E0, 0x021260, 0x000040, 0x000032, + 0x400075, 0x00007D, 0x07D574, 0x200512, + 0x000082, 0x40001A, 0x004104, 0x2FE186, + 0x000007, 0x037206, 0x640007, 0x060007, + 0x0000E5, 0x000020, 0x000040, 0x000A65, + 0x000020, 0x020040, 0x020040, 0x000040, + 0x000165, 0x000042, 0x70000A, 0x007104, + 0x30A286, 0x000007, 0x018206, 0x640007, + 0x050000, 0x007020, 0x000040, 0x037206, + 0x640007, 0x000007, 0x00306D, 0x028860, + 0x029060, 0x08000A, 0x028860, 0x008040, + 0x100012, 0x00100D, 0x009184, 0x314186, + 0x000E0D, 0x009184, 0x325186, 0x000007, + 0x300007, 0x001020, 0x003B6D, 0x008040, + 0x000080, 0x08001A, 0x000904, 0x316186, + 0x000007, 0x001220, 0x000DED, 0x008040, + 0x008042, 0x10000A, 0x40000D, 0x109544, + 0x000007, 0x001020, 0x000DED, 0x008040, + 0x008042, 0x20040A, 0x000082, 0x08001A, + 0x000904, 0x31F186, 0x000007, 0x003B6D, + 0x008042, 0x08000A, 0x000E15, 0x010984, + 0x329B86, 0x600007, 0x08001A, 0x000C15, + 0x010984, 0x328386, 0x000020, 0x1A0007, + 0x0002ED, 0x008040, 0x620007, 0x00306D, + 0x028042, 0x0A804A, 0x000820, 0x0A804A, + 0x000606, 0x10804A, 0x000007, 0x282512, + 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, + 0x000786, 0x000007, 0x0C0007, 0x0A0007, + 0x1C0007, 0x003465, 0x020040, 0x004820, + 0x025060, 0x40000A, 0x024060, 0x000040, + 0x454944, 0x000007, 0x004020, 0x003AE5, + 0x000040, 0x0028E5, 0x000042, 0x48000A, + 0x004904, 0x386886, 0x002C65, 0x000042, + 0x40000A, 0x0000D5, 0x454104, 0x000007, + 0x000655, 0x054504, 0x34F286, 0x0001D5, + 0x054504, 0x34F086, 0x002B65, 0x000042, + 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, + 0x000007, 0x454504, 0x000007, 0x0000CD, + 0x444944, 0x000007, 0x454504, 0x000007, + 0x00014D, 0x554944, 0x000007, 0x045144, + 0x34E986, 0x002C65, 0x000042, 0x48000A, + 0x4CD104, 0x000007, 0x04C144, 0x34F386, + 0x000007, 0x160007, 0x002CE5, 0x040042, + 0x40000A, 0x004020, 0x000040, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x356086, + 0x000007, 0x002402, 0x36A206, 0x005C02, + 0x0025E5, 0x000042, 0x40000A, 0x004274, + 0x002AE5, 0x000042, 0x40000A, 0x004274, + 0x500112, 0x0029E5, 0x000042, 0x40000A, + 0x004234, 0x454104, 0x000007, 0x004020, + 0x000040, 0x003EE5, 0x000020, 0x000040, + 0x002DE5, 0x400152, 0x50000A, 0x045144, + 0x364A86, 0x0000C5, 0x003EE5, 0x004020, + 0x000040, 0x002BE5, 0x000042, 0x40000A, + 0x404254, 0x000007, 0x002AE5, 0x004020, + 0x000040, 0x500132, 0x040134, 0x005674, + 0x0029E5, 0x020042, 0x42000A, 0x000042, + 0x50000A, 0x05417C, 0x0028E5, 0x000042, + 0x48000A, 0x0000C5, 0x4CC144, 0x371086, + 0x0026E5, 0x0027E5, 0x020042, 0x40004A, + 0x50000A, 0x00423C, 0x00567C, 0x0028E5, + 0x004820, 0x000040, 0x281D12, 0x282512, + 0x001F72, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x37AA86, 0x0E0007, 0x160007, + 0x1E0007, 0x003EE5, 0x000042, 0x40000A, + 0x004104, 0x37E886, 0x002D65, 0x000042, + 0x28340A, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, + 0x54D104, 0x00735C, 0x385186, 0x000007, + 0x000606, 0x080007, 0x0C0007, 0x080007, + 0x0A0007, 0x0001E5, 0x020045, 0x004020, + 0x000060, 0x000365, 0x000040, 0x002E65, + 0x001A20, 0x0A1A60, 0x000040, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x000606, 0x50004A, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +// -------------------------------------------- +// DS-1E Controller InstructionRAM Code +// 1999/06/21 +// Buf441 slot is Enabled. +// -------------------------------------------- +// 04/09 creat +// 04/12 stop nise fix +// 06/21 WorkingOff timming +static u32 CntrlInst1E[YDSXG_CTRLLENGTH / 4] = { + 0x000007, 0x240007, 0x0C0007, 0x1C0007, + 0x060007, 0x700002, 0x000020, 0x030040, + 0x007104, 0x004286, 0x030040, 0x000F0D, + 0x000810, 0x20043A, 0x000282, 0x00020D, + 0x000810, 0x20043A, 0x001282, 0x200E82, + 0x00800D, 0x000810, 0x20043A, 0x001A82, + 0x03460D, 0x000810, 0x10043A, 0x02EC0D, + 0x000810, 0x18043A, 0x00010D, 0x020015, + 0x0000FD, 0x000020, 0x038860, 0x039060, + 0x038060, 0x038040, 0x038040, 0x038040, + 0x018040, 0x000A7D, 0x038040, 0x038040, + 0x018040, 0x200402, 0x000882, 0x08001A, + 0x000904, 0x017186, 0x000007, 0x260007, + 0x400007, 0x000007, 0x03258D, 0x000810, + 0x18043A, 0x260007, 0x284402, 0x00087D, + 0x018042, 0x00160A, 0x05A206, 0x000007, + 0x440007, 0x00230D, 0x000810, 0x08043A, + 0x22FA06, 0x000007, 0x0007FD, 0x018042, + 0x08000A, 0x000904, 0x02AB86, 0x000195, + 0x090D04, 0x000007, 0x000820, 0x0000F5, + 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, + 0x018040, 0x000A7D, 0x038042, 0x13804A, + 0x18000A, 0x001820, 0x059060, 0x058860, + 0x018040, 0x0000FD, 0x018042, 0x70000A, + 0x000115, 0x071144, 0x033B86, 0x030000, + 0x007020, 0x036206, 0x018040, 0x00360D, + 0x000810, 0x08043A, 0x232206, 0x000007, + 0x02EC0D, 0x000810, 0x18043A, 0x019A06, + 0x000007, 0x240007, 0x000F8D, 0x000810, + 0x00163A, 0x002402, 0x005C02, 0x0028FD, + 0x000020, 0x018040, 0x08000D, 0x000815, + 0x510984, 0x000007, 0x00004D, 0x000E5D, + 0x000E02, 0x00430D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x00008D, 0x000924, + 0x000F02, 0x00470D, 0x000810, 0x08043A, + 0x2E1206, 0x000007, 0x480480, 0x001210, + 0x28043A, 0x00778D, 0x000810, 0x280C3A, + 0x00068D, 0x000810, 0x28143A, 0x284402, + 0x03258D, 0x000810, 0x18043A, 0x07FF8D, + 0x000820, 0x0002FD, 0x018040, 0x260007, + 0x200007, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x051286, 0x000007, 0x240007, + 0x02EC0D, 0x000810, 0x18043A, 0x00387D, + 0x018042, 0x08000A, 0x001015, 0x010984, + 0x019B86, 0x000007, 0x01B206, 0x000007, + 0x0008FD, 0x018042, 0x18000A, 0x001904, + 0x22B886, 0x280007, 0x001810, 0x28043A, + 0x280C02, 0x00000D, 0x000810, 0x28143A, + 0x08808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00020D, 0x189904, 0x000007, + 0x00402D, 0x0000BD, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x065A86, 0x000007, + 0x000100, 0x000A20, 0x00047D, 0x018040, + 0x018042, 0x20000A, 0x003015, 0x012144, + 0x036186, 0x000007, 0x002104, 0x036186, + 0x000007, 0x000F8D, 0x000810, 0x280C3A, + 0x023944, 0x07C986, 0x000007, 0x001810, + 0x28043A, 0x08810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x002810, 0x78003A, + 0x00788D, 0x000810, 0x08043A, 0x2A1206, + 0x000007, 0x00400D, 0x001015, 0x189904, + 0x292904, 0x393904, 0x000007, 0x070206, + 0x000007, 0x0004F5, 0x00007D, 0x000020, + 0x00008D, 0x010860, 0x018040, 0x00047D, + 0x038042, 0x21804A, 0x18000A, 0x021944, + 0x229086, 0x000007, 0x004075, 0x71F104, + 0x000007, 0x010042, 0x28000A, 0x002904, + 0x225886, 0x000007, 0x003C0D, 0x30A904, + 0x000007, 0x00077D, 0x018042, 0x08000A, + 0x000904, 0x08DA86, 0x00057D, 0x002820, + 0x03B060, 0x08F206, 0x018040, 0x003020, + 0x03A860, 0x018040, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x08FA86, 0x000007, + 0x00057D, 0x018042, 0x28040A, 0x000E8D, + 0x000810, 0x280C3A, 0x00000D, 0x000810, + 0x28143A, 0x09000D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x003DFD, 0x000020, + 0x018040, 0x00107D, 0x009D8D, 0x000810, + 0x08043A, 0x2A1206, 0x000007, 0x000815, + 0x08001A, 0x010984, 0x0A5186, 0x00137D, + 0x200500, 0x280F20, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x038A60, 0x018040, 0x00107D, 0x018042, + 0x08000A, 0x000215, 0x010984, 0x3A8186, + 0x000007, 0x007FBD, 0x383DC4, 0x000007, + 0x001A7D, 0x001375, 0x018042, 0x09004A, + 0x10000A, 0x0B8D04, 0x139504, 0x000007, + 0x000820, 0x019060, 0x001104, 0x225886, + 0x010040, 0x0017FD, 0x018042, 0x08000A, + 0x000904, 0x225A86, 0x000007, 0x00197D, + 0x038042, 0x09804A, 0x10000A, 0x000924, + 0x001664, 0x0011FD, 0x038042, 0x2B804A, + 0x19804A, 0x00008D, 0x218944, 0x000007, + 0x002244, 0x0C1986, 0x000007, 0x001A64, + 0x002A24, 0x00197D, 0x080102, 0x100122, + 0x000820, 0x039060, 0x018040, 0x003DFD, + 0x00008D, 0x000820, 0x018040, 0x001375, + 0x001A7D, 0x010042, 0x09804A, 0x10000A, + 0x00021D, 0x0189E4, 0x2992E4, 0x309144, + 0x000007, 0x00060D, 0x000A15, 0x000C1D, + 0x001025, 0x00A9E4, 0x012BE4, 0x000464, + 0x01B3E4, 0x0232E4, 0x000464, 0x000464, + 0x000464, 0x000464, 0x00040D, 0x08B1C4, + 0x000007, 0x000820, 0x000BF5, 0x030040, + 0x00197D, 0x038042, 0x09804A, 0x000A24, + 0x08000A, 0x080E64, 0x000007, 0x100122, + 0x000820, 0x031060, 0x010040, 0x0064AC, + 0x00027D, 0x000020, 0x018040, 0x00107D, + 0x018042, 0x0011FD, 0x3B804A, 0x09804A, + 0x20000A, 0x000095, 0x1A1144, 0x00A144, + 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, + 0x0018FD, 0x018042, 0x0010FD, 0x09804A, + 0x28000A, 0x000095, 0x010924, 0x002A64, + 0x0E4986, 0x000007, 0x002904, 0x0E5A86, + 0x000007, 0x0E6206, 0x080002, 0x00008D, + 0x00387D, 0x000820, 0x018040, 0x00127D, + 0x018042, 0x10000A, 0x003904, 0x0F0986, + 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, + 0x000025, 0x0FB206, 0x00002D, 0x000015, + 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, + 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, + 0x400025, 0x00008D, 0x110944, 0x000007, + 0x00018D, 0x109504, 0x000007, 0x009164, + 0x000424, 0x000424, 0x000424, 0x100102, + 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, + 0x00018D, 0x00042D, 0x00008D, 0x109504, + 0x000007, 0x00020D, 0x109184, 0x000007, + 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, + 0x018040, 0x003BFD, 0x001020, 0x03A860, + 0x000815, 0x313184, 0x212184, 0x000007, + 0x03B060, 0x03A060, 0x018040, 0x0022FD, + 0x000095, 0x010924, 0x000424, 0x000424, + 0x001264, 0x100102, 0x000820, 0x039060, + 0x018040, 0x001924, 0x010F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x000424, 0x000424, + 0x00117D, 0x018042, 0x08000A, 0x000A24, + 0x280502, 0x280C02, 0x09800D, 0x000820, + 0x0002FD, 0x018040, 0x200007, 0x0022FD, + 0x018042, 0x08000A, 0x000095, 0x280DC4, + 0x011924, 0x00197D, 0x018042, 0x0011FD, + 0x09804A, 0x10000A, 0x0000B5, 0x113144, + 0x0A8D04, 0x000007, 0x080A44, 0x129504, + 0x000007, 0x0023FD, 0x001020, 0x038040, + 0x101244, 0x000007, 0x000820, 0x039060, + 0x018040, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x123286, 0x000007, 0x003BFD, + 0x000100, 0x000A10, 0x0B807A, 0x13804A, + 0x090984, 0x000007, 0x000095, 0x013D04, + 0x12B886, 0x10000A, 0x100002, 0x090984, + 0x000007, 0x038042, 0x11804A, 0x090D04, + 0x000007, 0x10000A, 0x090D84, 0x000007, + 0x00257D, 0x000820, 0x018040, 0x00010D, + 0x000810, 0x28143A, 0x00127D, 0x018042, + 0x20000A, 0x00197D, 0x018042, 0x00117D, + 0x31804A, 0x10000A, 0x003124, 0x013B8D, + 0x00397D, 0x000820, 0x058040, 0x038042, + 0x09844A, 0x000606, 0x08040A, 0x300102, + 0x003124, 0x000424, 0x000424, 0x001224, + 0x280502, 0x001A4C, 0x143986, 0x700002, + 0x00002D, 0x030000, 0x00387D, 0x018042, + 0x10000A, 0x146206, 0x002124, 0x0000AD, + 0x100002, 0x00010D, 0x000924, 0x006B24, + 0x014A0D, 0x00397D, 0x000820, 0x058040, + 0x038042, 0x09844A, 0x000606, 0x08040A, + 0x003264, 0x00008D, 0x000A24, 0x001020, + 0x00227D, 0x018040, 0x014F8D, 0x000810, + 0x08043A, 0x2B5A06, 0x000007, 0x002820, + 0x00207D, 0x018040, 0x00117D, 0x038042, + 0x13804A, 0x33800A, 0x00387D, 0x018042, + 0x08000A, 0x000904, 0x177286, 0x000007, + 0x00008D, 0x030964, 0x015B0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x380102, 0x000424, + 0x000424, 0x001224, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x15DA86, 0x000007, + 0x280502, 0x001A4C, 0x177186, 0x000007, + 0x032164, 0x00632C, 0x003DFD, 0x018042, + 0x08000A, 0x000095, 0x090904, 0x000007, + 0x000820, 0x001A4C, 0x169986, 0x018040, + 0x030000, 0x16B206, 0x002124, 0x00010D, + 0x000924, 0x006B24, 0x016F0D, 0x00397D, + 0x000820, 0x058040, 0x038042, 0x09844A, + 0x000606, 0x08040A, 0x003A64, 0x000095, + 0x001224, 0x0002FD, 0x018042, 0x08000A, + 0x000904, 0x171286, 0x000007, 0x01760D, + 0x000810, 0x08043A, 0x2B5A06, 0x000007, + 0x160A06, 0x000007, 0x007020, 0x08010A, + 0x10012A, 0x0020FD, 0x038860, 0x039060, + 0x018040, 0x00227D, 0x018042, 0x003DFD, + 0x08000A, 0x31844A, 0x000904, 0x181086, + 0x18008B, 0x00008D, 0x189904, 0x00312C, + 0x18E206, 0x000007, 0x00324C, 0x186B86, + 0x000007, 0x001904, 0x186886, 0x000007, + 0x000095, 0x199144, 0x00222C, 0x003124, + 0x00636C, 0x000E3D, 0x001375, 0x000BFD, + 0x010042, 0x09804A, 0x10000A, 0x038AEC, + 0x0393EC, 0x00224C, 0x18E186, 0x000007, + 0x00008D, 0x189904, 0x00226C, 0x00322C, + 0x30050A, 0x301DAB, 0x002083, 0x0018FD, + 0x018042, 0x08000A, 0x018924, 0x300502, + 0x001083, 0x001875, 0x010042, 0x10000A, + 0x00008D, 0x010924, 0x001375, 0x330542, + 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, + 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, + 0x006083, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x19B286, 0x000007, 0x001E2D, + 0x0005FD, 0x018042, 0x08000A, 0x028924, + 0x280502, 0x00060D, 0x000810, 0x280C3A, + 0x00008D, 0x000810, 0x28143A, 0x0A808D, + 0x000820, 0x0002F5, 0x010040, 0x220007, + 0x001275, 0x030042, 0x21004A, 0x00008D, + 0x1A0944, 0x000007, 0x01AB8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, + 0x030042, 0x0D004A, 0x10000A, 0x089144, + 0x000007, 0x000820, 0x010040, 0x0025F5, + 0x0A3144, 0x000007, 0x000820, 0x032860, + 0x030040, 0x00217D, 0x038042, 0x0B804A, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00008D, 0x000124, 0x00012C, 0x000E64, + 0x001A64, 0x00636C, 0x08010A, 0x10012A, + 0x000820, 0x031060, 0x030040, 0x0020FD, + 0x018042, 0x08000A, 0x00227D, 0x018042, + 0x10000A, 0x000820, 0x031060, 0x030040, + 0x00197D, 0x018042, 0x08000A, 0x0022FD, + 0x038042, 0x10000A, 0x000820, 0x031060, + 0x030040, 0x090D04, 0x000007, 0x000820, + 0x030040, 0x038042, 0x0B804A, 0x10000A, + 0x000820, 0x031060, 0x030040, 0x038042, + 0x13804A, 0x19804A, 0x110D04, 0x198D04, + 0x000007, 0x08000A, 0x001020, 0x031860, + 0x030860, 0x030040, 0x00008D, 0x0B0944, + 0x000007, 0x000820, 0x010040, 0x0005F5, + 0x030042, 0x08000A, 0x000820, 0x010040, + 0x0000F5, 0x010042, 0x08000A, 0x000904, + 0x1D9886, 0x001E75, 0x030042, 0x01044A, + 0x000C0A, 0x1DAA06, 0x000007, 0x000402, + 0x000C02, 0x00177D, 0x001AF5, 0x018042, + 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, + 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, + 0x00043D, 0x0013F5, 0x001AFD, 0x030042, + 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, + 0x089144, 0x19A144, 0x0389E4, 0x0399EC, + 0x005502, 0x005D0A, 0x030042, 0x0B004A, + 0x1B804A, 0x13804A, 0x20000A, 0x089144, + 0x19A144, 0x0389E4, 0x0399EC, 0x006502, + 0x006D0A, 0x030042, 0x0B004A, 0x19004A, + 0x2B804A, 0x13804A, 0x21804A, 0x30000A, + 0x089144, 0x19A144, 0x2AB144, 0x0389E4, + 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, + 0x000702, 0x00107D, 0x000415, 0x018042, + 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, + 0x0019FD, 0x010042, 0x09804A, 0x10000A, + 0x000934, 0x001674, 0x0029F5, 0x010042, + 0x10000A, 0x00917C, 0x002075, 0x010042, + 0x08000A, 0x000904, 0x200A86, 0x0026F5, + 0x0027F5, 0x030042, 0x09004A, 0x10000A, + 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, + 0x010042, 0x51804A, 0x48000A, 0x160007, + 0x001075, 0x010042, 0x282C0A, 0x281D12, + 0x282512, 0x001F32, 0x1E0007, 0x0E0007, + 0x001975, 0x010042, 0x002DF5, 0x0D004A, + 0x10000A, 0x009144, 0x20EA86, 0x010042, + 0x28340A, 0x000E5D, 0x00008D, 0x000375, + 0x000820, 0x010040, 0x05D2F4, 0x54D104, + 0x00735C, 0x218B86, 0x000007, 0x0C0007, + 0x080007, 0x0A0007, 0x02178D, 0x000810, + 0x08043A, 0x34B206, 0x000007, 0x219206, + 0x000007, 0x080007, 0x002275, 0x010042, + 0x20000A, 0x002104, 0x225886, 0x001E2D, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x21CA86, 0x000007, 0x002010, 0x30043A, + 0x00057D, 0x0180C3, 0x08000A, 0x028924, + 0x280502, 0x280C02, 0x0A810D, 0x000820, + 0x0002F5, 0x010040, 0x220007, 0x0004FD, + 0x018042, 0x70000A, 0x030000, 0x007020, + 0x07FA06, 0x018040, 0x022B8D, 0x000810, + 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x22C286, + 0x000007, 0x020206, 0x000007, 0x000875, + 0x0009FD, 0x00010D, 0x234206, 0x000295, + 0x000B75, 0x00097D, 0x00000D, 0x000515, + 0x010042, 0x18000A, 0x001904, 0x2A0086, + 0x0006F5, 0x001020, 0x010040, 0x0004F5, + 0x000820, 0x010040, 0x000775, 0x010042, + 0x09804A, 0x10000A, 0x001124, 0x000904, + 0x23F286, 0x000815, 0x080102, 0x101204, + 0x241206, 0x000575, 0x081204, 0x000007, + 0x100102, 0x000575, 0x000425, 0x021124, + 0x100102, 0x000820, 0x031060, 0x010040, + 0x001924, 0x2A0086, 0x00008D, 0x000464, + 0x009D04, 0x291086, 0x180102, 0x000575, + 0x010042, 0x28040A, 0x00018D, 0x000924, + 0x280D02, 0x00000D, 0x000924, 0x281502, + 0x10000D, 0x000820, 0x0002F5, 0x010040, + 0x200007, 0x001175, 0x0002FD, 0x018042, + 0x08000A, 0x000904, 0x24FA86, 0x000007, + 0x000100, 0x080B20, 0x130B60, 0x1B0B60, + 0x030A60, 0x010040, 0x050042, 0x3D004A, + 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, + 0x010042, 0x28140A, 0x0004F5, 0x010042, + 0x08000A, 0x000315, 0x010D04, 0x260286, + 0x004015, 0x000095, 0x010D04, 0x25F086, + 0x100022, 0x10002A, 0x261A06, 0x000007, + 0x333104, 0x2AA904, 0x000007, 0x032124, + 0x280502, 0x284402, 0x001124, 0x400102, + 0x000424, 0x000424, 0x003224, 0x00292C, + 0x00636C, 0x277386, 0x000007, 0x02B164, + 0x000464, 0x000464, 0x00008D, 0x000A64, + 0x280D02, 0x10008D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x00008D, 0x38B904, + 0x000007, 0x03296C, 0x30010A, 0x0002F5, + 0x010042, 0x08000A, 0x000904, 0x270286, + 0x000007, 0x00212C, 0x28050A, 0x00316C, + 0x00046C, 0x00046C, 0x28450A, 0x001124, + 0x006B64, 0x100102, 0x00008D, 0x01096C, + 0x280D0A, 0x10010D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x004124, 0x000424, + 0x000424, 0x003224, 0x300102, 0x032944, + 0x27FA86, 0x000007, 0x300002, 0x0004F5, + 0x010042, 0x08000A, 0x000315, 0x010D04, + 0x284086, 0x003124, 0x000464, 0x300102, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x284A86, 0x000007, 0x284402, 0x003124, + 0x300502, 0x003924, 0x300583, 0x000883, + 0x0005F5, 0x010042, 0x28040A, 0x00008D, + 0x008124, 0x280D02, 0x00008D, 0x008124, + 0x281502, 0x10018D, 0x000820, 0x0002F5, + 0x010040, 0x220007, 0x001025, 0x000575, + 0x030042, 0x09004A, 0x10000A, 0x0A0904, + 0x121104, 0x000007, 0x001020, 0x050860, + 0x050040, 0x0006FD, 0x018042, 0x09004A, + 0x10000A, 0x0000A5, 0x0A0904, 0x121104, + 0x000007, 0x000820, 0x019060, 0x010040, + 0x0002F5, 0x010042, 0x08000A, 0x000904, + 0x29CA86, 0x000007, 0x244206, 0x000007, + 0x000606, 0x000007, 0x0002F5, 0x010042, + 0x08000A, 0x000904, 0x2A1A86, 0x000007, + 0x000100, 0x080B20, 0x138B60, 0x1B8B60, + 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, + 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, + 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, + 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, + 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, + 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, + 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, + 0x038A60, 0x000606, 0x018040, 0x00008D, + 0x000A64, 0x280D02, 0x000A24, 0x00027D, + 0x018042, 0x10000A, 0x001224, 0x0003FD, + 0x018042, 0x08000A, 0x000904, 0x2C0A86, + 0x000007, 0x00018D, 0x000A24, 0x000464, + 0x000464, 0x080102, 0x000924, 0x000424, + 0x000424, 0x100102, 0x02000D, 0x009144, + 0x2C6186, 0x000007, 0x0001FD, 0x018042, + 0x08000A, 0x000A44, 0x2C4386, 0x018042, + 0x0A000D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00027D, 0x001020, 0x000606, + 0x018040, 0x0002F5, 0x010042, 0x08000A, + 0x000904, 0x2CB286, 0x000007, 0x00037D, + 0x018042, 0x08000A, 0x000904, 0x2CE286, + 0x000007, 0x000075, 0x002E7D, 0x010042, + 0x0B804A, 0x000020, 0x000904, 0x000686, + 0x010040, 0x31844A, 0x30048B, 0x000883, + 0x00008D, 0x000810, 0x28143A, 0x00008D, + 0x000810, 0x280C3A, 0x000675, 0x010042, + 0x08000A, 0x003815, 0x010924, 0x280502, + 0x0B000D, 0x000820, 0x0002F5, 0x010040, + 0x000606, 0x220007, 0x000464, 0x000464, + 0x000606, 0x000007, 0x000134, 0x007F8D, + 0x00093C, 0x281D12, 0x282512, 0x001F32, + 0x0E0007, 0x00010D, 0x00037D, 0x000820, + 0x018040, 0x05D2F4, 0x000007, 0x080007, + 0x00037D, 0x018042, 0x08000A, 0x000904, + 0x2E8A86, 0x000007, 0x000606, 0x000007, + 0x000007, 0x000012, 0x100007, 0x320007, + 0x600007, 0x460007, 0x100080, 0x48001A, + 0x004904, 0x2EF186, 0x000007, 0x001210, + 0x58003A, 0x000145, 0x5C5D04, 0x000007, + 0x000080, 0x48001A, 0x004904, 0x2F4186, + 0x000007, 0x001210, 0x50003A, 0x005904, + 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, + 0x7FFF7D, 0x07D524, 0x004224, 0x500102, + 0x200502, 0x000082, 0x40001A, 0x004104, + 0x2FC986, 0x000007, 0x003865, 0x40001A, + 0x004020, 0x00104D, 0x04C184, 0x31AB86, + 0x000040, 0x040007, 0x000165, 0x000145, + 0x004020, 0x000040, 0x000765, 0x080080, + 0x40001A, 0x004104, 0x305986, 0x000007, + 0x001210, 0x40003A, 0x004104, 0x30B286, + 0x00004D, 0x0000CD, 0x004810, 0x20043A, + 0x000882, 0x40001A, 0x004104, 0x30C186, + 0x000007, 0x004820, 0x005904, 0x319886, + 0x000040, 0x0007E5, 0x200480, 0x2816A0, + 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, + 0x000040, 0x000032, 0x400075, 0x00007D, + 0x07D574, 0x200512, 0x000082, 0x40001A, + 0x004104, 0x317186, 0x000007, 0x038A06, + 0x640007, 0x0000E5, 0x000020, 0x000040, + 0x000A65, 0x000020, 0x020040, 0x020040, + 0x000040, 0x000165, 0x000042, 0x70000A, + 0x007104, 0x323286, 0x000007, 0x060007, + 0x019A06, 0x640007, 0x050000, 0x007020, + 0x000040, 0x038A06, 0x640007, 0x000007, + 0x00306D, 0x028860, 0x029060, 0x08000A, + 0x028860, 0x008040, 0x100012, 0x00100D, + 0x009184, 0x32D186, 0x000E0D, 0x009184, + 0x33E186, 0x000007, 0x300007, 0x001020, + 0x003B6D, 0x008040, 0x000080, 0x08001A, + 0x000904, 0x32F186, 0x000007, 0x001220, + 0x000DED, 0x008040, 0x008042, 0x10000A, + 0x40000D, 0x109544, 0x000007, 0x001020, + 0x000DED, 0x008040, 0x008042, 0x20040A, + 0x000082, 0x08001A, 0x000904, 0x338186, + 0x000007, 0x003B6D, 0x008042, 0x08000A, + 0x000E15, 0x010984, 0x342B86, 0x600007, + 0x08001A, 0x000C15, 0x010984, 0x341386, + 0x000020, 0x1A0007, 0x0002ED, 0x008040, + 0x620007, 0x00306D, 0x028042, 0x0A804A, + 0x000820, 0x0A804A, 0x000606, 0x10804A, + 0x000007, 0x282512, 0x001F32, 0x05D2F4, + 0x54D104, 0x00735C, 0x000786, 0x000007, + 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, + 0x020040, 0x004820, 0x025060, 0x40000A, + 0x024060, 0x000040, 0x454944, 0x000007, + 0x004020, 0x003AE5, 0x000040, 0x0028E5, + 0x000042, 0x48000A, 0x004904, 0x39F886, + 0x002C65, 0x000042, 0x40000A, 0x0000D5, + 0x454104, 0x000007, 0x000655, 0x054504, + 0x368286, 0x0001D5, 0x054504, 0x368086, + 0x002B65, 0x000042, 0x003AE5, 0x50004A, + 0x40000A, 0x45C3D4, 0x000007, 0x454504, + 0x000007, 0x0000CD, 0x444944, 0x000007, + 0x454504, 0x000007, 0x00014D, 0x554944, + 0x000007, 0x045144, 0x367986, 0x002C65, + 0x000042, 0x48000A, 0x4CD104, 0x000007, + 0x04C144, 0x368386, 0x000007, 0x160007, + 0x002CE5, 0x040042, 0x40000A, 0x004020, + 0x000040, 0x002965, 0x000042, 0x40000A, + 0x004104, 0x36F086, 0x000007, 0x002402, + 0x383206, 0x005C02, 0x0025E5, 0x000042, + 0x40000A, 0x004274, 0x002AE5, 0x000042, + 0x40000A, 0x004274, 0x500112, 0x0029E5, + 0x000042, 0x40000A, 0x004234, 0x454104, + 0x000007, 0x004020, 0x000040, 0x003EE5, + 0x000020, 0x000040, 0x002DE5, 0x400152, + 0x50000A, 0x045144, 0x37DA86, 0x0000C5, + 0x003EE5, 0x004020, 0x000040, 0x002BE5, + 0x000042, 0x40000A, 0x404254, 0x000007, + 0x002AE5, 0x004020, 0x000040, 0x500132, + 0x040134, 0x005674, 0x0029E5, 0x020042, + 0x42000A, 0x000042, 0x50000A, 0x05417C, + 0x0028E5, 0x000042, 0x48000A, 0x0000C5, + 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, + 0x020042, 0x40004A, 0x50000A, 0x00423C, + 0x00567C, 0x0028E5, 0x004820, 0x000040, + 0x281D12, 0x282512, 0x001F72, 0x002965, + 0x000042, 0x40000A, 0x004104, 0x393A86, + 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, + 0x000042, 0x40000A, 0x004104, 0x397886, + 0x002D65, 0x000042, 0x28340A, 0x003465, + 0x020042, 0x42004A, 0x004020, 0x4A004A, + 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, + 0x39E186, 0x000007, 0x000606, 0x080007, + 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, + 0x020045, 0x004020, 0x000060, 0x000365, + 0x000040, 0x002E65, 0x001A20, 0x0A1A60, + 0x000040, 0x003465, 0x020042, 0x42004A, + 0x004020, 0x4A004A, 0x000606, 0x50004A, + 0x0017FD, 0x018042, 0x08000A, 0x000904, + 0x225A86, 0x000007, 0x00107D, 0x018042, + 0x0011FD, 0x33804A, 0x19804A, 0x20000A, + 0x000095, 0x2A1144, 0x01A144, 0x3B9086, + 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, + 0x018042, 0x0010FD, 0x09804A, 0x38000A, + 0x000095, 0x010924, 0x003A64, 0x3B8186, + 0x000007, 0x003904, 0x3B9286, 0x000007, + 0x3B9A06, 0x00000D, 0x00008D, 0x000820, + 0x00387D, 0x018040, 0x700002, 0x00117D, + 0x018042, 0x00197D, 0x29804A, 0x30000A, + 0x380002, 0x003124, 0x000424, 0x000424, + 0x002A24, 0x280502, 0x00068D, 0x000810, + 0x28143A, 0x00750D, 0x00B124, 0x002264, + 0x3D0386, 0x284402, 0x000810, 0x280C3A, + 0x0B800D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00758D, 0x00B124, 0x100102, + 0x012144, 0x3E4986, 0x001810, 0x10003A, + 0x00387D, 0x018042, 0x08000A, 0x000904, + 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, + 0x00008D, 0x023164, 0x000A64, 0x280D02, + 0x0B808D, 0x000820, 0x0002FD, 0x018040, + 0x200007, 0x00387D, 0x018042, 0x08000A, + 0x000904, 0x3E3286, 0x030000, 0x0002FD, + 0x018042, 0x08000A, 0x000904, 0x3D8286, + 0x000007, 0x002810, 0x28043A, 0x00750D, + 0x030924, 0x002264, 0x280D02, 0x02316C, + 0x28450A, 0x0B810D, 0x000820, 0x0002FD, + 0x018040, 0x200007, 0x00008D, 0x000A24, + 0x3E4A06, 0x100102, 0x001810, 0x10003A, + 0x0000BD, 0x003810, 0x30043A, 0x00187D, + 0x018042, 0x0018FD, 0x09804A, 0x20000A, + 0x0000AD, 0x028924, 0x07212C, 0x001010, + 0x300583, 0x300D8B, 0x3014BB, 0x301C83, + 0x002083, 0x00137D, 0x038042, 0x33844A, + 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, + 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, + 0x001E0D, 0x0005FD, 0x018042, 0x20000A, + 0x020924, 0x00068D, 0x00A96C, 0x00009D, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x3F6A86, 0x000007, 0x280502, 0x280D0A, + 0x284402, 0x001810, 0x28143A, 0x0C008D, + 0x000820, 0x0002FD, 0x018040, 0x220007, + 0x003904, 0x225886, 0x001E0D, 0x00057D, + 0x018042, 0x20000A, 0x020924, 0x0000A5, + 0x0002FD, 0x018042, 0x08000A, 0x000904, + 0x402A86, 0x000007, 0x280502, 0x280C02, + 0x002010, 0x28143A, 0x0C010D, 0x000820, + 0x0002FD, 0x018040, 0x225A06, 0x220007, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000 +}; + +#endif //_HWMCODE_ diff --git a/trunk/sound/oss/yss225.c b/trunk/sound/oss/yss225.c new file mode 100644 index 000000000000..e700400576d8 --- /dev/null +++ b/trunk/sound/oss/yss225.c @@ -0,0 +1,319 @@ +#include + +unsigned char page_zero[] __initdata = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +unsigned char page_one[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +unsigned char page_two[] __initdata = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +unsigned char page_three[] __initdata = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +unsigned char page_four[] __initdata = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +unsigned char page_six[] __initdata = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +unsigned char page_seven[] __initdata = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +unsigned char page_zero_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_one_v2[] __initdata = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_two_v2[] __initdata = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_three_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_four_v2[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_seven_v2[] __initdata = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char mod_v2[] __initdata = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +unsigned char coefficients[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +unsigned char coefficients2[] __initdata = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +unsigned char coefficients3[] __initdata = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + diff --git a/trunk/sound/oss/yss225.h b/trunk/sound/oss/yss225.h new file mode 100644 index 000000000000..56d8b6b5e432 --- /dev/null +++ b/trunk/sound/oss/yss225.h @@ -0,0 +1,24 @@ +#ifndef __yss255_h__ +#define __yss255_h__ + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif /* __ys225_h__ */ + diff --git a/trunk/sound/sound_core.c b/trunk/sound/sound_core.c index 5322c50c9617..0b0a016ca6d6 100644 --- a/trunk/sound/sound_core.c +++ b/trunk/sound/sound_core.c @@ -365,6 +365,25 @@ int register_sound_dsp(const struct file_operations *fops, int dev) EXPORT_SYMBOL(register_sound_dsp); +/** + * register_sound_synth - register a synth device + * @fops: File operations for the driver + * @dev: Unit number to allocate + * + * Allocate a synth device. Unit is the number of the synth device requested. + * Pass -1 to request the next free synth unit. On success the allocated + * number is returned, on failure a negative error code is returned. + */ + + +int register_sound_synth(const struct file_operations *fops, int dev) +{ + return sound_insert_unit(&chains[9], fops, dev, 9, 137, + "synth", S_IRUSR | S_IWUSR, NULL); +} + +EXPORT_SYMBOL(register_sound_synth); + /** * unregister_sound_special - unregister a special sound device * @unit: unit number to allocate @@ -430,6 +449,21 @@ void unregister_sound_dsp(int unit) EXPORT_SYMBOL(unregister_sound_dsp); +/** + * unregister_sound_synth - unregister a synth device + * @unit: unit number to allocate + * + * Release a sound device that was allocated with register_sound_synth(). + * The unit passed is the return value from the register function. + */ + +void unregister_sound_synth(int unit) +{ + return sound_remove_unit(&chains[9], unit); +} + +EXPORT_SYMBOL(unregister_sound_synth); + /* * Now our file operations */