]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge master.kernel.org:/home/rmk/linux-2.6-arm
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 27 Jul 2008 23:46:08 +0000 (16:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 27 Jul 2008 23:46:08 +0000 (16:46 -0700)
* master.kernel.org:/home/rmk/linux-2.6-arm:
  [ARM] Fix shared mmap when more than two maps of the same file exist
  [ARM] fix VIPT/VIVT macro optimisations, add comments
  [ARM] 5179/1: Replace obsolete IRQT_* and __IRQT_* values with IRQ_TYPE_*
  [ARM] update defconfig for eseries.
  [ARM] PXA: squash warning in pxafb
  [ARM] pxa: PXA25x UDC - Fix warning during build
  [ARM] fix nwflash.c: 6ee8928d94841aa764aeaf645ad16daff811dc26
  [ARM] fix IOP32x, IOP33x, MXC and Samsung builds
  [ARM] pci: provide dummy pci_get_legacy_ide_irq()
  [ARM] fix fls() for 64-bit arguments
  [ARM] fix mode for board-yl-9200.c
  [ARM] 5176/1: arm/Makefile: fix: ARM946T -> ARM946E

620 files changed:
Documentation/isdn/README.mISDN [new file with mode: 0644]
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sparse.txt
Kbuild
Makefile
arch/alpha/kernel/osf_sys.c
arch/arm/mach-at91/at91cap9_devices.c
arch/arm/mach-at91/at91rm9200_devices.c
arch/arm/mach-at91/at91sam9260_devices.c
arch/arm/mach-at91/at91sam9261_devices.c
arch/arm/mach-at91/at91sam9263_devices.c
arch/arm/mach-at91/at91sam9rl_devices.c
arch/arm/mach-at91/board-cam60.c
arch/arm/mach-at91/board-cap9adk.c
arch/arm/mach-at91/board-dk.c
arch/arm/mach-at91/board-kb9202.c
arch/arm/mach-at91/board-sam9-l9260.c
arch/arm/mach-at91/board-sam9260ek.c
arch/arm/mach-at91/board-sam9261ek.c
arch/arm/mach-at91/board-sam9263ek.c
arch/arm/mach-at91/board-sam9rlek.c
arch/arm/mach-at91/board-yl-9200.c
arch/avr32/boards/atstk1000/Kconfig
arch/avr32/boards/atstk1000/Makefile
arch/avr32/boards/atstk1000/atstk1002.c
arch/avr32/boards/atstk1000/atstk1003.c
arch/avr32/boards/atstk1000/atstk1004.c
arch/avr32/kernel/time.c
arch/avr32/mach-at32ap/at32ap700x.c
arch/avr32/mach-at32ap/hsmc.c
arch/ia64/kvm/kvm-ia64.c
arch/parisc/hpux/sys_hpux.c
arch/powerpc/kvm/44x_tlb.c
arch/powerpc/kvm/emulate.c
arch/s390/kvm/gaccess.h
arch/s390/kvm/intercept.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/sigp.c
arch/x86/kvm/mmu.c
arch/x86/kvm/svm.c
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
drivers/char/ser_a2232.c
drivers/char/vme_scc.c
drivers/crypto/talitos.c
drivers/firewire/Kconfig
drivers/firewire/fw-card.c
drivers/firewire/fw-cdev.c
drivers/firewire/fw-ohci.c
drivers/firewire/fw-topology.c
drivers/firewire/fw-transaction.c
drivers/firmware/memmap.c
drivers/infiniband/core/ucm.c
drivers/infiniband/core/ucma.c
drivers/infiniband/hw/mlx4/cq.c
drivers/infiniband/hw/mlx4/main.c
drivers/infiniband/hw/mlx4/mlx4_ib.h
drivers/infiniband/hw/mlx4/mr.c
drivers/infiniband/hw/mlx4/qp.c
drivers/infiniband/hw/mlx4/srq.c
drivers/infiniband/hw/mlx4/user.h
drivers/infiniband/hw/nes/nes.c
drivers/infiniband/hw/nes/nes_cm.c
drivers/infiniband/hw/nes/nes_cm.h
drivers/infiniband/hw/nes/nes_hw.c
drivers/infiniband/hw/nes/nes_verbs.c
drivers/infiniband/ulp/ipoib/Kconfig
drivers/isdn/Kconfig
drivers/isdn/Makefile
drivers/isdn/hardware/Makefile
drivers/isdn/hardware/mISDN/Kconfig [new file with mode: 0644]
drivers/isdn/hardware/mISDN/Makefile [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfc_multi.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfc_pci.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfcmulti.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfcpci.c [new file with mode: 0644]
drivers/isdn/mISDN/Kconfig [new file with mode: 0644]
drivers/isdn/mISDN/Makefile [new file with mode: 0644]
drivers/isdn/mISDN/core.c [new file with mode: 0644]
drivers/isdn/mISDN/core.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_audio.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_biquad.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_blowfish.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_cmx.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_core.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_dtmf.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_ecdis.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_hwec.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_hwec.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_pipeline.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_tones.c [new file with mode: 0644]
drivers/isdn/mISDN/fsm.c [new file with mode: 0644]
drivers/isdn/mISDN/fsm.h [new file with mode: 0644]
drivers/isdn/mISDN/hwchannel.c [new file with mode: 0644]
drivers/isdn/mISDN/l1oip.h [new file with mode: 0644]
drivers/isdn/mISDN/l1oip_codec.c [new file with mode: 0644]
drivers/isdn/mISDN/l1oip_core.c [new file with mode: 0644]
drivers/isdn/mISDN/layer1.c [new file with mode: 0644]
drivers/isdn/mISDN/layer1.h [new file with mode: 0644]
drivers/isdn/mISDN/layer2.c [new file with mode: 0644]
drivers/isdn/mISDN/layer2.h [new file with mode: 0644]
drivers/isdn/mISDN/socket.c [new file with mode: 0644]
drivers/isdn/mISDN/stack.c [new file with mode: 0644]
drivers/isdn/mISDN/tei.c [new file with mode: 0644]
drivers/isdn/mISDN/timerdev.c [new file with mode: 0644]
drivers/md/dm-mpath.c
drivers/message/fusion/mptbase.c
drivers/message/fusion/mptctl.c
drivers/message/fusion/mptfc.c
drivers/message/fusion/mptlan.c
drivers/message/fusion/mptsas.c
drivers/message/fusion/mptscsih.c
drivers/misc/Kconfig
drivers/misc/atmel-ssc.c
drivers/mmc/core/Makefile
drivers/mmc/core/bus.c
drivers/mmc/core/core.h
drivers/mmc/core/debugfs.c [new file with mode: 0644]
drivers/mmc/core/host.c
drivers/mmc/host/atmel-mci-regs.h
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/imxmmc.c
drivers/mmc/host/mmc_spi.c
drivers/mtd/Kconfig
drivers/mtd/afs.c
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/chips/cfi_probe.c
drivers/mtd/chips/cfi_util.c
drivers/mtd/chips/chipreg.c
drivers/mtd/chips/gen_probe.c
drivers/mtd/chips/jedec_probe.c
drivers/mtd/chips/map_absent.c
drivers/mtd/chips/map_ram.c
drivers/mtd/chips/map_rom.c
drivers/mtd/cmdlinepart.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/block2mtd.c
drivers/mtd/devices/doc2000.c
drivers/mtd/devices/doc2001.c
drivers/mtd/devices/doc2001plus.c
drivers/mtd/devices/docecc.c
drivers/mtd/devices/docprobe.c
drivers/mtd/devices/lart.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/ms02-nv.c
drivers/mtd/devices/ms02-nv.h
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/mtdram.c
drivers/mtd/devices/phram.c
drivers/mtd/devices/pmc551.c
drivers/mtd/devices/slram.c
drivers/mtd/ftl.c
drivers/mtd/inftlcore.c
drivers/mtd/inftlmount.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/amd76xrom.c
drivers/mtd/maps/autcpu12-nvram.c
drivers/mtd/maps/bast-flash.c [deleted file]
drivers/mtd/maps/bfin-async-flash.c [new file with mode: 0644]
drivers/mtd/maps/cdb89712.c
drivers/mtd/maps/ceiva.c
drivers/mtd/maps/cfi_flagadm.c
drivers/mtd/maps/dbox2-flash.c
drivers/mtd/maps/dc21285.c
drivers/mtd/maps/dilnetpc.c
drivers/mtd/maps/dmv182.c
drivers/mtd/maps/ebony.c
drivers/mtd/maps/edb7312.c
drivers/mtd/maps/fortunet.c
drivers/mtd/maps/h720x-flash.c
drivers/mtd/maps/ichxrom.c
drivers/mtd/maps/impa7.c
drivers/mtd/maps/integrator-flash.c
drivers/mtd/maps/ipaq-flash.c
drivers/mtd/maps/ixp2000.c
drivers/mtd/maps/ixp4xx.c
drivers/mtd/maps/l440gx.c
drivers/mtd/maps/map_funcs.c
drivers/mtd/maps/mbx860.c
drivers/mtd/maps/netsc520.c
drivers/mtd/maps/nettel.c
drivers/mtd/maps/octagon-5066.c
drivers/mtd/maps/omap-toto-flash.c
drivers/mtd/maps/pci.c
drivers/mtd/maps/pcmciamtd.c
drivers/mtd/maps/physmap.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/redwood.c
drivers/mtd/maps/rpxlite.c
drivers/mtd/maps/sa1100-flash.c
drivers/mtd/maps/sbc8240.c
drivers/mtd/maps/sbc_gxx.c
drivers/mtd/maps/sc520cdp.c
drivers/mtd/maps/scb2_flash.c
drivers/mtd/maps/scx200_docflash.c
drivers/mtd/maps/sharpsl-flash.c
drivers/mtd/maps/solutionengine.c
drivers/mtd/maps/sun_uflash.c
drivers/mtd/maps/tqm8xxl.c
drivers/mtd/maps/ts5500_flash.c
drivers/mtd/maps/tsunami_flash.c
drivers/mtd/maps/uclinux.c
drivers/mtd/maps/vmax301.c
drivers/mtd/maps/walnut.c
drivers/mtd/maps/wr_sbc82xx_flash.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdblock.c
drivers/mtd/mtdblock_ro.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/atmel_nand.c [moved from drivers/mtd/nand/at91_nand.c with 62% similarity]
drivers/mtd/nand/atmel_nand_ecc.h [new file with mode: 0644]
drivers/mtd/nand/au1550nd.c
drivers/mtd/nand/autcpu12.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/edb7312.c
drivers/mtd/nand/excite_nandflash.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/h1910.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_bbt.c
drivers/mtd/nand/nand_ecc.c
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/ppchameleonevb.c
drivers/mtd/nand/rtc_from4.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/nand/sharpsl.c
drivers/mtd/nand/spia.c
drivers/mtd/nand/toto.c
drivers/mtd/nand/ts7250.c
drivers/mtd/nftlcore.c
drivers/mtd/nftlmount.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/redboot.c
drivers/mtd/rfd_ftl.c
drivers/net/bnx2x_main.c
drivers/net/mlx4/alloc.c
drivers/net/mlx4/catas.c
drivers/net/mlx4/cmd.c
drivers/net/mlx4/cq.c
drivers/net/mlx4/eq.c
drivers/net/mlx4/fw.c
drivers/net/mlx4/fw.h
drivers/net/mlx4/icm.c
drivers/net/mlx4/icm.h
drivers/net/mlx4/intf.c
drivers/net/mlx4/main.c
drivers/net/mlx4/mcg.c
drivers/net/mlx4/mlx4.h
drivers/net/mlx4/mr.c
drivers/net/mlx4/qp.c
drivers/net/mlx4/reset.c
drivers/net/mlx4/srq.c
drivers/net/ppp_generic.c
drivers/s390/kvm/Makefile
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/3w-9xxx.c
drivers/scsi/3w-9xxx.h
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/advansys.c
drivers/scsi/aha152x.c
drivers/scsi/aic94xx/aic94xx.h
drivers/scsi/aic94xx/aic94xx_hwi.c
drivers/scsi/aic94xx/aic94xx_scb.c
drivers/scsi/aic94xx/aic94xx_task.c
drivers/scsi/aic94xx/aic94xx_tmf.c
drivers/scsi/arm/fas216.c
drivers/scsi/ch.c
drivers/scsi/device_handler/Kconfig
drivers/scsi/device_handler/Makefile
drivers/scsi/device_handler/scsi_dh.c
drivers/scsi/device_handler/scsi_dh_alua.c [new file with mode: 0644]
drivers/scsi/device_handler/scsi_dh_emc.c
drivers/scsi/device_handler/scsi_dh_hp_sw.c
drivers/scsi/device_handler/scsi_dh_rdac.c
drivers/scsi/ibmvscsi/ibmvfc.c
drivers/scsi/ibmvscsi/ibmvfc.h
drivers/scsi/ibmvscsi/ibmvstgt.c
drivers/scsi/imm.c
drivers/scsi/ipr.h
drivers/scsi/libsas/sas_ata.c
drivers/scsi/libsas/sas_expander.c
drivers/scsi/libsas/sas_port.c
drivers/scsi/libsas/sas_scsi_host.c
drivers/scsi/libsrp.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/megaraid/mega_common.h
drivers/scsi/megaraid/megaraid_mbox.c
drivers/scsi/megaraid/megaraid_mm.c
drivers/scsi/nsp32.c
drivers/scsi/nsp32_debug.c
drivers/scsi/pcmcia/nsp_cs.c
drivers/scsi/pcmcia/nsp_debug.c
drivers/scsi/ppa.c
drivers/scsi/qla1280.c
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_gs.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_mbx.c
drivers/scsi/qla2xxx/qla_mid.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_sup.c
drivers/scsi/qla2xxx/qla_version.h
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/scsi.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_devinfo.c
drivers/scsi/scsi_error.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_netlink.c
drivers/scsi/scsi_priv.h
drivers/scsi/scsi_proc.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_sysfs.c
drivers/scsi/scsi_tgt_priv.h
drivers/scsi/scsi_transport_fc.c
drivers/scsi/scsi_transport_sas.c
drivers/scsi/sd.c
drivers/scsi/sd.h
drivers/scsi/sd_dif.c [new file with mode: 0644]
drivers/scsi/st.c
drivers/scsi/stex.c
drivers/scsi/sym53c8xx_2/sym_hipd.c
drivers/scsi/tmscsim.c
drivers/scsi/wd7000.c
drivers/scsi/zalon.c
drivers/usb/serial/ipaq.c
fs/affs/file.c
fs/afs/internal.h
fs/afs/security.c
fs/aio.c
fs/attr.c
fs/bad_inode.c
fs/cifs/cifsfs.c
fs/coda/dir.c
fs/coda/pioctl.c
fs/compat.c
fs/ecryptfs/inode.c
fs/exec.c
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext3/acl.c
fs/ext3/acl.h
fs/ext4/acl.c
fs/ext4/acl.h
fs/fat/file.c
fs/fcntl.c
fs/fifo.c
fs/file.c
fs/file_table.c
fs/fuse/dir.c
fs/fuse/file.c
fs/gfs2/inode.c
fs/gfs2/inode.h
fs/gfs2/ops_export.c
fs/gfs2/ops_inode.c
fs/gfs2/super.c
fs/hfs/inode.c
fs/hfsplus/inode.c
fs/hostfs/hostfs_kern.c
fs/hpfs/namei.c
fs/hppfs/hppfs.c
fs/inotify_user.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/dir.c
fs/jffs2/file.c
fs/jffs2/ioctl.c
fs/jffs2/os-linux.h
fs/jfs/acl.c
fs/jfs/jfs_acl.h
fs/namei.c
fs/namespace.c
fs/ncpfs/dir.c
fs/nfs/dir.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsfh.c
fs/nfsd/vfs.c
fs/ntfs/file.c
fs/ocfs2/file.c
fs/ocfs2/file.h
fs/open.c
fs/pipe.c
fs/proc/base.c
fs/proc/inode.c
fs/proc/proc_sysctl.c
fs/reiserfs/xattr.c
fs/smbfs/file.c
fs/splice.c
fs/stat.c
fs/ubifs/file.c
fs/utimes.c
fs/xattr.c
fs/xfs/linux-2.6/xfs_ioctl.c
fs/xfs/linux-2.6/xfs_iops.c
fs/xfs/linux-2.6/xfs_lrw.c
include/Kbuild
include/asm-alpha/namei.h [deleted file]
include/asm-arm/arch-at91/at91_ecc.h [deleted file]
include/asm-arm/arch-at91/board.h
include/asm-arm/namei.h [deleted file]
include/asm-avr32/arch-at32ap/board.h
include/asm-avr32/namei.h [deleted file]
include/asm-blackfin/namei.h [deleted file]
include/asm-cris/namei.h [deleted file]
include/asm-frv/namei.h [deleted file]
include/asm-generic/gpio.h
include/asm-generic/vmlinux.lds.h
include/asm-h8300/namei.h [deleted file]
include/asm-ia64/namei.h [deleted file]
include/asm-m32r/namei.h [deleted file]
include/asm-m68k/namei.h [deleted file]
include/asm-m68knommu/namei.h [deleted file]
include/asm-mips/namei.h [deleted file]
include/asm-mn10300/namei.h [deleted file]
include/asm-parisc/namei.h [deleted file]
include/asm-powerpc/kvm_ppc.h
include/asm-powerpc/namei.h [deleted file]
include/asm-s390/kvm_host.h
include/asm-s390/namei.h [deleted file]
include/asm-sh/namei.h [deleted file]
include/asm-sparc/namei.h [deleted file]
include/asm-sparc/namei_32.h [deleted file]
include/asm-sparc/namei_64.h [deleted file]
include/asm-sparc64/namei.h [deleted file]
include/asm-um/namei.h [deleted file]
include/asm-v850/namei.h [deleted file]
include/asm-x86/kvm_host.h
include/asm-x86/namei.h [deleted file]
include/asm-xtensa/namei.h [deleted file]
include/linux/coda_linux.h
include/linux/fs.h
include/linux/fs_struct.h
include/linux/hugetlb.h
include/linux/jffs2.h
include/linux/mISDNdsp.h [new file with mode: 0644]
include/linux/mISDNhw.h [new file with mode: 0644]
include/linux/mISDNif.h [new file with mode: 0644]
include/linux/mlx4/qp.h
include/linux/mmc/card.h
include/linux/mmc/host.h
include/linux/mount.h
include/linux/mtd/blktrans.h
include/linux/mtd/cfi.h
include/linux/mtd/cfi_endian.h
include/linux/mtd/concat.h
include/linux/mtd/doc2000.h
include/linux/mtd/flashchip.h
include/linux/mtd/ftl.h
include/linux/mtd/gen_probe.h
include/linux/mtd/inftl.h
include/linux/mtd/map.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/nand_ecc.h
include/linux/mtd/nftl.h
include/linux/mtd/partitions.h
include/linux/mtd/physmap.h
include/linux/mtd/plat-ram.h
include/linux/mtd/pmc551.h
include/linux/mtd/xip.h
include/linux/namei.h
include/linux/nfs_fs.h
include/linux/pci_ids.h
include/linux/proc_fs.h
include/linux/reiserfs_xattr.h
include/linux/rtnetlink.h
include/linux/sched.h
include/linux/security.h
include/linux/shmem_fs.h
include/linux/slab.h
include/linux/socket.h
include/linux/sysctl.h
include/linux/task_io_accounting.h
include/linux/task_io_accounting_ops.h
include/linux/tracehook.h
include/mtd/inftl-user.h
include/mtd/jffs2-user.h
include/mtd/mtd-abi.h
include/mtd/mtd-user.h
include/mtd/nftl-user.h
include/net/af_unix.h
include/net/ip.h
include/net/ipv6.h
include/net/net_namespace.h
include/net/request_sock.h
include/net/route.h
include/scsi/scsi.h
include/scsi/scsi_cmnd.h
include/scsi/scsi_device.h
include/scsi/scsi_dh.h
include/scsi/scsi_eh.h
include/scsi/scsi_host.h
init/Kconfig
ipc/mqueue.c
kernel/cgroup.c
kernel/exec_domain.c
kernel/exit.c
kernel/fork.c
kernel/sysctl.c
kernel/trace/trace.c
kernel/tsacct.c
mm/filemap.c
mm/filemap_xip.c
mm/shmem_acl.c
mm/util.c
net/appletalk/ddp.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/core/datagram.c
net/core/dev.c
net/core/request_sock.c
net/core/skbuff.c
net/core/stream.c
net/core/user_dma.c
net/dccp/dccp.h
net/dccp/input.c
net/dccp/ipv4.c
net/dccp/ipv6.c
net/dccp/proto.c
net/dccp/timer.c
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_fragment.c
net/ipv4/inet_hashtables.c
net/ipv4/inet_timewait_sock.c
net/ipv4/ip_fragment.c
net/ipv4/ip_output.c
net/ipv4/netfilter/arptable_filter.c
net/ipv4/netfilter/iptable_security.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_timer.c
net/ipv6/addrconf.c
net/ipv6/af_inet6.c
net/ipv6/inet6_connection_sock.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_output.c
net/ipv6/mip6.c
net/ipv6/netfilter/ip6table_security.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c
net/ipv6/syncookies.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c
net/key/af_key.c
net/netfilter/nf_conntrack_extend.c
net/netlink/af_netlink.c
net/packet/af_packet.c
net/rxrpc/af_rxrpc.c
net/sched/act_api.c
net/sched/act_police.c
net/sched/cls_u32.c
net/sched/sch_atm.c
net/sched/sch_cbq.c
net/sched/sch_generic.c
net/sched/sch_htb.c
net/sched/sch_sfq.c
net/sctp/associola.c
net/sysctl_net.c
net/unix/af_unix.c
net/unix/garbage.c
net/xfrm/xfrm_algo.c
net/xfrm/xfrm_ipcomp.c
net/xfrm/xfrm_state.c
scripts/Makefile.headersinst
scripts/diffconfig [new file with mode: 0755]
scripts/hdrcheck.sh [deleted file]
scripts/headers.sh [new file with mode: 0755]
scripts/headers_check.pl [new file with mode: 0644]
scripts/headers_install.pl [new file with mode: 0644]
scripts/kconfig/conf.c
scripts/kconfig/confdata.c
scripts/kconfig/lkc.h
scripts/kernel-doc
scripts/setlocalversion
security/capability.c
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c
sound/isa/cs423x/cs4236.c
sound/isa/opti9xx/opti92x-ad1848.c
sound/pci/ac97/ac97_codec.c
sound/pci/ac97/ac97_patch.c
sound/pci/azt3328.h
sound/pci/ens1370.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/soc/au1x/psc-i2s.c
sound/soc/codecs/wm9712.c
sound/soc/soc-dapm.c

diff --git a/Documentation/isdn/README.mISDN b/Documentation/isdn/README.mISDN
new file mode 100644 (file)
index 0000000..cd8bf92
--- /dev/null
@@ -0,0 +1,6 @@
+mISDN is a new modular ISDN driver, in the long term it should replace
+the old I4L driver architecture for passiv ISDN cards.
+It was designed to allow a broad range of applications and interfaces
+but only have the basic function in kernel, the interface to the user
+space is based on sockets with a own address family AF_ISDN.
+
index 72aff61e731509d7c87f3a3913aa3b4e965ab9c5..6f6d117ac7e22cd2d9fc981f0fc8542d824d1db8 100644 (file)
@@ -1024,6 +1024,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          intel-mac-v3  Intel Mac Type 3
          intel-mac-v4  Intel Mac Type 4
          intel-mac-v5  Intel Mac Type 5
+         intel-mac-auto Intel Mac (detect type according to subsystem id)
          macmini       Intel Mac Mini (equivalent with type 3)
          macbook       Intel Mac Book (eq. type 5)
          macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
index 1a3bdc27d95e98ce49c07791b02515e38ef678a5..42f43fa59f24c75c1f553dc5078f88b949302fd9 100644 (file)
@@ -73,10 +73,10 @@ recompiled, or use "make C=2" to run sparse on the files whether they need to
 be recompiled or not.  The latter is a fast way to check the whole tree if you
 have already built it.
 
-The optional make variable CHECKFLAGS can be used to pass arguments to sparse.
-The build system passes -Wbitwise to sparse automatically.  To perform
-endianness checks, you may define __CHECK_ENDIAN__:
+The optional make variable CF can be used to pass arguments to sparse.  The
+build system passes -Wbitwise to sparse automatically.  To perform endianness
+checks, you may define __CHECK_ENDIAN__:
 
-        make C=2 CHECKFLAGS="-D__CHECK_ENDIAN__"
+        make C=2 CF="-D__CHECK_ENDIAN__"
 
 These checks are disabled by default as they generate a host of warnings.
diff --git a/Kbuild b/Kbuild
index e750e9c3fe59862e377a568e0758ca838e11c6f1..f056b4feee519748ae6ef22fde3df1da532e177b 100644 (file)
--- a/Kbuild
+++ b/Kbuild
@@ -43,7 +43,7 @@ $(obj)/$(bounds-file): kernel/bounds.s Kbuild
 # 2) Generate asm-offsets.h
 #
 
-offsets-file := include/asm-$(SRCARCH)/asm-offsets.h
+offsets-file := include/asm/asm-offsets.h
 
 always  += $(offsets-file)
 targets += $(offsets-file)
@@ -81,7 +81,6 @@ arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c \
        $(call if_changed_dep,cc_s_c)
 
 $(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s Kbuild
-       $(Q)mkdir -p $(dir $@)
        $(call cmd,offsets)
 
 #####
index 3cad7db5eba7a7570b392f5d5b4c99b794ee71e5..40f24810116ccfcf9fe775b03c0fd58eef5fe905 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -205,6 +205,9 @@ ifeq ($(ARCH),x86_64)
         SRCARCH := x86
 endif
 
+# Where to locate arch specific headers
+hdr-arch       := $(SRCARCH)
+
 KCONFIG_CONFIG ?= .config
 
 # SHELL used by kbuild
@@ -326,7 +329,8 @@ AFLAGS_KERNEL       =
 # Needed to be compatible with the O= option
 LINUXINCLUDE    := -Iinclude \
                    $(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) \
-                  -include include/linux/autoconf.h
+                   -I$(srctree)/arch/$(hdr-arch)/include               \
+                   -include include/linux/autoconf.h
 
 KBUILD_CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE)
 
@@ -922,7 +926,9 @@ ifneq ($(KBUILD_SRC),)
                /bin/false; \
        fi;
        $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi;
-       $(Q)ln -fsn $(srctree)/include/asm-$(SRCARCH) include2/asm
+       $(Q)if [ -e $(srctree)/include/asm-$(SRCARCH)/system.h ]; then  \
+           ln -fsn $(srctree)/include/asm-$(SRCARCH) include2/asm;     \
+           fi
 endif
 
 # prepare2 creates a makefile if using a separate output directory
@@ -948,22 +954,34 @@ export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
 
 # The asm symlink changes when $(ARCH) changes.
 # Detect this and ask user to run make mrproper
-
-include/asm: FORCE
-       $(Q)set -e; asmlink=`readlink include/asm | cut -d '-' -f 2`;   \
-       if [ -L include/asm ]; then                                     \
-               if [ "$$asmlink" != "$(SRCARCH)" ]; then                \
+define check-symlink
+       set -e;                                                            \
+       if [ -L include/asm ]; then                                        \
+               asmlink=`readlink include/asm | cut -d '-' -f 2`;          \
+               if [ "$$asmlink" != "$(SRCARCH)" ]; then                   \
                        echo "ERROR: the symlink $@ points to asm-$$asmlink but asm-$(SRCARCH) was expected"; \
                        echo "       set ARCH or save .config and run 'make mrproper' to fix it";             \
-                       exit 1;                                         \
-               fi;                                                     \
-       else                                                            \
-               echo '  SYMLINK $@ -> include/asm-$(SRCARCH)';          \
-               if [ ! -d include ]; then                               \
-                       mkdir -p include;                               \
-               fi;                                                     \
-               ln -fsn asm-$(SRCARCH) $@;                              \
+                       exit 1;                                            \
+               fi;                                                        \
        fi
+endef
+
+# We create the target directory of the symlink if it does
+# not exist so the test in chack-symlink works and we have a
+# directory for generated filesas used by some architectures.
+define create-symlink
+       if [ ! -L include/asm ]; then                                      \
+                       echo '  SYMLINK $@ -> include/asm-$(SRCARCH)';     \
+                       if [ ! -d include/asm-$(SRCARCH) ]; then           \
+                               mkdir -p include/asm-$(SRCARCH);           \
+                       fi;                                                \
+                       ln -fsn asm-$(SRCARCH) $@;                         \
+       fi
+endef
+
+include/asm: FORCE
+       $(Q)$(check-symlink)
+       $(Q)$(create-symlink)
 
 # Generate some files
 # ---------------------------------------------------------------------------
@@ -1010,36 +1028,43 @@ firmware_install: FORCE
 
 # ---------------------------------------------------------------------------
 # Kernel headers
-INSTALL_HDR_PATH=$(objtree)/usr
-export INSTALL_HDR_PATH
 
-HDRFILTER=generic i386 x86_64
-HDRARCHES=$(filter-out $(HDRFILTER),$(patsubst $(srctree)/include/asm-%/Kbuild,%,$(wildcard $(srctree)/include/asm-*/Kbuild)))
+#Default location for installed headers
+export INSTALL_HDR_PATH = $(objtree)/usr
 
-PHONY += headers_install_all
-headers_install_all: include/linux/version.h scripts_basic FORCE
+hdr-inst := -rR -f $(srctree)/scripts/Makefile.headersinst obj
+# Find out where the Kbuild file is located to support
+# arch/$(ARCH)/include/asm
+hdr-dir = $(strip                                                         \
+          $(if $(wildcard $(srctree)/arch/$(hdr-arch)/include/asm/Kbuild), \
+               arch/$(hdr-arch)/include/asm, include/asm-$(hdr-arch)))
+
+# If we do an all arch process set dst to asm-$(hdr-arch)
+hdr-dst = $(if $(KBUILD_HEADERS), dst=include/asm-$(hdr-arch), dst=include/asm)
+
+PHONY += __headers
+__headers: include/linux/version.h scripts_basic FORCE
        $(Q)$(MAKE) $(build)=scripts scripts/unifdef
-       $(Q)for arch in $(HDRARCHES); do \
-        $(MAKE) ARCH=$$arch -f $(srctree)/scripts/Makefile.headersinst obj=include BIASMDIR=-bi-$$arch ;\
-        done
+
+PHONY += headers_install_all
+headers_install_all:
+       $(Q)$(CONFIG_SHELL) $(srctree)/scripts/headers.sh install
 
 PHONY += headers_install
-headers_install: include/linux/version.h scripts_basic FORCE
-       @if [ ! -r $(srctree)/include/asm-$(SRCARCH)/Kbuild ]; then \
-         echo '*** Error: Headers not exportable for this architecture ($(SRCARCH))'; \
-         exit 1 ; fi
-       $(Q)$(MAKE) $(build)=scripts scripts/unifdef
-       $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.headersinst ARCH=$(SRCARCH) obj=include
+headers_install: __headers
+       $(if $(wildcard $(srctree)/$(hdr-dir)/Kbuild),, \
+       $(error Headers not exportable for the $(SRCARCH) architecture))
+       $(Q)$(MAKE) $(hdr-inst)=include
+       $(Q)$(MAKE) $(hdr-inst)=$(hdr-dir) $(hdr-dst)
 
 PHONY += headers_check_all
 headers_check_all: headers_install_all
-       $(Q)for arch in $(HDRARCHES); do \
-        $(MAKE) ARCH=$$arch -f $(srctree)/scripts/Makefile.headersinst obj=include BIASMDIR=-bi-$$arch HDRCHECK=1 ;\
-        done
+       $(Q)$(CONFIG_SHELL) $(srctree)/scripts/headers.sh check
 
 PHONY += headers_check
 headers_check: headers_install
-       $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.headersinst ARCH=$(SRCARCH) obj=include HDRCHECK=1
+       $(Q)$(MAKE) $(hdr-inst)=include HDRCHECK=1
+       $(Q)$(MAKE) $(hdr-inst)=$(hdr-dir) $(hdr-dst) HDRCHECK=1
 
 # ---------------------------------------------------------------------------
 # Modules
@@ -1131,7 +1156,7 @@ MRPROPER_FILES += .config .config.old include/asm .version .old_version \
                   include/linux/autoconf.h include/linux/version.h      \
                   include/linux/utsrelease.h                            \
                   include/linux/bounds.h include/asm*/asm-offsets.h     \
-                 Module.symvers tags TAGS cscope*
+                 Module.symvers Module.markers tags TAGS cscope*
 
 # clean - Delete most, but leave enough to build external modules
 #
@@ -1150,7 +1175,7 @@ clean: archclean $(clean-dirs)
                \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
                -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
                -o -name '*.symtypes' -o -name 'modules.order' \
-               -o -name 'Module.markers' \) \
+               -o -name 'Module.markers' -o -name '.tmp_*.o.*' \) \
                -type f -print | xargs rm -f
 
 # mrproper - Delete all generated files, including .config
@@ -1224,21 +1249,17 @@ help:
        @echo  '  cscope          - Generate cscope index'
        @echo  '  kernelrelease   - Output the release version string'
        @echo  '  kernelversion   - Output the version stored in Makefile'
-       @if [ -r $(srctree)/include/asm-$(SRCARCH)/Kbuild ]; then \
-        echo  '  headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH'; \
+       @echo  '  headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH'; \
         echo  '                    (default: $(INSTALL_HDR_PATH))'; \
-        fi
-       @echo  ''
+        echo  ''
        @echo  'Static analysers'
        @echo  '  checkstack      - Generate a list of stack hogs'
        @echo  '  namespacecheck  - Name space analysis on compiled kernel'
        @echo  '  versioncheck    - Sanity check on version.h usage'
        @echo  '  includecheck    - Check for duplicate included header files'
        @echo  '  export_report   - List the usages of all exported symbols'
-       @if [ -r $(srctree)/include/asm-$(SRCARCH)/Kbuild ]; then \
-        echo  '  headers_check   - Sanity check on exported headers'; \
-        fi
-       @echo  ''
+       @echo  '  headers_check   - Sanity check on exported headers'; \
+        echo  ''
        @echo  'Kernel packaging:'
        @$(MAKE) $(build)=$(package-dir) help
        @echo  ''
@@ -1411,7 +1432,11 @@ define find-sources
               \( -name config -o -name 'asm-*' \) -prune \
               -o -name $1 -print; \
          for arch in $(ALLINCLUDE_ARCHS) ; do \
-              find $(__srctree)include/asm-$${arch} $(RCS_FIND_IGNORE) \
+              test -e $(__srctree)include/asm-$${arch} && \
+                 find $(__srctree)include/asm-$${arch} $(RCS_FIND_IGNORE) \
+                   -name $1 -print; \
+              test -e $(__srctree)arch/$${arch}/include/asm && \
+                find $(__srctree)arch/$${arch}/include/asm $(RCS_FIND_IGNORE) \
                    -name $1 -print; \
          done ; \
          find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \
index 32ca1b9273078d7025466075e677a59e34938b33..6e943135f0e041346e63348a18fb33239113b9c1 100644 (file)
@@ -253,15 +253,15 @@ do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer,
 }
 
 asmlinkage int
-osf_statfs(char __user *path, struct osf_statfs __user *buffer, unsigned long bufsiz)
+osf_statfs(char __user *pathname, struct osf_statfs __user *buffer, unsigned long bufsiz)
 {
-       struct nameidata nd;
+       struct path path;
        int retval;
 
-       retval = user_path_walk(path, &nd);
+       retval = user_path(pathname, &path);
        if (!retval) {
-               retval = do_osf_statfs(nd.path.dentry, buffer, bufsiz);
-               path_put(&nd.path);
+               retval = do_osf_statfs(path.dentry, buffer, bufsiz);
+               path_put(&path);
        }
        return retval;
 }
index 747b9dedab88a8e51d75be21b5c9799f34e38b53..dc8b40783d94243565a1e7963941f35fdf267c13 100644 (file)
@@ -377,7 +377,7 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
  * -------------------------------------------------------------------- */
 
 #if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE)
-static struct at91_nand_data nand_data;
+static struct atmel_nand_data nand_data;
 
 #define NAND_BASE      AT91_CHIPSELECT_3
 
@@ -395,7 +395,7 @@ static struct resource nand_resources[] = {
 };
 
 static struct platform_device at91cap9_nand_device = {
-       .name           = "at91_nand",
+       .name           = "atmel_nand",
        .id             = -1,
        .dev            = {
                                .platform_data  = &nand_data,
@@ -404,7 +404,7 @@ static struct platform_device at91cap9_nand_device = {
        .num_resources  = ARRAY_SIZE(nand_resources),
 };
 
-void __init at91_add_device_nand(struct at91_nand_data *data)
+void __init at91_add_device_nand(struct atmel_nand_data *data)
 {
        unsigned long csa, mode;
 
@@ -445,7 +445,7 @@ void __init at91_add_device_nand(struct at91_nand_data *data)
        platform_device_register(&at91cap9_nand_device);
 }
 #else
-void __init at91_add_device_nand(struct at91_nand_data *data) {}
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
 #endif
 
 
index de19bee83f758c55c4b092428b31cfdc573048ce..8ced9bc82099911445a0d459169a185455554e09 100644 (file)
@@ -369,7 +369,7 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
  * -------------------------------------------------------------------- */
 
 #if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE)
-static struct at91_nand_data nand_data;
+static struct atmel_nand_data nand_data;
 
 #define NAND_BASE      AT91_CHIPSELECT_3
 
@@ -382,7 +382,7 @@ static struct resource nand_resources[] = {
 };
 
 static struct platform_device at91rm9200_nand_device = {
-       .name           = "at91_nand",
+       .name           = "atmel_nand",
        .id             = -1,
        .dev            = {
                                .platform_data  = &nand_data,
@@ -391,7 +391,7 @@ static struct platform_device at91rm9200_nand_device = {
        .num_resources  = ARRAY_SIZE(nand_resources),
 };
 
-void __init at91_add_device_nand(struct at91_nand_data *data)
+void __init at91_add_device_nand(struct atmel_nand_data *data)
 {
        unsigned int csa;
 
@@ -429,7 +429,7 @@ void __init at91_add_device_nand(struct at91_nand_data *data)
        platform_device_register(&at91rm9200_nand_device);
 }
 #else
-void __init at91_add_device_nand(struct at91_nand_data *data) {}
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
 #endif
 
 
index 86cba4ac29b1672ffba1cf182b8d08af923a2744..cae5f52f1278e1311baa90e5fe4da69880a02c8c 100644 (file)
@@ -284,7 +284,7 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
  * -------------------------------------------------------------------- */
 
 #if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE)
-static struct at91_nand_data nand_data;
+static struct atmel_nand_data nand_data;
 
 #define NAND_BASE      AT91_CHIPSELECT_3
 
@@ -302,7 +302,7 @@ static struct resource nand_resources[] = {
 };
 
 static struct platform_device at91sam9260_nand_device = {
-       .name           = "at91_nand",
+       .name           = "atmel_nand",
        .id             = -1,
        .dev            = {
                                .platform_data  = &nand_data,
@@ -311,7 +311,7 @@ static struct platform_device at91sam9260_nand_device = {
        .num_resources  = ARRAY_SIZE(nand_resources),
 };
 
-void __init at91_add_device_nand(struct at91_nand_data *data)
+void __init at91_add_device_nand(struct atmel_nand_data *data)
 {
        unsigned long csa, mode;
 
@@ -373,7 +373,7 @@ void __init at91_add_device_nand(struct at91_nand_data *data)
        platform_device_register(&at91sam9260_nand_device);
 }
 #else
-void __init at91_add_device_nand(struct at91_nand_data *data) {}
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
 #endif
 
 
index ec1891375dfbefb9a9090a9ee17e6212c10a3128..483d436af22d84610b84b9a916748b222c35c263 100644 (file)
@@ -199,7 +199,7 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
  * -------------------------------------------------------------------- */
 
 #if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE)
-static struct at91_nand_data nand_data;
+static struct atmel_nand_data nand_data;
 
 #define NAND_BASE      AT91_CHIPSELECT_3
 
@@ -211,8 +211,8 @@ static struct resource nand_resources[] = {
        }
 };
 
-static struct platform_device at91_nand_device = {
-       .name           = "at91_nand",
+static struct platform_device atmel_nand_device = {
+       .name           = "atmel_nand",
        .id             = -1,
        .dev            = {
                                .platform_data  = &nand_data,
@@ -221,7 +221,7 @@ static struct platform_device at91_nand_device = {
        .num_resources  = ARRAY_SIZE(nand_resources),
 };
 
-void __init at91_add_device_nand(struct at91_nand_data *data)
+void __init at91_add_device_nand(struct atmel_nand_data *data)
 {
        unsigned long csa, mode;
 
@@ -262,11 +262,11 @@ void __init at91_add_device_nand(struct at91_nand_data *data)
        at91_set_A_periph(AT91_PIN_PC1, 0);             /* NANDWE */
 
        nand_data = *data;
-       platform_device_register(&at91_nand_device);
+       platform_device_register(&atmel_nand_device);
 }
 
 #else
-void __init at91_add_device_nand(struct at91_nand_data *data) {}
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
 #endif
 
 
index 8a81f76f02000e270641f2f77a2853000e92221f..9762b15f658ac0238bed885ab511e43c8aed83d7 100644 (file)
@@ -353,7 +353,7 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
  * -------------------------------------------------------------------- */
 
 #if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE)
-static struct at91_nand_data nand_data;
+static struct atmel_nand_data nand_data;
 
 #define NAND_BASE      AT91_CHIPSELECT_3
 
@@ -371,7 +371,7 @@ static struct resource nand_resources[] = {
 };
 
 static struct platform_device at91sam9263_nand_device = {
-       .name           = "at91_nand",
+       .name           = "atmel_nand",
        .id             = -1,
        .dev            = {
                                .platform_data  = &nand_data,
@@ -380,7 +380,7 @@ static struct platform_device at91sam9263_nand_device = {
        .num_resources  = ARRAY_SIZE(nand_resources),
 };
 
-void __init at91_add_device_nand(struct at91_nand_data *data)
+void __init at91_add_device_nand(struct atmel_nand_data *data)
 {
        unsigned long csa, mode;
 
@@ -421,7 +421,7 @@ void __init at91_add_device_nand(struct at91_nand_data *data)
        platform_device_register(&at91sam9263_nand_device);
 }
 #else
-void __init at91_add_device_nand(struct at91_nand_data *data) {}
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
 #endif
 
 
index ae28101e75422a5376ff03037a8e6e987adf365c..5f3094870cadcbaa5e92579cbd451b3e9ab89943 100644 (file)
@@ -195,7 +195,7 @@ void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
  * -------------------------------------------------------------------- */
 
 #if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE)
-static struct at91_nand_data nand_data;
+static struct atmel_nand_data nand_data;
 
 #define NAND_BASE      AT91_CHIPSELECT_3
 
@@ -212,8 +212,8 @@ static struct resource nand_resources[] = {
        }
 };
 
-static struct platform_device at91_nand_device = {
-       .name           = "at91_nand",
+static struct platform_device atmel_nand_device = {
+       .name           = "atmel_nand",
        .id             = -1,
        .dev            = {
                                .platform_data  = &nand_data,
@@ -222,7 +222,7 @@ static struct platform_device at91_nand_device = {
        .num_resources  = ARRAY_SIZE(nand_resources),
 };
 
-void __init at91_add_device_nand(struct at91_nand_data *data)
+void __init at91_add_device_nand(struct atmel_nand_data *data)
 {
        unsigned long csa;
 
@@ -259,11 +259,11 @@ void __init at91_add_device_nand(struct at91_nand_data *data)
        at91_set_A_periph(AT91_PIN_PB5, 0);             /* NANDWE */
 
        nand_data = *data;
-       platform_device_register(&at91_nand_device);
+       platform_device_register(&atmel_nand_device);
 }
 
 #else
-void __init at91_add_device_nand(struct at91_nand_data *data) {}
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
 #endif
 
 
index b22a1a00405547cf7be13f22371dd510a5677f33..af2c33aff1a8c9aafa194429248f74bf028eab07 100644 (file)
@@ -142,7 +142,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return cam60_nand_partition;
 }
 
-static struct at91_nand_data __initdata cam60_nand_data = {
+static struct atmel_nand_data __initdata cam60_nand_data = {
        .ale            = 21,
        .cle            = 22,
        // .det_pin     = ... not there
index b4b67eb1cbcb69dbb192d732f8c61380c6945b4a..117cf6c9afce3534b8c904bb4915974728456dbb 100644 (file)
@@ -181,7 +181,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return cap9adk_nand_partitions;
 }
 
-static struct at91_nand_data __initdata cap9adk_nand_data = {
+static struct atmel_nand_data __initdata cap9adk_nand_data = {
        .ale            = 21,
        .cle            = 22,
 //     .det_pin        = ... not connected
index dab958d2592658d7e9b0bb242bb628b3bfd0102c..02a70b2f355bd07e3551e82fa60d96be266cde8b 100644 (file)
@@ -147,7 +147,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return dk_nand_partition;
 }
 
-static struct at91_nand_data __initdata dk_nand_data = {
+static struct atmel_nand_data __initdata dk_nand_data = {
        .ale            = 22,
        .cle            = 21,
        .det_pin        = AT91_PIN_PB1,
index cb065febd95e6e35bf9145accdf72b4946ec9284..082ed59365a4a4f90a13e6ab19897cad6df30f9f 100644 (file)
@@ -105,7 +105,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return kb9202_nand_partition;
 }
 
-static struct at91_nand_data __initdata kb9202_nand_data = {
+static struct atmel_nand_data __initdata kb9202_nand_data = {
        .ale            = 22,
        .cle            = 21,
        // .det_pin     = ... not there
index 8f76af5e219aea79278bff8085243966a7b53b3e..57a6221943ed3b82f57e69d6db53994aaf7fc9f7 100644 (file)
@@ -141,7 +141,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return ek_nand_partition;
 }
 
-static struct at91_nand_data __initdata ek_nand_data = {
+static struct atmel_nand_data __initdata ek_nand_data = {
        .ale            = 21,
        .cle            = 22,
 //     .det_pin        = ... not connected
index 4d1d9c777084dfb3f695bebcffc1b1ab8b11d2ae..6a680795c3c83d80cea369cd85d4fee0269201d4 100644 (file)
@@ -178,7 +178,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return ek_nand_partition;
 }
 
-static struct at91_nand_data __initdata ek_nand_data = {
+static struct atmel_nand_data __initdata ek_nand_data = {
        .ale            = 21,
        .cle            = 22,
 //     .det_pin        = ... not connected
index 08382c0df2211401c1ccb04887a45a938901f4de..43dfbd0d543a07c7aeb7689354e3ea506bff60b1 100644 (file)
@@ -183,7 +183,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return ek_nand_partition;
 }
 
-static struct at91_nand_data __initdata ek_nand_data = {
+static struct atmel_nand_data __initdata ek_nand_data = {
        .ale            = 22,
        .cle            = 21,
 //     .det_pin        = ... not connected
index b4cd5d0ed597ed57bbd90460b1e8b8b7797f3d2f..6605a098011761bb32cc85b58077cfac6f866cbb 100644 (file)
@@ -187,7 +187,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return ek_nand_partition;
 }
 
-static struct at91_nand_data __initdata ek_nand_data = {
+static struct atmel_nand_data __initdata ek_nand_data = {
        .ale            = 21,
        .cle            = 22,
 //     .det_pin        = ... not connected
index b6a70fc735c38b602d47556e1d9d41ea0ce5140c..66e77bb2e079c4ad9592ed1d6dc347159fa079e8 100644 (file)
@@ -96,7 +96,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return ek_nand_partition;
 }
 
-static struct at91_nand_data __initdata ek_nand_data = {
+static struct atmel_nand_data __initdata ek_nand_data = {
        .ale            = 21,
        .cle            = 22,
 //     .det_pin        = ... not connected
index 7079050ab88d75995468151e098263c568b3aa4b..bbbfd06f5e0c37b0a22899f656b869a15b5c5ff4 100644 (file)
@@ -180,7 +180,7 @@ static struct mtd_partition * __init nand_partitions(int size, int *num_partitio
        return yl9200_nand_partition;
 }
 
-static struct at91_nand_data __initdata yl9200_nand_data = {
+static struct atmel_nand_data __initdata yl9200_nand_data = {
        .ale            = 6,
        .cle            = 7,
        // .det_pin     = ... not connected
index af90b00100fd0d96f58df404411e00affce02bcd..8dc48214f0b76ffe44c15e2ca5f74a52641a2086 100644 (file)
@@ -18,6 +18,10 @@ config BOARD_ATSTK1004
        bool "ATSTK1004"
        select CPU_AT32AP7002
 
+config BOARD_ATSTK1006
+       bool "ATSTK1006"
+       select CPU_AT32AP7000
+
 endchoice
 
 
index beead86462e8a6a7f8f7797e746be176268394ba..edecee03742d4a935df734e4e86f9d755c7eb8ad 100644 (file)
@@ -2,3 +2,4 @@ obj-y                           += setup.o flash.o
 obj-$(CONFIG_BOARD_ATSTK1002)  += atstk1002.o
 obj-$(CONFIG_BOARD_ATSTK1003)  += atstk1003.o
 obj-$(CONFIG_BOARD_ATSTK1004)  += atstk1004.o
+obj-$(CONFIG_BOARD_ATSTK1006)  += atstk1002.o
index e11659b732fab61d743f5e72b3487b298c5f9646..8538ba75ef92c8a1def6ee96caea4557618ae061 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * ATSTK1002 daughterboard-specific init code
+ * ATSTK1002/ATSTK1006 daughterboard-specific init code
  *
- * Copyright (C) 2005-2006 Atmel Corporation
+ * Copyright (C) 2005-2007 Atmel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,6 +21,8 @@
 
 #include <asm/io.h>
 #include <asm/setup.h>
+#include <asm/atmel-mci.h>
+
 #include <asm/arch/at32ap700x.h>
 #include <asm/arch/board.h>
 #include <asm/arch/init.h>
@@ -35,6 +37,74 @@ unsigned long at32_board_osc_rates[3] = {
        [2] = 12000000, /* 12 MHz on osc1 */
 };
 
+/*
+ * The ATSTK1006 daughterboard is very similar to the ATSTK1002. Both
+ * have the AT32AP7000 chip on board; the difference is that the
+ * STK1006 has 128 MB SDRAM (the STK1002 uses the 8 MB SDRAM chip on
+ * the STK1000 motherboard) and 256 MB NAND flash (the STK1002 has
+ * none.)
+ *
+ * The RAM difference is handled by the boot loader, so the only
+ * difference we end up handling here is the NAND flash.
+ */
+#ifdef CONFIG_BOARD_ATSTK1006
+#include <linux/mtd/partitions.h>
+#include <asm/arch/smc.h>
+
+static struct smc_timing nand_timing __initdata = {
+       .ncs_read_setup         = 0,
+       .nrd_setup              = 10,
+       .ncs_write_setup        = 0,
+       .nwe_setup              = 10,
+
+       .ncs_read_pulse         = 30,
+       .nrd_pulse              = 15,
+       .ncs_write_pulse        = 30,
+       .nwe_pulse              = 15,
+
+       .read_cycle             = 30,
+       .write_cycle            = 30,
+
+       .ncs_read_recover       = 0,
+       .nrd_recover            = 15,
+       .ncs_write_recover      = 0,
+       /* WE# high -> RE# low min 60 ns */
+       .nwe_recover            = 50,
+};
+
+static struct smc_config nand_config __initdata = {
+       .bus_width              = 1,
+       .nrd_controlled         = 1,
+       .nwe_controlled         = 1,
+       .nwait_mode             = 0,
+       .byte_write             = 0,
+       .tdf_cycles             = 2,
+       .tdf_mode               = 0,
+};
+
+static struct mtd_partition nand_partitions[] = {
+       {
+               .name           = "main",
+               .offset         = 0x00000000,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+static struct mtd_partition *nand_part_info(int size, int *num_partitions)
+{
+       *num_partitions = ARRAY_SIZE(nand_partitions);
+       return nand_partitions;
+}
+
+struct atmel_nand_data atstk1006_nand_data __initdata = {
+       .cle            = 21,
+       .ale            = 22,
+       .rdy_pin        = GPIO_PIN_PB(30),
+       .enable_pin     = GPIO_PIN_PB(29),
+       .partition_info = nand_part_info,
+};
+#endif
+
 struct eth_addr {
        u8 addr[6];
 };
@@ -192,6 +262,21 @@ void __init setup_board(void)
        at32_setup_serial_console(0);
 }
 
+#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
+
+/* MMC card detect requires MACB0 *NOT* be used */
+#ifdef CONFIG_BOARD_ATSTK1002_SW6_CUSTOM
+static struct mci_platform_data __initdata mci0_data = {
+       .detect_pin     = GPIO_PIN_PC(14),      /* gpio30/sdcd */
+       .wp_pin         = GPIO_PIN_PC(15),      /* gpio31/sdwp */
+};
+#define MCI_PDATA      &mci0_data
+#else
+#define MCI_PDATA      NULL
+#endif /* SW6 for sd{cd,wp} routing */
+
+#endif /* SW2 for MMC signal routing */
+
 static int __init atstk1002_init(void)
 {
        /*
@@ -218,6 +303,12 @@ static int __init atstk1002_init(void)
 
        at32_add_system_devices();
 
+#ifdef CONFIG_BOARD_ATSTK1006
+       smc_set_timing(&nand_config, &nand_timing);
+       smc_set_configuration(3, &nand_config);
+       at32_add_device_nand(0, &atstk1006_nand_data);
+#endif
+
 #ifdef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
        at32_add_device_usart(1);
 #else
@@ -235,7 +326,7 @@ static int __init atstk1002_init(void)
        at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
 #endif
 #ifndef CONFIG_BOARD_ATSTK1002_SW2_CUSTOM
-       at32_add_device_mci(0, NULL);
+       at32_add_device_mci(0, MCI_PDATA);
 #endif
 #ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM
        set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
index ea109f435a83adc6ffb67cd8264d8a84552f52c9..591fc73b554a58dc5c71e56fdd5471030dd32c32 100644 (file)
@@ -154,7 +154,7 @@ static int __init atstk1003_init(void)
        at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
 #endif
 #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
-       at32_add_device_mci(0);
+       at32_add_device_mci(0, NULL);
 #endif
        at32_add_device_usba(0, NULL);
 #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
index c7236df74d7422125d6048c33804f8d167c1c91c..d9c5e0a21256cf30cd7c9a031b27753ddd2f67d6 100644 (file)
@@ -137,7 +137,7 @@ static int __init atstk1004_init(void)
        at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
 #endif
 #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
-       at32_add_device_mci(0);
+       at32_add_device_mci(0, NULL);
 #endif
        at32_add_device_lcdc(0, &atstk1000_lcdc_data,
                             fbmem_start, fbmem_size, 0);
index abd954fb7ba0ccbf37b1494b93c1600045fd1a88..7e7f32771ae1aa9d0a25b38739cebc81352173a4 100644 (file)
@@ -43,6 +43,9 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
 {
        struct clock_event_device *evdev = dev_id;
 
+       if (unlikely(!(intc_get_pending(0) & 1)))
+               return IRQ_NONE;
+
        /*
         * Disable the interrupt until the clockevent subsystem
         * reprograms it.
@@ -55,7 +58,8 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
 
 static struct irqaction timer_irqaction = {
        .handler        = timer_interrupt,
-       .flags          = IRQF_TIMER | IRQF_DISABLED,
+       /* Oprofile uses the same irq as the timer, so allow it to be shared */
+       .flags          = IRQF_TIMER | IRQF_DISABLED | IRQF_SHARED,
        .name           = "avr32_comparator",
 };
 
index 604f44f5dd164833a9bfdb075a7d65a713617fff..1617048c86c5955bb549d84be9d1bf77ca90c6af 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
+#include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/usb/atmel_usba_udc.h>
 
@@ -1285,7 +1286,6 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
 {
        struct mci_platform_data        _data;
        struct platform_device          *pdev;
-       struct dw_dma_slave             *dws;
 
        if (id != 0)
                return NULL;
@@ -1300,7 +1300,9 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
 
        if (!data) {
                data = &_data;
-               memset(data, 0, sizeof(struct mci_platform_data));
+               memset(data, -1, sizeof(struct mci_platform_data));
+               data->detect_pin = GPIO_PIN_NONE;
+               data->wp_pin = GPIO_PIN_NONE;
        }
 
        if (platform_device_add_data(pdev, data,
@@ -1314,12 +1316,10 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
        select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */
        select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */
 
-       if (data) {
-               if (data->detect_pin != GPIO_PIN_NONE)
-                       at32_select_gpio(data->detect_pin, 0);
-               if (data->wp_pin != GPIO_PIN_NONE)
-                       at32_select_gpio(data->wp_pin, 0);
-       }
+       if (gpio_is_valid(data->detect_pin))
+               at32_select_gpio(data->detect_pin, 0);
+       if (gpio_is_valid(data->wp_pin))
+               at32_select_gpio(data->wp_pin, 0);
 
        atmel_mci0_pclk.dev = &pdev->dev;
 
@@ -1853,11 +1853,11 @@ at32_add_device_cf(unsigned int id, unsigned int extint,
        if (at32_init_ide_or_cf(pdev, data->cs, extint))
                goto fail;
 
-       if (data->detect_pin != GPIO_PIN_NONE)
+       if (gpio_is_valid(data->detect_pin))
                at32_select_gpio(data->detect_pin, AT32_GPIOF_DEGLITCH);
-       if (data->reset_pin != GPIO_PIN_NONE)
+       if (gpio_is_valid(data->reset_pin))
                at32_select_gpio(data->reset_pin, 0);
-       if (data->vcc_pin != GPIO_PIN_NONE)
+       if (gpio_is_valid(data->vcc_pin))
                at32_select_gpio(data->vcc_pin, 0);
        /* READY is used as extint, so we can't select it as gpio */
 
@@ -1870,6 +1870,58 @@ fail:
 }
 #endif
 
+/* --------------------------------------------------------------------
+ * NAND Flash / SmartMedia
+ * -------------------------------------------------------------------- */
+static struct resource smc_cs3_resource[] __initdata = {
+       {
+               .start  = 0x0c000000,
+               .end    = 0x0fffffff,
+               .flags  = IORESOURCE_MEM,
+       }, {
+               .start  = 0xfff03c00,
+               .end    = 0xfff03fff,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+struct platform_device *__init
+at32_add_device_nand(unsigned int id, struct atmel_nand_data *data)
+{
+       struct platform_device *pdev;
+
+       if (id != 0 || !data)
+               return NULL;
+
+       pdev = platform_device_alloc("atmel_nand", id);
+       if (!pdev)
+               goto fail;
+
+       if (platform_device_add_resources(pdev, smc_cs3_resource,
+                               ARRAY_SIZE(smc_cs3_resource)))
+               goto fail;
+
+       if (platform_device_add_data(pdev, data,
+                               sizeof(struct atmel_nand_data)))
+               goto fail;
+
+       set_ebi_sfr_bits(HMATRIX_BIT(CS3A));
+       if (data->enable_pin)
+               at32_select_gpio(data->enable_pin,
+                               AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
+       if (data->rdy_pin)
+               at32_select_gpio(data->rdy_pin, 0);
+       if (data->det_pin)
+               at32_select_gpio(data->det_pin, 0);
+
+       platform_device_add(pdev);
+       return pdev;
+
+fail:
+       platform_device_put(pdev);
+       return NULL;
+}
+
 /* --------------------------------------------------------------------
  * AC97C
  * -------------------------------------------------------------------- */
@@ -1885,9 +1937,11 @@ static struct clk atmel_ac97c0_pclk = {
        .index          = 10,
 };
 
-struct platform_device *__init at32_add_device_ac97c(unsigned int id)
+struct platform_device *__init
+at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data)
 {
        struct platform_device *pdev;
+       struct ac97c_platform_data _data;
 
        if (id != 0)
                return NULL;
@@ -1898,19 +1952,37 @@ struct platform_device *__init at32_add_device_ac97c(unsigned int id)
 
        if (platform_device_add_resources(pdev, atmel_ac97c0_resource,
                                ARRAY_SIZE(atmel_ac97c0_resource)))
-               goto err_add_resources;
+               goto fail;
 
-       select_peripheral(PB(20), PERIPH_B, 0); /* SYNC */
-       select_peripheral(PB(21), PERIPH_B, 0); /* SDO  */
-       select_peripheral(PB(22), PERIPH_B, 0); /* SDI  */
-       select_peripheral(PB(23), PERIPH_B, 0); /* SCLK */
+       if (!data) {
+               data = &_data;
+               memset(data, 0, sizeof(struct ac97c_platform_data));
+               data->reset_pin = GPIO_PIN_NONE;
+       }
+
+       data->dma_rx_periph_id = 3;
+       data->dma_tx_periph_id = 4;
+       data->dma_controller_id = 0;
+
+       if (platform_device_add_data(pdev, data,
+                               sizeof(struct ac97c_platform_data)))
+               goto fail;
+
+       select_peripheral(PB(20), PERIPH_B, 0); /* SDO  */
+       select_peripheral(PB(21), PERIPH_B, 0); /* SYNC */
+       select_peripheral(PB(22), PERIPH_B, 0); /* SCLK */
+       select_peripheral(PB(23), PERIPH_B, 0); /* SDI  */
+
+       /* TODO: gpio_is_valid(data->reset_pin) with kernel 2.6.26. */
+       if (data->reset_pin != GPIO_PIN_NONE)
+               at32_select_gpio(data->reset_pin, 0);
 
        atmel_ac97c0_pclk.dev = &pdev->dev;
 
        platform_device_add(pdev);
        return pdev;
 
-err_add_resources:
+fail:
        platform_device_put(pdev);
        return NULL;
 }
index fa427ed427874843ea523c30c2c69b46089914be..b2d9bc61a35ca1ca55572bf8aa6636ecde50f9cc 100644 (file)
@@ -278,4 +278,4 @@ static int __init hsmc_init(void)
 {
        return platform_driver_register(&hsmc_driver);
 }
-arch_initcall(hsmc_init);
+core_initcall(hsmc_init);
index 2672f4d278ac52695592f23f6ddc501fcce3b7a8..7a37d06376be893799fa2bfdb30311251a3edbeb 100644 (file)
@@ -125,9 +125,9 @@ void kvm_arch_hardware_enable(void *garbage)
                                PAGE_KERNEL));
        local_irq_save(saved_psr);
        slot = ia64_itr_entry(0x3, KVM_VMM_BASE, pte, KVM_VMM_SHIFT);
+       local_irq_restore(saved_psr);
        if (slot < 0)
                return;
-       local_irq_restore(saved_psr);
 
        spin_lock(&vp_lock);
        status = ia64_pal_vp_init_env(kvm_vsa_base ?
@@ -160,9 +160,9 @@ void kvm_arch_hardware_disable(void *garbage)
 
        local_irq_save(saved_psr);
        slot = ia64_itr_entry(0x3, KVM_VMM_BASE, pte, KVM_VMM_SHIFT);
+       local_irq_restore(saved_psr);
        if (slot < 0)
                return;
-       local_irq_restore(saved_psr);
 
        status = ia64_pal_vp_exit_env(host_iva);
        if (status)
@@ -1253,6 +1253,7 @@ static int vti_vcpu_setup(struct kvm_vcpu *vcpu, int id)
 uninit:
        kvm_vcpu_uninit(vcpu);
 fail:
+       local_irq_restore(psr);
        return r;
 }
 
index be255ebb609c98ddda7c280c0a1ca938d5d6f571..18072e03a019e1411b88a5e0b3fa1ee48e2b6458 100644 (file)
@@ -210,19 +210,19 @@ static int vfs_statfs_hpux(struct dentry *dentry, struct hpux_statfs *buf)
 }
 
 /* hpux statfs */
-asmlinkage long hpux_statfs(const char __user *path,
+asmlinkage long hpux_statfs(const char __user *pathname,
                                                struct hpux_statfs __user *buf)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct hpux_statfs tmp;
-               error = vfs_statfs_hpux(nd.path.dentry, &tmp);
+               error = vfs_statfs_hpux(path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
index 75dff7cfa814130a465098ea880c1f05cbf882e5..5a5602da50910d441f806c6b0a111fcd3e19aab3 100644 (file)
@@ -177,7 +177,8 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid,
                                                    vcpu->arch.msr & MSR_PR);
 }
 
-void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, u64 eaddr, u64 asid)
+void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
+                           gva_t eend, u32 asid)
 {
        unsigned int pid = asid & 0xff;
        int i;
@@ -191,7 +192,7 @@ void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, u64 eaddr, u64 asid)
                if (!get_tlb_v(stlbe))
                        continue;
 
-               if (eaddr < get_tlb_eaddr(stlbe))
+               if (eend < get_tlb_eaddr(stlbe))
                        continue;
 
                if (eaddr > get_tlb_end(stlbe))
index 0000974612834121aa9c6a4048e0e89e09695374..8c605d0a5488fdce0268cbbdf3055d9404028f7b 100644 (file)
@@ -137,7 +137,7 @@ static int kvmppc_emul_tlbwe(struct kvm_vcpu *vcpu, u32 inst)
        if (tlbe->word0 & PPC44x_TLB_VALID) {
                eaddr = get_tlb_eaddr(tlbe);
                asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid;
-               kvmppc_mmu_invalidate(vcpu, eaddr, asid);
+               kvmppc_mmu_invalidate(vcpu, eaddr, get_tlb_end(tlbe), asid);
        }
 
        switch (ws) {
index 4e0633c413f3841dc1132c39d06dc5645b57f66d..ed60f3a74a85f1ca6a8e994a974fc2bba6dd1652 100644 (file)
 #include <asm/uaccess.h>
 
 static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
-                                              u64 guestaddr)
+                                              unsigned long guestaddr)
 {
-       u64 prefix  = vcpu->arch.sie_block->prefix;
-       u64 origin  = vcpu->kvm->arch.guest_origin;
-       u64 memsize = vcpu->kvm->arch.guest_memsize;
+       unsigned long prefix  = vcpu->arch.sie_block->prefix;
+       unsigned long origin  = vcpu->kvm->arch.guest_origin;
+       unsigned long memsize = vcpu->kvm->arch.guest_memsize;
 
        if (guestaddr < 2 * PAGE_SIZE)
                guestaddr += prefix;
@@ -37,7 +37,7 @@ static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
        return (void __user *) guestaddr;
 }
 
-static inline int get_guest_u64(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int get_guest_u64(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                                u64 *result)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -47,10 +47,10 @@ static inline int get_guest_u64(struct kvm_vcpu *vcpu, u64 guestaddr,
        if (IS_ERR((void __force *) uptr))
                return PTR_ERR((void __force *) uptr);
 
-       return get_user(*result, (u64 __user *) uptr);
+       return get_user(*result, (unsigned long __user *) uptr);
 }
 
-static inline int get_guest_u32(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int get_guest_u32(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                                u32 *result)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -63,7 +63,7 @@ static inline int get_guest_u32(struct kvm_vcpu *vcpu, u64 guestaddr,
        return get_user(*result, (u32 __user *) uptr);
 }
 
-static inline int get_guest_u16(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int get_guest_u16(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                                u16 *result)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -76,7 +76,7 @@ static inline int get_guest_u16(struct kvm_vcpu *vcpu, u64 guestaddr,
        return get_user(*result, (u16 __user *) uptr);
 }
 
-static inline int get_guest_u8(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int get_guest_u8(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                               u8 *result)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -87,7 +87,7 @@ static inline int get_guest_u8(struct kvm_vcpu *vcpu, u64 guestaddr,
        return get_user(*result, (u8 __user *) uptr);
 }
 
-static inline int put_guest_u64(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int put_guest_u64(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                                u64 value)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -100,7 +100,7 @@ static inline int put_guest_u64(struct kvm_vcpu *vcpu, u64 guestaddr,
        return put_user(value, (u64 __user *) uptr);
 }
 
-static inline int put_guest_u32(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int put_guest_u32(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                                u32 value)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -113,7 +113,7 @@ static inline int put_guest_u32(struct kvm_vcpu *vcpu, u64 guestaddr,
        return put_user(value, (u32 __user *) uptr);
 }
 
-static inline int put_guest_u16(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int put_guest_u16(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                                u16 value)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -126,7 +126,7 @@ static inline int put_guest_u16(struct kvm_vcpu *vcpu, u64 guestaddr,
        return put_user(value, (u16 __user *) uptr);
 }
 
-static inline int put_guest_u8(struct kvm_vcpu *vcpu, u64 guestaddr,
+static inline int put_guest_u8(struct kvm_vcpu *vcpu, unsigned long guestaddr,
                               u8 value)
 {
        void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
@@ -138,7 +138,8 @@ static inline int put_guest_u8(struct kvm_vcpu *vcpu, u64 guestaddr,
 }
 
 
-static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu, u64 guestdest,
+static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
+                                      unsigned long guestdest,
                                       const void *from, unsigned long n)
 {
        int rc;
@@ -153,12 +154,12 @@ static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu, u64 guestdest,
        return 0;
 }
 
-static inline int copy_to_guest(struct kvm_vcpu *vcpu, u64 guestdest,
+static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
                                const void *from, unsigned long n)
 {
-       u64 prefix  = vcpu->arch.sie_block->prefix;
-       u64 origin  = vcpu->kvm->arch.guest_origin;
-       u64 memsize = vcpu->kvm->arch.guest_memsize;
+       unsigned long prefix  = vcpu->arch.sie_block->prefix;
+       unsigned long origin  = vcpu->kvm->arch.guest_origin;
+       unsigned long memsize = vcpu->kvm->arch.guest_memsize;
 
        if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
                goto slowpath;
@@ -189,7 +190,8 @@ slowpath:
 }
 
 static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
-                                        u64 guestsrc, unsigned long n)
+                                        unsigned long guestsrc,
+                                        unsigned long n)
 {
        int rc;
        unsigned long i;
@@ -204,11 +206,11 @@ static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
 }
 
 static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
-                                 u64 guestsrc, unsigned long n)
+                                 unsigned long guestsrc, unsigned long n)
 {
-       u64 prefix  = vcpu->arch.sie_block->prefix;
-       u64 origin  = vcpu->kvm->arch.guest_origin;
-       u64 memsize = vcpu->kvm->arch.guest_memsize;
+       unsigned long prefix  = vcpu->arch.sie_block->prefix;
+       unsigned long origin  = vcpu->kvm->arch.guest_origin;
+       unsigned long memsize = vcpu->kvm->arch.guest_memsize;
 
        if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
                goto slowpath;
@@ -238,11 +240,12 @@ slowpath:
        return __copy_from_guest_slow(vcpu, to, guestsrc, n);
 }
 
-static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu, u64 guestdest,
+static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
+                                        unsigned long guestdest,
                                         const void *from, unsigned long n)
 {
-       u64 origin  = vcpu->kvm->arch.guest_origin;
-       u64 memsize = vcpu->kvm->arch.guest_memsize;
+       unsigned long origin  = vcpu->kvm->arch.guest_origin;
+       unsigned long memsize = vcpu->kvm->arch.guest_memsize;
 
        if (guestdest + n > memsize)
                return -EFAULT;
@@ -256,10 +259,11 @@ static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu, u64 guestdest,
 }
 
 static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
-                                          u64 guestsrc, unsigned long n)
+                                          unsigned long guestsrc,
+                                          unsigned long n)
 {
-       u64 origin  = vcpu->kvm->arch.guest_origin;
-       u64 memsize = vcpu->kvm->arch.guest_memsize;
+       unsigned long origin  = vcpu->kvm->arch.guest_origin;
+       unsigned long memsize = vcpu->kvm->arch.guest_memsize;
 
        if (guestsrc + n > memsize)
                return -EFAULT;
index 47a0b642174c1ef807ca5d16774f9d10c6584197..61236102203e8746933ae96dab4876b421d901d6 100644 (file)
@@ -20,7 +20,7 @@
 #include "kvm-s390.h"
 #include "gaccess.h"
 
-static int handle_lctg(struct kvm_vcpu *vcpu)
+static int handle_lctlg(struct kvm_vcpu *vcpu)
 {
        int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4;
        int reg3 = vcpu->arch.sie_block->ipa & 0x000f;
@@ -30,7 +30,7 @@ static int handle_lctg(struct kvm_vcpu *vcpu)
        u64 useraddr;
        int reg, rc;
 
-       vcpu->stat.instruction_lctg++;
+       vcpu->stat.instruction_lctlg++;
        if ((vcpu->arch.sie_block->ipb & 0xff) != 0x2f)
                return -ENOTSUPP;
 
@@ -38,9 +38,12 @@ static int handle_lctg(struct kvm_vcpu *vcpu)
        if (base2)
                useraddr += vcpu->arch.guest_gprs[base2];
 
+       if (useraddr & 7)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
        reg = reg1;
 
-       VCPU_EVENT(vcpu, 5, "lctg r1:%x, r3:%x,b2:%x,d2:%x", reg1, reg3, base2,
+       VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x,b2:%x,d2:%x", reg1, reg3, base2,
                   disp2);
 
        do {
@@ -74,6 +77,9 @@ static int handle_lctl(struct kvm_vcpu *vcpu)
        if (base2)
                useraddr += vcpu->arch.guest_gprs[base2];
 
+       if (useraddr & 3)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
        VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x,b2:%x,d2:%x", reg1, reg3, base2,
                   disp2);
 
@@ -99,7 +105,7 @@ static intercept_handler_t instruction_handlers[256] = {
        [0xae] = kvm_s390_handle_sigp,
        [0xb2] = kvm_s390_handle_priv,
        [0xb7] = handle_lctl,
-       [0xeb] = handle_lctg,
+       [0xeb] = handle_lctlg,
 };
 
 static int handle_noop(struct kvm_vcpu *vcpu)
index 11230b0db957e3f8d5c3cbea6016bd9d917f0760..2960702b48246488087546669b1459524ead2958 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/lowcore.h>
 #include <asm/uaccess.h>
 #include <linux/kvm_host.h>
+#include <linux/signal.h>
 #include "kvm-s390.h"
 #include "gaccess.h"
 
@@ -246,15 +247,10 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
        default:
                BUG();
        }
-
        if (exception) {
-               VCPU_EVENT(vcpu, 1, "%s", "program exception while delivering"
-                          " interrupt");
-               kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-               if (inti->type == KVM_S390_PROGRAM_INT) {
-                       printk(KERN_WARNING "kvm: recursive program check\n");
-                       BUG();
-               }
+               printk("kvm: The guest lowcore is not mapped during interrupt "
+                       "delivery, killing userspace\n");
+               do_exit(SIGKILL);
        }
 }
 
@@ -277,14 +273,11 @@ static int __try_deliver_ckc_interrupt(struct kvm_vcpu *vcpu)
                __LC_EXT_NEW_PSW, sizeof(psw_t));
        if (rc == -EFAULT)
                exception = 1;
-
        if (exception) {
-               VCPU_EVENT(vcpu, 1, "%s", "program exception while delivering" \
-                          " ckc interrupt");
-               kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-               return 0;
+               printk("kvm: The guest lowcore is not mapped during interrupt "
+                       "delivery, killing userspace\n");
+               do_exit(SIGKILL);
        }
-
        return 1;
 }
 
index 1782cbcd2829545270d897cd4f911d95f54d0fa7..8b00eb2ddf57459c9469d8455d0762d365a71c1f 100644 (file)
@@ -39,7 +39,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "exit_instruction", VCPU_STAT(exit_instruction) },
        { "exit_program_interruption", VCPU_STAT(exit_program_interruption) },
        { "exit_instr_and_program_int", VCPU_STAT(exit_instr_and_program) },
-       { "instruction_lctg", VCPU_STAT(instruction_lctg) },
+       { "instruction_lctlg", VCPU_STAT(instruction_lctlg) },
        { "instruction_lctl", VCPU_STAT(instruction_lctl) },
        { "deliver_emergency_signal", VCPU_STAT(deliver_emergency_signal) },
        { "deliver_service_signal", VCPU_STAT(deliver_service_signal) },
@@ -112,7 +112,12 @@ long kvm_arch_dev_ioctl(struct file *filp,
 
 int kvm_dev_ioctl_check_extension(long ext)
 {
-       return 0;
+       switch (ext) {
+       case KVM_CAP_USER_MEMORY:
+               return 1;
+       default:
+               return 0;
+       }
 }
 
 /* Section: vm related */
index 5a556114eaa58dd067955d0048f931cd5a0ac369..170392687ce042f64c69ea7f4fe85d863025b768 100644 (file)
@@ -43,7 +43,8 @@
 #define SIGP_STAT_RECEIVER_CHECK    0x00000001UL
 
 
-static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, u64 *reg)
+static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr,
+                       unsigned long *reg)
 {
        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
        int rc;
@@ -167,7 +168,7 @@ static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
 }
 
 static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
-                            u64 *reg)
+                            unsigned long *reg)
 {
        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
        struct kvm_s390_local_interrupt *li;
index b0e4ddca6c1856a2441fe9641ae46cc71ab39724..2fa231923cf7e90d9e969478f22207ba0040cd57 100644 (file)
@@ -1814,6 +1814,7 @@ int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva)
        spin_unlock(&vcpu->kvm->mmu_lock);
        return r;
 }
+EXPORT_SYMBOL_GPL(kvm_mmu_unprotect_page_virt);
 
 void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu)
 {
@@ -1870,6 +1871,12 @@ void kvm_enable_tdp(void)
 }
 EXPORT_SYMBOL_GPL(kvm_enable_tdp);
 
+void kvm_disable_tdp(void)
+{
+       tdp_enabled = false;
+}
+EXPORT_SYMBOL_GPL(kvm_disable_tdp);
+
 static void free_mmu_pages(struct kvm_vcpu *vcpu)
 {
        struct kvm_mmu_page *sp;
index b756e876dce391b222c6e4ec84c7dd6447e738de..e2ee264740c7dbb6ea6717b3aa7617c393fd4831 100644 (file)
@@ -453,7 +453,8 @@ static __init int svm_hardware_setup(void)
        if (npt_enabled) {
                printk(KERN_INFO "kvm: Nested Paging enabled\n");
                kvm_enable_tdp();
-       }
+       } else
+               kvm_disable_tdp();
 
        return 0;
 
@@ -1007,10 +1008,13 @@ static int pf_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
        struct kvm *kvm = svm->vcpu.kvm;
        u64 fault_address;
        u32 error_code;
+       bool event_injection = false;
 
        if (!irqchip_in_kernel(kvm) &&
-               is_external_interrupt(exit_int_info))
+           is_external_interrupt(exit_int_info)) {
+               event_injection = true;
                push_irq(&svm->vcpu, exit_int_info & SVM_EVTINJ_VEC_MASK);
+       }
 
        fault_address  = svm->vmcb->control.exit_info_2;
        error_code = svm->vmcb->control.exit_info_1;
@@ -1024,6 +1028,8 @@ static int pf_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
                            (u32)fault_address, (u32)(fault_address >> 32),
                            handler);
 
+       if (event_injection)
+               kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
        return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
 }
 
index 0cac63701719c9dd265cb7ca2078c97379e9f235..2a69773e3b26235f3261aea232ef5fe17411d6cb 100644 (file)
@@ -2298,6 +2298,8 @@ static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                cr2 = vmcs_readl(EXIT_QUALIFICATION);
                KVMTRACE_3D(PAGE_FAULT, vcpu, error_code, (u32)cr2,
                            (u32)((u64)cr2 >> 32), handler);
+               if (vect_info & VECTORING_INFO_VALID_MASK)
+                       kvm_mmu_unprotect_page_virt(vcpu, cr2);
                return kvm_mmu_page_fault(vcpu, cr2, error_code);
        }
 
@@ -3116,15 +3118,6 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
                return ERR_PTR(-ENOMEM);
 
        allocate_vpid(vmx);
-       if (id == 0 && vm_need_ept()) {
-               kvm_mmu_set_base_ptes(VMX_EPT_READABLE_MASK |
-                       VMX_EPT_WRITABLE_MASK |
-                       VMX_EPT_DEFAULT_MT << VMX_EPT_MT_EPTE_SHIFT);
-               kvm_mmu_set_mask_ptes(0ull, VMX_EPT_FAKE_ACCESSED_MASK,
-                               VMX_EPT_FAKE_DIRTY_MASK, 0ull,
-                               VMX_EPT_EXECUTABLE_MASK);
-               kvm_enable_tdp();
-       }
 
        err = kvm_vcpu_init(&vmx->vcpu, kvm, id);
        if (err)
@@ -3303,8 +3296,17 @@ static int __init vmx_init(void)
        vmx_disable_intercept_for_msr(vmx_msr_bitmap, MSR_IA32_SYSENTER_ESP);
        vmx_disable_intercept_for_msr(vmx_msr_bitmap, MSR_IA32_SYSENTER_EIP);
 
-       if (cpu_has_vmx_ept())
+       if (vm_need_ept()) {
                bypass_guest_pf = 0;
+               kvm_mmu_set_base_ptes(VMX_EPT_READABLE_MASK |
+                       VMX_EPT_WRITABLE_MASK |
+                       VMX_EPT_DEFAULT_MT << VMX_EPT_MT_EPTE_SHIFT);
+               kvm_mmu_set_mask_ptes(0ull, VMX_EPT_FAKE_ACCESSED_MASK,
+                               VMX_EPT_FAKE_DIRTY_MASK, 0ull,
+                               VMX_EPT_EXECUTABLE_MASK);
+               kvm_enable_tdp();
+       } else
+               kvm_disable_tdp();
 
        if (bypass_guest_pf)
                kvm_mmu_set_nonpresent_ptes(~0xffeull, 0ull);
index 9f1cdb011cfff3d93b4cf8fe1f64349768183742..5916191420c714d7e9b43ca9b1bf995672f88eab 100644 (file)
@@ -3184,6 +3184,10 @@ static void seg_desct_to_kvm_desct(struct desc_struct *seg_desc, u16 selector,
        kvm_desct->base |= seg_desc->base2 << 24;
        kvm_desct->limit = seg_desc->limit0;
        kvm_desct->limit |= seg_desc->limit << 16;
+       if (seg_desc->g) {
+               kvm_desct->limit <<= 12;
+               kvm_desct->limit |= 0xfff;
+       }
        kvm_desct->selector = selector;
        kvm_desct->type = seg_desc->type;
        kvm_desct->present = seg_desc->p;
@@ -3223,6 +3227,7 @@ static void get_segment_descritptor_dtable(struct kvm_vcpu *vcpu,
 static int load_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
                                         struct desc_struct *seg_desc)
 {
+       gpa_t gpa;
        struct descriptor_table dtable;
        u16 index = selector >> 3;
 
@@ -3232,13 +3237,16 @@ static int load_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
                kvm_queue_exception_e(vcpu, GP_VECTOR, selector & 0xfffc);
                return 1;
        }
-       return kvm_read_guest(vcpu->kvm, dtable.base + index * 8, seg_desc, 8);
+       gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, dtable.base);
+       gpa += index * 8;
+       return kvm_read_guest(vcpu->kvm, gpa, seg_desc, 8);
 }
 
 /* allowed just for 8 bytes segments */
 static int save_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
                                         struct desc_struct *seg_desc)
 {
+       gpa_t gpa;
        struct descriptor_table dtable;
        u16 index = selector >> 3;
 
@@ -3246,7 +3254,9 @@ static int save_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
 
        if (dtable.limit < index * 8 + 7)
                return 1;
-       return kvm_write_guest(vcpu->kvm, dtable.base + index * 8, seg_desc, 8);
+       gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, dtable.base);
+       gpa += index * 8;
+       return kvm_write_guest(vcpu->kvm, gpa, seg_desc, 8);
 }
 
 static u32 get_tss_base_addr(struct kvm_vcpu *vcpu,
@@ -3258,55 +3268,7 @@ static u32 get_tss_base_addr(struct kvm_vcpu *vcpu,
        base_addr |= (seg_desc->base1 << 16);
        base_addr |= (seg_desc->base2 << 24);
 
-       return base_addr;
-}
-
-static int load_tss_segment32(struct kvm_vcpu *vcpu,
-                             struct desc_struct *seg_desc,
-                             struct tss_segment_32 *tss)
-{
-       u32 base_addr;
-
-       base_addr = get_tss_base_addr(vcpu, seg_desc);
-
-       return kvm_read_guest(vcpu->kvm, base_addr, tss,
-                             sizeof(struct tss_segment_32));
-}
-
-static int save_tss_segment32(struct kvm_vcpu *vcpu,
-                             struct desc_struct *seg_desc,
-                             struct tss_segment_32 *tss)
-{
-       u32 base_addr;
-
-       base_addr = get_tss_base_addr(vcpu, seg_desc);
-
-       return kvm_write_guest(vcpu->kvm, base_addr, tss,
-                              sizeof(struct tss_segment_32));
-}
-
-static int load_tss_segment16(struct kvm_vcpu *vcpu,
-                             struct desc_struct *seg_desc,
-                             struct tss_segment_16 *tss)
-{
-       u32 base_addr;
-
-       base_addr = get_tss_base_addr(vcpu, seg_desc);
-
-       return kvm_read_guest(vcpu->kvm, base_addr, tss,
-                             sizeof(struct tss_segment_16));
-}
-
-static int save_tss_segment16(struct kvm_vcpu *vcpu,
-                             struct desc_struct *seg_desc,
-                             struct tss_segment_16 *tss)
-{
-       u32 base_addr;
-
-       base_addr = get_tss_base_addr(vcpu, seg_desc);
-
-       return kvm_write_guest(vcpu->kvm, base_addr, tss,
-                              sizeof(struct tss_segment_16));
+       return vcpu->arch.mmu.gva_to_gpa(vcpu, base_addr);
 }
 
 static u16 get_segment_selector(struct kvm_vcpu *vcpu, int seg)
@@ -3466,20 +3428,26 @@ static int load_state_from_tss16(struct kvm_vcpu *vcpu,
 }
 
 static int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
-                      struct desc_struct *cseg_desc,
+                      u32 old_tss_base,
                       struct desc_struct *nseg_desc)
 {
        struct tss_segment_16 tss_segment_16;
        int ret = 0;
 
-       if (load_tss_segment16(vcpu, cseg_desc, &tss_segment_16))
+       if (kvm_read_guest(vcpu->kvm, old_tss_base, &tss_segment_16,
+                          sizeof tss_segment_16))
                goto out;
 
        save_state_to_tss16(vcpu, &tss_segment_16);
-       save_tss_segment16(vcpu, cseg_desc, &tss_segment_16);
 
-       if (load_tss_segment16(vcpu, nseg_desc, &tss_segment_16))
+       if (kvm_write_guest(vcpu->kvm, old_tss_base, &tss_segment_16,
+                           sizeof tss_segment_16))
                goto out;
+
+       if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
+                          &tss_segment_16, sizeof tss_segment_16))
+               goto out;
+
        if (load_state_from_tss16(vcpu, &tss_segment_16))
                goto out;
 
@@ -3489,20 +3457,26 @@ out:
 }
 
 static int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
-                      struct desc_struct *cseg_desc,
+                      u32 old_tss_base,
                       struct desc_struct *nseg_desc)
 {
        struct tss_segment_32 tss_segment_32;
        int ret = 0;
 
-       if (load_tss_segment32(vcpu, cseg_desc, &tss_segment_32))
+       if (kvm_read_guest(vcpu->kvm, old_tss_base, &tss_segment_32,
+                          sizeof tss_segment_32))
                goto out;
 
        save_state_to_tss32(vcpu, &tss_segment_32);
-       save_tss_segment32(vcpu, cseg_desc, &tss_segment_32);
 
-       if (load_tss_segment32(vcpu, nseg_desc, &tss_segment_32))
+       if (kvm_write_guest(vcpu->kvm, old_tss_base, &tss_segment_32,
+                           sizeof tss_segment_32))
+               goto out;
+
+       if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
+                          &tss_segment_32, sizeof tss_segment_32))
                goto out;
+
        if (load_state_from_tss32(vcpu, &tss_segment_32))
                goto out;
 
@@ -3517,16 +3491,20 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
        struct desc_struct cseg_desc;
        struct desc_struct nseg_desc;
        int ret = 0;
+       u32 old_tss_base = get_segment_base(vcpu, VCPU_SREG_TR);
+       u16 old_tss_sel = get_segment_selector(vcpu, VCPU_SREG_TR);
 
-       kvm_get_segment(vcpu, &tr_seg, VCPU_SREG_TR);
+       old_tss_base = vcpu->arch.mmu.gva_to_gpa(vcpu, old_tss_base);
 
+       /* FIXME: Handle errors. Failure to read either TSS or their
+        * descriptors should generate a pagefault.
+        */
        if (load_guest_segment_descriptor(vcpu, tss_selector, &nseg_desc))
                goto out;
 
-       if (load_guest_segment_descriptor(vcpu, tr_seg.selector, &cseg_desc))
+       if (load_guest_segment_descriptor(vcpu, old_tss_sel, &cseg_desc))
                goto out;
 
-
        if (reason != TASK_SWITCH_IRET) {
                int cpl;
 
@@ -3544,8 +3522,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
 
        if (reason == TASK_SWITCH_IRET || reason == TASK_SWITCH_JMP) {
                cseg_desc.type &= ~(1 << 1); //clear the B flag
-               save_guest_segment_descriptor(vcpu, tr_seg.selector,
-                                             &cseg_desc);
+               save_guest_segment_descriptor(vcpu, old_tss_sel, &cseg_desc);
        }
 
        if (reason == TASK_SWITCH_IRET) {
@@ -3557,10 +3534,10 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
        kvm_x86_ops->cache_regs(vcpu);
 
        if (nseg_desc.type & 8)
-               ret = kvm_task_switch_32(vcpu, tss_selector, &cseg_desc,
+               ret = kvm_task_switch_32(vcpu, tss_selector, old_tss_base,
                                         &nseg_desc);
        else
-               ret = kvm_task_switch_16(vcpu, tss_selector, &cseg_desc,
+               ret = kvm_task_switch_16(vcpu, tss_selector, old_tss_base,
                                         &nseg_desc);
 
        if (reason == TASK_SWITCH_CALL || reason == TASK_SWITCH_GATE) {
index 4ba3aec9e1cd0dc50444c05857467ade96a0fc4e..7b0c35207d9b4fb034b3414d6a9b4788830d7cac 100644 (file)
@@ -192,7 +192,7 @@ static inline void a2232_receive_char(struct a2232_port *port, int ch, int err)
        Maybe one could implement a more efficient version by not only
        transferring one character at a time.
 */
-       struct tty_struct *tty = port->gs.tty;
+       struct tty_struct *tty = port->gs.port.tty;
 
 #if 0
        switch(err) {
@@ -226,7 +226,7 @@ static void a2232_disable_tx_interrupts(void *ptr)
 
        /* Does this here really have to be? */
        local_irq_save(flags);
-       port->gs.flags &= ~GS_TX_INTEN;
+       port->gs.port.flags &= ~GS_TX_INTEN;
        local_irq_restore(flags);
 }
 
@@ -242,7 +242,7 @@ static void a2232_enable_tx_interrupts(void *ptr)
 
        /* Does this here really have to be? */
        local_irq_save(flags);
-       port->gs.flags |= GS_TX_INTEN;
+       port->gs.port.flags |= GS_TX_INTEN;
        local_irq_restore(flags);
 }
 
@@ -276,9 +276,9 @@ static void a2232_shutdown_port(void *ptr)
 
        local_irq_save(flags);
 
-       port->gs.flags &= ~GS_ACTIVE;
+       port->gs.port.flags &= ~GS_ACTIVE;
        
-       if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
+       if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) {
                /* Set DTR and RTS to Low, flush output.
                   The NetBSD driver "msc.c" does it this way. */
                stat->Command = (       (stat->Command & ~A2232CMD_CMask) | 
@@ -309,7 +309,7 @@ static int  a2232_set_real_termios(void *ptr)
        volatile struct a2232status *status;
        volatile struct a2232memory *mem;
 
-       if (!port->gs.tty || !port->gs.tty->termios) return 0;
+       if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0;
 
        status = a2232stat(port->which_a2232, port->which_port_on_a2232);
        mem = a2232mem(port->which_a2232);
@@ -345,7 +345,7 @@ static int  a2232_set_real_termios(void *ptr)
        }
        a2232_param |= rate;
 
-       cflag  = port->gs.tty->termios->c_cflag;
+       cflag  = port->gs.port.tty->termios->c_cflag;
 
        // get character size
        chsize = cflag & CSIZE;
@@ -382,7 +382,7 @@ static int  a2232_set_real_termios(void *ptr)
                the conventional way of inserting START/STOP characters
                by hand in throttle()/unthrottle().
        */
-       softflow = !!( port->gs.tty->termios->c_iflag & IXOFF );
+       softflow = !!( port->gs.port.tty->termios->c_iflag & IXOFF );
 
        // get Parity (Enabled/Disabled? If Enabled, Odd or Even?)
        parity = cflag & (PARENB | PARODD);
@@ -400,9 +400,9 @@ static int  a2232_set_real_termios(void *ptr)
        /*      Hmm. Maybe an own a2232_port structure
                member would be cleaner?        */
        if (cflag & CLOCAL)
-               port->gs.flags &= ~ASYNC_CHECK_CD;
+               port->gs.port.flags &= ~ASYNC_CHECK_CD;
        else
-               port->gs.flags |= ASYNC_CHECK_CD;
+               port->gs.port.flags |= ASYNC_CHECK_CD;
 
 
        /* Now we have all parameters and can go to set them: */
@@ -482,18 +482,18 @@ static int  a2232_open(struct tty_struct * tty, struct file * filp)
        port = &a2232_ports[line];
        
        tty->driver_data = port;
-       port->gs.tty = tty;
-       port->gs.count++;
+       port->gs.port.tty = tty;
+       port->gs.port.count++;
        retval = gs_init_port(&port->gs);
        if (retval) {
-               port->gs.count--;
+               port->gs.port.count--;
                return retval;
        }
-       port->gs.flags |= GS_ACTIVE;
+       port->gs.port.flags |= GS_ACTIVE;
        retval = gs_block_til_ready(port, filp);
 
        if (retval) {
-               port->gs.count--;
+               port->gs.port.count--;
                return retval;
        }
 
@@ -522,7 +522,7 @@ int ch, err, n, p;
                for (p = 0; p < NUMLINES; p++){ /* for every port on this board */
                        err = 0;
                        port = &a2232_ports[n*NUMLINES+p];
-                       if ( port->gs.flags & GS_ACTIVE ){ /* if the port is used */
+                       if ( port->gs.port.flags & GS_ACTIVE ){ /* if the port is used */
 
                                status = a2232stat(n,p);
 
@@ -577,8 +577,8 @@ int ch, err, n, p;
                                obuf = mem->OutBuf[p];
                                bufpos = status->OutHead;
                                while ( (port->gs.xmit_cnt > 0)         &&
-                                       (!port->gs.tty->stopped)        &&
-                                       (!port->gs.tty->hw_stopped) ){  /* While there are chars to transmit */
+                                       (!port->gs.port.tty->stopped)   &&
+                                       (!port->gs.port.tty->hw_stopped) ){     /* While there are chars to transmit */
                                        if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */
                                                ch = port->gs.xmit_buf[port->gs.xmit_tail];                                     /* get the next char to transmit */
                                                port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */
@@ -592,8 +592,8 @@ int ch, err, n, p;
                                status->OutHead = bufpos;
                                        
                                /* WakeUp if output buffer runs low */
-                               if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
-                                       tty_wakeup(port->gs.tty);
+                               if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) {
+                                       tty_wakeup(port->gs.port.tty);
                                }
                        } // if the port is used
                } // for every port on the board
@@ -613,16 +613,16 @@ int ch, err, n, p;
                                                struct a2232_port *port = &a2232_ports[n*7+p];
                                                port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */
 
-                                               if (!(port->gs.flags & ASYNC_CHECK_CD))
+                                               if (!(port->gs.port.flags & ASYNC_CHECK_CD))
                                                        ;       /* Don't report DCD changes */
                                                else if (port->cd_status) { // if DCD on: DCD went UP!
                                                        
                                                        /* Are we blocking in open?*/
-                                                       wake_up_interruptible(&port->gs.open_wait);
+                                                       wake_up_interruptible(&port->gs.port.open_wait);
                                                }
                                                else { // if DCD off: DCD went DOWN!
-                                                       if (port->gs.tty)
-                                                               tty_hangup (port->gs.tty);
+                                                       if (port->gs.port.tty)
+                                                               tty_hangup (port->gs.port.tty);
                                                }
                                                
                                        } // if CD changed for this port
@@ -655,8 +655,8 @@ static void a2232_init_portstructs(void)
 #ifdef NEW_WRITE_LOCKING
                mutex_init(&(port->gs.port_write_mutex));
 #endif
-               init_waitqueue_head(&port->gs.open_wait);
-               init_waitqueue_head(&port->gs.close_wait);
+               init_waitqueue_head(&port->gs.port.open_wait);
+               init_waitqueue_head(&port->gs.port.close_wait);
        }
 }
 
index 69c5afe97f19ef6e6fc932ceabcfc42420ff6d32..1718b3c481dbd944eb343b140801e930dd5cc355 100644 (file)
@@ -183,8 +183,8 @@ static void scc_init_portstructs(void)
 #ifdef NEW_WRITE_LOCKING
                port->gs.port_write_mutex = MUTEX;
 #endif
-               init_waitqueue_head(&port->gs.open_wait);
-               init_waitqueue_head(&port->gs.close_wait);
+               init_waitqueue_head(&port->gs.port.open_wait);
+               init_waitqueue_head(&port->gs.port.close_wait);
        }
 }
 
@@ -422,7 +422,7 @@ static irqreturn_t scc_rx_int(int irq, void *data)
 {
        unsigned char   ch;
        struct scc_port *port = data;
-       struct tty_struct *tty = port->gs.tty;
+       struct tty_struct *tty = port->gs.port.tty;
        SCC_ACCESS_INIT(port);
 
        ch = SCCread_NB(RX_DATA_REG);
@@ -453,7 +453,7 @@ static irqreturn_t scc_rx_int(int irq, void *data)
 static irqreturn_t scc_spcond_int(int irq, void *data)
 {
        struct scc_port *port = data;
-       struct tty_struct *tty = port->gs.tty;
+       struct tty_struct *tty = port->gs.port.tty;
        unsigned char   stat, ch, err;
        int             int_pending_mask = port->channel == CHANNEL_A ?
                                           IPR_A_RX : IPR_B_RX;
@@ -500,7 +500,7 @@ static irqreturn_t scc_tx_int(int irq, void *data)
        struct scc_port *port = data;
        SCC_ACCESS_INIT(port);
 
-       if (!port->gs.tty) {
+       if (!port->gs.port.tty) {
                printk(KERN_WARNING "scc_tx_int with NULL tty!\n");
                SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
                SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);
@@ -512,8 +512,9 @@ static irqreturn_t scc_tx_int(int irq, void *data)
                        SCCwrite(TX_DATA_REG, port->x_char);
                        port->x_char = 0;
                }
-               else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||
-                               port->gs.tty->hw_stopped)
+               else if ((port->gs.xmit_cnt <= 0) ||
+                        port->gs.port.tty->stopped ||
+                        port->gs.port.tty->hw_stopped)
                        break;
                else {
                        SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]);
@@ -522,15 +523,15 @@ static irqreturn_t scc_tx_int(int irq, void *data)
                                break;
                }
        }
-       if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||
-                       port->gs.tty->hw_stopped) {
+       if ((port->gs.xmit_cnt <= 0) || port->gs.port.tty->stopped ||
+           port->gs.port.tty->hw_stopped) {
                /* disable tx interrupts */
                SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
                SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);   /* disable tx_int on next tx underrun? */
-               port->gs.flags &= ~GS_TX_INTEN;
+               port->gs.port.flags &= ~GS_TX_INTEN;
        }
-       if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)
-               tty_wakeup(port->gs.tty);
+       if (port->gs.port.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)
+               tty_wakeup(port->gs.port.tty);
 
        SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
        return IRQ_HANDLED;
@@ -550,14 +551,14 @@ static irqreturn_t scc_stat_int(int irq, void *data)
 
        if (changed & SR_DCD) {
                port->c_dcd = !!(sr & SR_DCD);
-               if (!(port->gs.flags & ASYNC_CHECK_CD))
+               if (!(port->gs.port.flags & ASYNC_CHECK_CD))
                        ;       /* Don't report DCD changes */
                else if (port->c_dcd) {
-                       wake_up_interruptible(&port->gs.open_wait);
+                       wake_up_interruptible(&port->gs.port.open_wait);
                }
                else {
-                       if (port->gs.tty)
-                               tty_hangup (port->gs.tty);
+                       if (port->gs.port.tty)
+                               tty_hangup (port->gs.port.tty);
                }
        }
        SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET);
@@ -578,7 +579,7 @@ static void scc_disable_tx_interrupts(void *ptr)
 
        local_irq_save(flags);
        SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
-       port->gs.flags &= ~GS_TX_INTEN;
+       port->gs.port.flags &= ~GS_TX_INTEN;
        local_irq_restore(flags);
 }
 
@@ -636,8 +637,8 @@ static void scc_shutdown_port(void *ptr)
 {
        struct scc_port *port = ptr;
 
-       port->gs.flags &= ~ GS_ACTIVE;
-       if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
+       port->gs.port.flags &= ~ GS_ACTIVE;
+       if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) {
                scc_setsignals (port, 0, 0);
        }
 }
@@ -652,14 +653,14 @@ static int scc_set_real_termios (void *ptr)
        struct scc_port *port = ptr;
        SCC_ACCESS_INIT(port);
 
-       if (!port->gs.tty || !port->gs.tty->termios) return 0;
+       if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0;
 
        channel = port->channel;
 
        if (channel == CHANNEL_A)
                return 0;               /* Settings controlled by boot PROM */
 
-       cflag  = port->gs.tty->termios->c_cflag;
+       cflag  = port->gs.port.tty->termios->c_cflag;
        baud = port->gs.baud;
        chsize = (cflag & CSIZE) >> 4;
 
@@ -678,9 +679,9 @@ static int scc_set_real_termios (void *ptr)
        }
 
        if (cflag & CLOCAL)
-               port->gs.flags &= ~ASYNC_CHECK_CD;
+               port->gs.port.flags &= ~ASYNC_CHECK_CD;
        else
-               port->gs.flags |= ASYNC_CHECK_CD;
+               port->gs.port.flags |= ASYNC_CHECK_CD;
 
 #ifdef CONFIG_MVME147_SCC
        if (MACH_IS_MVME147)
@@ -856,7 +857,7 @@ static int scc_open (struct tty_struct * tty, struct file * filp)
                { COMMAND_REG, CR_EXTSTAT_RESET },
        };
 #endif
-       if (!(port->gs.flags & ASYNC_INITIALIZED)) {
+       if (!(port->gs.port.flags & ASYNC_INITIALIZED)) {
                local_irq_save(flags);
 #if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC)
                if (MACH_IS_MVME147 || MACH_IS_MVME16x) {
@@ -880,18 +881,18 @@ static int scc_open (struct tty_struct * tty, struct file * filp)
        }
 
        tty->driver_data = port;
-       port->gs.tty = tty;
-       port->gs.count++;
+       port->gs.port.tty = tty;
+       port->gs.port.count++;
        retval = gs_init_port(&port->gs);
        if (retval) {
-               port->gs.count--;
+               port->gs.port.count--;
                return retval;
        }
-       port->gs.flags |= GS_ACTIVE;
+       port->gs.port.flags |= GS_ACTIVE;
        retval = gs_block_til_ready(port, filp);
 
        if (retval) {
-               port->gs.count--;
+               port->gs.port.count--;
                return retval;
        }
 
index b11943dadefd7a23d85d29f5908451859e397497..681c15f420834a545292b17974e523d387d2f243 100644 (file)
@@ -99,6 +99,9 @@ struct talitos_private {
        /* next channel to be assigned next incoming descriptor */
        atomic_t last_chan;
 
+       /* per-channel number of requests pending in channel h/w fifo */
+       atomic_t *submit_count;
+
        /* per-channel request fifo */
        struct talitos_request **fifo;
 
@@ -263,15 +266,15 @@ static int talitos_submit(struct device *dev, struct talitos_desc *desc,
 
        spin_lock_irqsave(&priv->head_lock[ch], flags);
 
-       head = priv->head[ch];
-       request = &priv->fifo[ch][head];
-
-       if (request->desc) {
-               /* request queue is full */
+       if (!atomic_inc_not_zero(&priv->submit_count[ch])) {
+               /* h/w fifo is full */
                spin_unlock_irqrestore(&priv->head_lock[ch], flags);
                return -EAGAIN;
        }
 
+       head = priv->head[ch];
+       request = &priv->fifo[ch][head];
+
        /* map descriptor and save caller data */
        request->dma_desc = dma_map_single(dev, desc, sizeof(*desc),
                                           DMA_BIDIRECTIONAL);
@@ -335,6 +338,9 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
                priv->tail[ch] = (tail + 1) & (priv->fifo_len - 1);
 
                spin_unlock_irqrestore(&priv->tail_lock[ch], flags);
+
+               atomic_dec(&priv->submit_count[ch]);
+
                saved_req.callback(dev, saved_req.desc, saved_req.context,
                                   status);
                /* channel may resume processing in single desc error case */
@@ -842,7 +848,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
 
        /* adjust (decrease) last one (or two) entry's len to cryptlen */
        link_tbl_ptr--;
-       while (link_tbl_ptr->len <= (-cryptlen)) {
+       while (be16_to_cpu(link_tbl_ptr->len) <= (-cryptlen)) {
                /* Empty this entry, and move to previous one */
                cryptlen += be16_to_cpu(link_tbl_ptr->len);
                link_tbl_ptr->len = 0;
@@ -874,7 +880,7 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        unsigned int cryptlen = areq->cryptlen;
        unsigned int authsize = ctx->authsize;
        unsigned int ivsize;
-       int sg_count;
+       int sg_count, ret;
 
        /* hmac key */
        map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
@@ -978,7 +984,12 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
        map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0,
                               DMA_FROM_DEVICE);
 
-       return talitos_submit(dev, desc, callback, areq);
+       ret = talitos_submit(dev, desc, callback, areq);
+       if (ret != -EINPROGRESS) {
+               ipsec_esp_unmap(dev, edesc, areq);
+               kfree(edesc);
+       }
+       return ret;
 }
 
 
@@ -1009,6 +1020,8 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
        struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
        struct ipsec_esp_edesc *edesc;
        int src_nents, dst_nents, alloc_len, dma_len;
+       gfp_t flags = areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
+                     GFP_ATOMIC;
 
        if (areq->cryptlen + ctx->authsize > TALITOS_MAX_DATA_LEN) {
                dev_err(ctx->dev, "cryptlen exceeds h/w max limit\n");
@@ -1022,7 +1035,7 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
                dst_nents = src_nents;
        } else {
                dst_nents = sg_count(areq->dst, areq->cryptlen + ctx->authsize);
-               dst_nents = (dst_nents == 1) ? 0 : src_nents;
+               dst_nents = (dst_nents == 1) ? 0 : dst_nents;
        }
 
        /*
@@ -1040,7 +1053,7 @@ static struct ipsec_esp_edesc *ipsec_esp_edesc_alloc(struct aead_request *areq,
                alloc_len += icv_stashing ? ctx->authsize : 0;
        }
 
-       edesc = kmalloc(alloc_len, GFP_DMA);
+       edesc = kmalloc(alloc_len, GFP_DMA | flags);
        if (!edesc) {
                dev_err(ctx->dev, "could not allocate edescriptor\n");
                return ERR_PTR(-ENOMEM);
@@ -1337,6 +1350,7 @@ static int __devexit talitos_remove(struct of_device *ofdev)
        if (hw_supports(dev, DESC_HDR_SEL0_RNG))
                talitos_unregister_rng(dev);
 
+       kfree(priv->submit_count);
        kfree(priv->tail);
        kfree(priv->head);
 
@@ -1466,9 +1480,6 @@ static int talitos_probe(struct of_device *ofdev,
                goto err_out;
        }
 
-       of_node_put(np);
-       np = NULL;
-
        priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
                                  GFP_KERNEL);
        priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
@@ -1504,6 +1515,16 @@ static int talitos_probe(struct of_device *ofdev,
                }
        }
 
+       priv->submit_count = kmalloc(sizeof(atomic_t) * priv->num_channels,
+                                    GFP_KERNEL);
+       if (!priv->submit_count) {
+               dev_err(dev, "failed to allocate fifo submit count space\n");
+               err = -ENOMEM;
+               goto err_out;
+       }
+       for (i = 0; i < priv->num_channels; i++)
+               atomic_set(&priv->submit_count[i], -priv->chfifo_len);
+
        priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
        priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
        if (!priv->head || !priv->tail) {
@@ -1559,8 +1580,6 @@ static int talitos_probe(struct of_device *ofdev,
 
 err_out:
        talitos_remove(ofdev);
-       if (np)
-               of_node_put(np);
 
        return err;
 }
index 76f26710fc16393bbecb80310924789798d2f718..fa6d6abefd4d14d9bdb8c372118a1ddbcea7b2fb 100644 (file)
@@ -16,8 +16,13 @@ config FIREWIRE
          enable the new stack.
 
          To compile this driver as a module, say M here: the module will be
-         called firewire-core.  It functionally replaces ieee1394, raw1394,
-         and video1394.
+         called firewire-core.
+
+         This module functionally replaces ieee1394, raw1394, and video1394.
+         To access it from application programs, you generally need at least
+         libraw1394 version 2.  IIDC/DCAM applications also need libdc1394
+         version 2.  No libraries are required to access storage devices
+         through the firewire-sbp2 driver.
 
 config FIREWIRE_OHCI
        tristate "OHCI-1394 controllers"
index da873d795aad94bccf9e88a58a4b9bde9821daf1..bbd73a406e537106a2eeda4ba4a2ba1a543b030d 100644 (file)
@@ -539,7 +539,7 @@ fw_core_remove_card(struct fw_card *card)
        wait_for_completion(&card->done);
 
        cancel_delayed_work_sync(&card->work);
-       fw_flush_transactions(card);
+       WARN_ON(!list_empty(&card->transaction_list));
        del_timer_sync(&card->flush_timer);
 }
 EXPORT_SYMBOL(fw_core_remove_card);
index c639915fc3cbc8637c39d93927488b1350001cc6..bc81d6fcd2fd2bbc043f31d8707aaaaf3d34a01a 100644 (file)
@@ -382,9 +382,9 @@ complete_transaction(struct fw_card *card, int rcode,
 
        response->response.type   = FW_CDEV_EVENT_RESPONSE;
        response->response.rcode  = rcode;
-       queue_event(client, &response->event,
-                   &response->response, sizeof(response->response),
-                   response->response.data, response->response.length);
+       queue_event(client, &response->event, &response->response,
+                   sizeof(response->response) + response->response.length,
+                   NULL, 0);
 }
 
 static int ioctl_send_request(struct client *client, void *buffer)
index 566672e0bcffbb6902d5174899b7b1933213847a..251416f2148f36c72cdd8afa0ac74a792c7db5fa 100644 (file)
@@ -171,7 +171,6 @@ struct iso_context {
 struct fw_ohci {
        struct fw_card card;
 
-       u32 version;
        __iomem char *registers;
        dma_addr_t self_id_bus;
        __le32 *self_id_cpu;
@@ -180,6 +179,8 @@ struct fw_ohci {
        int generation;
        int request_generation; /* for timestamping incoming requests */
        u32 bus_seconds;
+
+       bool use_dualbuffer;
        bool old_uninorth;
        bool bus_reset_packet_quirk;
 
@@ -1885,7 +1886,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
        } else {
                mask = &ohci->ir_context_mask;
                list = ohci->ir_context_list;
-               if (ohci->version >= OHCI_VERSION_1_1)
+               if (ohci->use_dualbuffer)
                        callback = handle_ir_dualbuffer_packet;
                else
                        callback = handle_ir_packet_per_buffer;
@@ -1949,7 +1950,7 @@ static int ohci_start_iso(struct fw_iso_context *base,
        } else {
                index = ctx - ohci->ir_context_list;
                control = IR_CONTEXT_ISOCH_HEADER;
-               if (ohci->version >= OHCI_VERSION_1_1)
+               if (ohci->use_dualbuffer)
                        control |= IR_CONTEXT_DUAL_BUFFER_MODE;
                match = (tags << 28) | (sync << 8) | ctx->base.channel;
                if (cycle >= 0) {
@@ -2279,7 +2280,7 @@ ohci_queue_iso(struct fw_iso_context *base,
        spin_lock_irqsave(&ctx->context.ohci->lock, flags);
        if (base->type == FW_ISO_CONTEXT_TRANSMIT)
                retval = ohci_queue_iso_transmit(base, packet, buffer, payload);
-       else if (ctx->context.ohci->version >= OHCI_VERSION_1_1)
+       else if (ctx->context.ohci->use_dualbuffer)
                retval = ohci_queue_iso_receive_dualbuffer(base, packet,
                                                         buffer, payload);
        else
@@ -2341,7 +2342,7 @@ static int __devinit
 pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
 {
        struct fw_ohci *ohci;
-       u32 bus_options, max_receive, link_speed;
+       u32 bus_options, max_receive, link_speed, version;
        u64 guid;
        int err;
        size_t size;
@@ -2366,12 +2367,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
        pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
        pci_set_drvdata(dev, ohci);
 
-#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
-       ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE &&
-                            dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW;
-#endif
-       ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI;
-
        spin_lock_init(&ohci->lock);
 
        tasklet_init(&ohci->bus_reset_tasklet,
@@ -2390,6 +2385,23 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
                goto fail_iomem;
        }
 
+       version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
+       ohci->use_dualbuffer = version >= OHCI_VERSION_1_1;
+
+/* x86-32 currently doesn't use highmem for dma_alloc_coherent */
+#if !defined(CONFIG_X86_32)
+       /* dual-buffer mode is broken with descriptor addresses above 2G */
+       if (dev->vendor == PCI_VENDOR_ID_TI &&
+           dev->device == PCI_DEVICE_ID_TI_TSB43AB22)
+               ohci->use_dualbuffer = false;
+#endif
+
+#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
+       ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE &&
+                            dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW;
+#endif
+       ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI;
+
        ar_context_init(&ohci->ar_request_ctx, ohci,
                        OHCI1394_AsReqRcvContextControlSet);
 
@@ -2441,9 +2453,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
        if (err < 0)
                goto fail_self_id;
 
-       ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
        fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n",
-                 dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff);
+                 dev->dev.bus_id, version >> 16, version & 0xff);
        return 0;
 
  fail_self_id:
index 213b0ff8f3d6dd55d5edf5af91efdc21a50b638b..c1b81077c4a82b6e15bc528b314dfa87a73e1c35 100644 (file)
@@ -510,8 +510,6 @@ fw_core_handle_bus_reset(struct fw_card *card,
        struct fw_node *local_node;
        unsigned long flags;
 
-       fw_flush_transactions(card);
-
        spin_lock_irqsave(&card->lock, flags);
 
        /*
index 40db8075227259c849007a92b4ea9c045745d9b2..e5d1a0b64fcf1b714a186d31a8b7aabf90e7aa97 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/kref.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
@@ -151,7 +152,7 @@ transmit_complete_callback(struct fw_packet *packet,
 
 static void
 fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
-               int node_id, int source_id, int generation, int speed,
+               int destination_id, int source_id, int generation, int speed,
                unsigned long long offset, void *payload, size_t length)
 {
        int ext_tcode;
@@ -166,7 +167,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
                HEADER_RETRY(RETRY_X) |
                HEADER_TLABEL(tlabel) |
                HEADER_TCODE(tcode) |
-               HEADER_DESTINATION(node_id);
+               HEADER_DESTINATION(destination_id);
        packet->header[1] =
                HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id);
        packet->header[2] =
@@ -252,7 +253,7 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
                fw_transaction_callback_t callback, void *callback_data)
 {
        unsigned long flags;
-       int tlabel, source;
+       int tlabel;
 
        /*
         * Bump the flush timer up 100ms first of all so we
@@ -268,7 +269,6 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
 
        spin_lock_irqsave(&card->lock, flags);
 
-       source = card->node_id;
        tlabel = card->current_tlabel;
        if (card->tlabel_mask & (1 << tlabel)) {
                spin_unlock_irqrestore(&card->lock, flags);
@@ -279,77 +279,58 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
        card->current_tlabel = (card->current_tlabel + 1) & 0x1f;
        card->tlabel_mask |= (1 << tlabel);
 
-       list_add_tail(&t->link, &card->transaction_list);
-
-       spin_unlock_irqrestore(&card->lock, flags);
-
-       /* Initialize rest of transaction, fill out packet and send it. */
        t->node_id = node_id;
        t->tlabel = tlabel;
        t->callback = callback;
        t->callback_data = callback_data;
 
-       fw_fill_request(&t->packet, tcode, t->tlabel,
-                       node_id, source, generation,
-                       speed, offset, payload, length);
+       fw_fill_request(&t->packet, tcode, t->tlabel, node_id, card->node_id,
+                       generation, speed, offset, payload, length);
        t->packet.callback = transmit_complete_callback;
 
+       list_add_tail(&t->link, &card->transaction_list);
+
+       spin_unlock_irqrestore(&card->lock, flags);
+
        card->driver->send_request(card, &t->packet);
 }
 EXPORT_SYMBOL(fw_send_request);
 
-struct fw_phy_packet {
-       struct fw_packet packet;
-       struct completion done;
-       struct kref kref;
-};
-
-static void phy_packet_release(struct kref *kref)
-{
-       struct fw_phy_packet *p =
-                       container_of(kref, struct fw_phy_packet, kref);
-       kfree(p);
-}
+static DEFINE_MUTEX(phy_config_mutex);
+static DECLARE_COMPLETION(phy_config_done);
 
 static void transmit_phy_packet_callback(struct fw_packet *packet,
                                         struct fw_card *card, int status)
 {
-       struct fw_phy_packet *p =
-                       container_of(packet, struct fw_phy_packet, packet);
-
-       complete(&p->done);
-       kref_put(&p->kref, phy_packet_release);
+       complete(&phy_config_done);
 }
 
+static struct fw_packet phy_config_packet = {
+       .header_length  = 8,
+       .payload_length = 0,
+       .speed          = SCODE_100,
+       .callback       = transmit_phy_packet_callback,
+};
+
 void fw_send_phy_config(struct fw_card *card,
                        int node_id, int generation, int gap_count)
 {
-       struct fw_phy_packet *p;
        long timeout = DIV_ROUND_UP(HZ, 10);
        u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) |
                   PHY_CONFIG_ROOT_ID(node_id) |
                   PHY_CONFIG_GAP_COUNT(gap_count);
 
-       p = kmalloc(sizeof(*p), GFP_KERNEL);
-       if (p == NULL)
-               return;
+       mutex_lock(&phy_config_mutex);
+
+       phy_config_packet.header[0] = data;
+       phy_config_packet.header[1] = ~data;
+       phy_config_packet.generation = generation;
+       INIT_COMPLETION(phy_config_done);
+
+       card->driver->send_request(card, &phy_config_packet);
+       wait_for_completion_timeout(&phy_config_done, timeout);
 
-       p->packet.header[0] = data;
-       p->packet.header[1] = ~data;
-       p->packet.header_length = 8;
-       p->packet.payload_length = 0;
-       p->packet.speed = SCODE_100;
-       p->packet.generation = generation;
-       p->packet.callback = transmit_phy_packet_callback;
-       init_completion(&p->done);
-       kref_set(&p->kref, 2);
-
-       card->driver->send_request(card, &p->packet);
-       timeout = wait_for_completion_timeout(&p->done, timeout);
-       kref_put(&p->kref, phy_packet_release);
-
-       /* will leak p if the callback is never executed */
-       WARN_ON(timeout == 0);
+       mutex_unlock(&phy_config_mutex);
 }
 
 void fw_flush_transactions(struct fw_card *card)
index e23399c7f7735ad8677d7f011821cc5d3fa56717..001622eb86f962d28f6b6842981817ddf7f14132 100644 (file)
@@ -153,12 +153,14 @@ int __init firmware_map_add_early(resource_size_t start, resource_size_t end,
 
 static ssize_t start_show(struct firmware_map_entry *entry, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->start);
+       return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+               (unsigned long long)entry->start);
 }
 
 static ssize_t end_show(struct firmware_map_entry *entry, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->end);
+       return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+               (unsigned long long)entry->end);
 }
 
 static ssize_t type_show(struct firmware_map_entry *entry, char *buf)
index 9494005d1c9a62fe1463c94320af061f6ee1e40e..e603736682bfc6e52fbc6f4802375bd329aa7850 100644 (file)
@@ -43,7 +43,6 @@
 #include <linux/cdev.h>
 #include <linux/idr.h>
 #include <linux/mutex.h>
-#include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 
@@ -1154,11 +1153,18 @@ static unsigned int ib_ucm_poll(struct file *filp,
        return mask;
 }
 
+/*
+ * ib_ucm_open() does not need the BKL:
+ *
+ *  - no global state is referred to;
+ *  - there is no ioctl method to race against;
+ *  - no further module initialization is required for open to work
+ *    after the device is registered.
+ */
 static int ib_ucm_open(struct inode *inode, struct file *filp)
 {
        struct ib_ucm_file *file;
 
-       cycle_kernel_lock();
        file = kmalloc(sizeof(*file), GFP_KERNEL);
        if (!file)
                return -ENOMEM;
index 195f97302fe5243575d42abe3c990bdb4fa7d106..b41dd26bbfa1e1b83654c487d1ff16482bf3746d 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
 
 #include <rdma/rdma_user_cm.h>
 #include <rdma/ib_marshall.h>
@@ -1149,6 +1148,14 @@ static unsigned int ucma_poll(struct file *filp, struct poll_table_struct *wait)
        return mask;
 }
 
+/*
+ * ucma_open() does not need the BKL:
+ *
+ *  - no global state is referred to;
+ *  - there is no ioctl method to race against;
+ *  - no further module initialization is required for open to work
+ *    after the device is registered.
+ */
 static int ucma_open(struct inode *inode, struct file *filp)
 {
        struct ucma_file *file;
@@ -1157,7 +1164,6 @@ static int ucma_open(struct inode *inode, struct file *filp)
        if (!file)
                return -ENOMEM;
 
-       lock_kernel();
        INIT_LIST_HEAD(&file->event_list);
        INIT_LIST_HEAD(&file->ctx_list);
        init_waitqueue_head(&file->poll_wait);
@@ -1165,7 +1171,6 @@ static int ucma_open(struct inode *inode, struct file *filp)
 
        filp->private_data = file;
        file->filp = filp;
-       unlock_kernel();
        return 0;
 }
 
index 0b191a4842ce545f02f2984724d121a950d85945..a1464574bfdde532d8f6e19c6818fdc68d4f0876 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 38d6907ab521c97ea0fdcdba2039819d9d51d8b2..a3c2851c0545dcaf44eab62e9e7e4ed0bf4674e4 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index d26a91317d4da21dca7e5be57b7c45bc1a7f87d8..6e2b0dc21b614dd2b89171c1de04bc6cb133de90 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index db2086faa4edd9680e872ce8a4d74200e1fe2b71..a4cdb465cd1d6c2b49ada47998fd0f59d5624a30 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 02a99bc4442e6420058b21e79d6678beed56ff47..f7bc7dd8578a558704d32846cc2f24b99dfe0304 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 12d6bc6f8007da0ced4158b1a956c7da52bf56c0..d42565258fb7f0c2e0dc8053e62855a1887b87e4 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index e2d11be4525c805a5d643f5335a17cdf273a7e67..13beedeeef9f7aebfb805b4d405490614c019e76 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index d2884e7780987c2a9f5ee9ad409086100127ba25..b0cab64e5e3db0b45e6837a98a9bcfef6fe8e4c4 100644 (file)
@@ -276,6 +276,7 @@ static void nes_cqp_rem_ref_callback(struct nes_device *nesdev, struct nes_cqp_r
        }
        nes_free_resource(nesadapter, nesadapter->allocated_qps, nesqp->hwqp.qp_id);
 
+       nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = NULL;
        kfree(nesqp->allocated_buffer);
 
 }
@@ -289,7 +290,6 @@ void nes_rem_ref(struct ib_qp *ibqp)
        struct nes_qp *nesqp;
        struct nes_vnic *nesvnic = to_nesvnic(ibqp->device);
        struct nes_device *nesdev = nesvnic->nesdev;
-       struct nes_adapter *nesadapter = nesdev->nesadapter;
        struct nes_hw_cqp_wqe *cqp_wqe;
        struct nes_cqp_request *cqp_request;
        u32 opcode;
@@ -303,8 +303,6 @@ void nes_rem_ref(struct ib_qp *ibqp)
        }
 
        if (atomic_dec_and_test(&nesqp->refcount)) {
-               nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = NULL;
-
                /* Destroy the QP */
                cqp_request = nes_get_cqp_request(nesdev);
                if (cqp_request == NULL) {
index 6aa531d5276dfd969114d07ebcc4c871e611e79d..9f0b964b2c9932af7567dcae6efc7d98c6c01728 100644 (file)
@@ -74,36 +74,59 @@ atomic_t cm_nodes_destroyed;
 atomic_t cm_accel_dropped_pkts;
 atomic_t cm_resets_recvd;
 
-static inline int mini_cm_accelerated(struct nes_cm_core *, struct nes_cm_node *);
+static inline int mini_cm_accelerated(struct nes_cm_core *,
+       struct nes_cm_node *);
 static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *,
-               struct nes_vnic *, struct nes_cm_info *);
-static int add_ref_cm_node(struct nes_cm_node *);
-static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *);
+       struct nes_vnic *, struct nes_cm_info *);
 static int mini_cm_del_listen(struct nes_cm_core *, struct nes_cm_listener *);
-static struct sk_buff *form_cm_frame(struct sk_buff *, struct nes_cm_node *,
-                                    void *, u32, void *, u32, u8);
-static struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node);
-
 static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *,
-                                          struct nes_vnic *,
-                                          struct ietf_mpa_frame *,
-                                          struct nes_cm_info *);
+       struct nes_vnic *, u16, void *, struct nes_cm_info *);
+static int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *);
 static int mini_cm_accept(struct nes_cm_core *, struct ietf_mpa_frame *,
-                         struct nes_cm_node *);
+       struct nes_cm_node *);
 static int mini_cm_reject(struct nes_cm_core *, struct ietf_mpa_frame *,
-                         struct nes_cm_node *);
-static int mini_cm_close(struct nes_cm_core *, struct nes_cm_node *);
-static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *,
-                           struct sk_buff *);
+       struct nes_cm_node *);
+static void mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *,
+       struct sk_buff *);
 static int mini_cm_dealloc_core(struct nes_cm_core *);
 static int mini_cm_get(struct nes_cm_core *);
 static int mini_cm_set(struct nes_cm_core *, u32, u32);
+
+static struct sk_buff *form_cm_frame(struct sk_buff *, struct nes_cm_node *,
+       void *, u32, void *, u32, u8);
+static struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node);
+static int add_ref_cm_node(struct nes_cm_node *);
+static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *);
+
 static int nes_cm_disconn_true(struct nes_qp *);
 static int nes_cm_post_event(struct nes_cm_event *event);
 static int nes_disconnect(struct nes_qp *nesqp, int abrupt);
 static void nes_disconnect_worker(struct work_struct *work);
-static int send_ack(struct nes_cm_node *cm_node);
+
+static int send_mpa_request(struct nes_cm_node *, struct sk_buff *);
+static int send_syn(struct nes_cm_node *, u32, struct sk_buff *);
+static int send_reset(struct nes_cm_node *, struct sk_buff *);
+static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb);
 static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb);
+static void process_packet(struct nes_cm_node *, struct sk_buff *,
+       struct nes_cm_core *);
+
+static void active_open_err(struct nes_cm_node *, struct sk_buff *, int);
+static void passive_open_err(struct nes_cm_node *, struct sk_buff *, int);
+static void cleanup_retrans_entry(struct nes_cm_node *);
+static void handle_rcv_mpa(struct nes_cm_node *, struct sk_buff *,
+       enum nes_cm_event_type);
+static void free_retrans_entry(struct nes_cm_node *cm_node);
+static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
+       struct sk_buff *skb, int optionsize, int passive);
+
+/* CM event handler functions */
+static void cm_event_connected(struct nes_cm_event *);
+static void cm_event_connect_error(struct nes_cm_event *);
+static void cm_event_reset(struct nes_cm_event *);
+static void cm_event_mpa_req(struct nes_cm_event *);
+
+static void print_core(struct nes_cm_core *core);
 
 /* External CM API Interface */
 /* instance of function pointers for client API */
@@ -158,11 +181,11 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
        event->cm_info.loc_port = cm_node->loc_port;
        event->cm_info.cm_id = cm_node->cm_id;
 
-       nes_debug(NES_DBG_CM, "Created event=%p, type=%u, dst_addr=%08x[%x],"
-                       " src_addr=%08x[%x]\n",
-                       event, type,
-                       event->cm_info.loc_addr, event->cm_info.loc_port,
-                       event->cm_info.rem_addr, event->cm_info.rem_port);
+       nes_debug(NES_DBG_CM, "cm_node=%p Created event=%p, type=%u, "
+               "dst_addr=%08x[%x], src_addr=%08x[%x]\n",
+               cm_node, event, type, event->cm_info.loc_addr,
+               event->cm_info.loc_port, event->cm_info.rem_addr,
+               event->cm_info.rem_port);
 
        nes_cm_post_event(event);
        return event;
@@ -172,14 +195,11 @@ static struct nes_cm_event *create_event(struct nes_cm_node *cm_node,
 /**
  * send_mpa_request
  */
-static int send_mpa_request(struct nes_cm_node *cm_node)
+static int send_mpa_request(struct nes_cm_node *cm_node, struct sk_buff *skb)
 {
-       struct sk_buff *skb;
        int ret;
-
-       skb = get_free_pkt(cm_node);
        if (!skb) {
-               nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
+               nes_debug(NES_DBG_CM, "skb set to NULL\n");
                return -1;
        }
 
@@ -188,9 +208,8 @@ static int send_mpa_request(struct nes_cm_node *cm_node)
                        cm_node->mpa_frame_size, SET_ACK);
 
        ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
-       if (ret < 0) {
+       if (ret < 0)
                return ret;
-       }
 
        return 0;
 }
@@ -228,47 +247,13 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 len)
 }
 
 
-/**
- * handle_exception_pkt - process an exception packet.
- * We have been in a TSA state, and we have now received SW
- * TCP/IP traffic should be a FIN request or IP pkt with options
- */
-static int handle_exception_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb)
-{
-       int ret = 0;
-       struct tcphdr *tcph = tcp_hdr(skb);
-
-       /* first check to see if this a FIN pkt */
-       if (tcph->fin) {
-               /* we need to ACK the FIN request */
-               send_ack(cm_node);
-
-               /* check which side we are (client/server) and set next state accordingly */
-               if (cm_node->tcp_cntxt.client)
-                       cm_node->state = NES_CM_STATE_CLOSING;
-               else {
-                       /* we are the server side */
-                       cm_node->state = NES_CM_STATE_CLOSE_WAIT;
-                       /* since this is a self contained CM we don't wait for */
-                       /* an APP to close us, just send final FIN immediately */
-                       ret = send_fin(cm_node, NULL);
-                       cm_node->state = NES_CM_STATE_LAST_ACK;
-               }
-       } else {
-               ret = -EINVAL;
-       }
-
-       return ret;
-}
-
-
 /**
  * form_cm_frame - get a free packet and build empty frame Use
  * node info to build.
  */
-static struct sk_buff *form_cm_frame(struct sk_buff *skb, struct nes_cm_node *cm_node,
-                                    void *options, u32 optionsize, void *data,
-                                    u32 datasize, u8 flags)
+static struct sk_buff *form_cm_frame(struct sk_buff *skb,
+       struct nes_cm_node *cm_node, void *options, u32 optionsize,
+       void *data, u32 datasize, u8 flags)
 {
        struct tcphdr *tcph;
        struct iphdr *iph;
@@ -332,10 +317,12 @@ static struct sk_buff *form_cm_frame(struct sk_buff *skb, struct nes_cm_node *cm
                cm_node->tcp_cntxt.loc_seq_num++;
                tcph->syn = 1;
        } else
-               cm_node->tcp_cntxt.loc_seq_num += datasize;     /* data (no headers) */
+               cm_node->tcp_cntxt.loc_seq_num += datasize;
 
-       if (flags & SET_FIN)
+       if (flags & SET_FIN) {
+               cm_node->tcp_cntxt.loc_seq_num++;
                tcph->fin = 1;
+       }
 
        if (flags & SET_RST)
                tcph->rst = 1;
@@ -389,7 +376,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
                int close_when_complete)
 {
        unsigned long  flags;
-       struct nes_cm_core *cm_core;
+       struct nes_cm_core *cm_core = cm_node->cm_core;
        struct nes_timer_entry *new_send;
        int ret = 0;
        u32 was_timer_set;
@@ -411,7 +398,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
        new_send->close_when_complete = close_when_complete;
 
        if (type == NES_TIMER_TYPE_CLOSE) {
-               new_send->timetosend += (HZ/2); /* TODO: decide on the correct value here */
+               new_send->timetosend += (HZ/10);
                spin_lock_irqsave(&cm_node->recv_list_lock, flags);
                list_add_tail(&new_send->list, &cm_node->recv_list);
                spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
@@ -420,36 +407,28 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
        if (type == NES_TIMER_TYPE_SEND) {
                new_send->seq_num = ntohl(tcp_hdr(skb)->seq);
                atomic_inc(&new_send->skb->users);
+               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+               cm_node->send_entry = new_send;
+               add_ref_cm_node(cm_node);
+               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+               new_send->timetosend = jiffies + NES_RETRY_TIMEOUT;
 
                ret = nes_nic_cm_xmit(new_send->skb, cm_node->netdev);
                if (ret != NETDEV_TX_OK) {
-                       nes_debug(NES_DBG_CM, "Error sending packet %p (jiffies = %lu)\n",
-                                       new_send, jiffies);
+                       nes_debug(NES_DBG_CM, "Error sending packet %p "
+                               "(jiffies = %lu)\n", new_send, jiffies);
                        atomic_dec(&new_send->skb->users);
                        new_send->timetosend = jiffies;
                } else {
                        cm_packets_sent++;
                        if (!send_retrans) {
+                               cleanup_retrans_entry(cm_node);
                                if (close_when_complete)
-                                       rem_ref_cm_node(cm_node->cm_core, cm_node);
-                               dev_kfree_skb_any(new_send->skb);
-                               kfree(new_send);
+                                       rem_ref_cm_node(cm_core, cm_node);
                                return ret;
                        }
-                       new_send->timetosend = jiffies + NES_RETRY_TIMEOUT;
                }
-               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-               list_add_tail(&new_send->list, &cm_node->retrans_list);
-               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-       }
-       if (type == NES_TIMER_TYPE_RECV) {
-               new_send->seq_num = ntohl(tcp_hdr(skb)->seq);
-               new_send->timetosend = jiffies;
-               spin_lock_irqsave(&cm_node->recv_list_lock, flags);
-               list_add_tail(&new_send->list, &cm_node->recv_list);
-               spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
        }
-       cm_core = cm_node->cm_core;
 
        was_timer_set = timer_pending(&cm_core->tcp_timer);
 
@@ -476,23 +455,27 @@ static void nes_cm_timer_tick(unsigned long pass)
        struct list_head *list_node, *list_node_temp;
        struct nes_cm_core *cm_core = g_cm_core;
        struct nes_qp *nesqp;
-       struct sk_buff *skb;
        u32 settimer = 0;
        int ret = NETDEV_TX_OK;
-       int    node_done;
+       enum nes_cm_node_state last_state;
 
        spin_lock_irqsave(&cm_core->ht_lock, flags);
 
-       list_for_each_safe(list_node, list_core_temp, &cm_core->connected_nodes) {
+       list_for_each_safe(list_node, list_core_temp,
+               &cm_core->connected_nodes) {
                cm_node = container_of(list_node, struct nes_cm_node, list);
                add_ref_cm_node(cm_node);
                spin_unlock_irqrestore(&cm_core->ht_lock, flags);
                spin_lock_irqsave(&cm_node->recv_list_lock, flags);
-               list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) {
-                       recv_entry = container_of(list_core, struct nes_timer_entry, list);
-                       if ((time_after(recv_entry->timetosend, jiffies)) &&
-                                       (recv_entry->type == NES_TIMER_TYPE_CLOSE)) {
-                               if (nexttimeout > recv_entry->timetosend || !settimer) {
+               list_for_each_safe(list_core, list_node_temp,
+                       &cm_node->recv_list) {
+                       recv_entry = container_of(list_core,
+                               struct nes_timer_entry, list);
+                       if (!recv_entry)
+                               break;
+                       if (time_after(recv_entry->timetosend, jiffies)) {
+                               if (nexttimeout > recv_entry->timetosend ||
+                                       !settimer) {
                                        nexttimeout = recv_entry->timetosend;
                                        settimer = 1;
                                }
@@ -501,157 +484,143 @@ static void nes_cm_timer_tick(unsigned long pass)
                        list_del(&recv_entry->list);
                        cm_id = cm_node->cm_id;
                        spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
-                       if (recv_entry->type == NES_TIMER_TYPE_CLOSE) {
-                               nesqp = (struct nes_qp *)recv_entry->skb;
-                               spin_lock_irqsave(&nesqp->lock, qplockflags);
-                               if (nesqp->cm_id) {
-                                       nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, refcount = %d: "
-                                                       "****** HIT A NES_TIMER_TYPE_CLOSE"
-                                                       " with something to do!!! ******\n",
-                                                       nesqp->hwqp.qp_id, cm_id,
-                                                       atomic_read(&nesqp->refcount));
-                                       nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
-                                       nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
-                                       nesqp->ibqp_state = IB_QPS_ERR;
-                                       spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_cm_disconn(nesqp);
-                               } else {
-                                       spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, refcount = %d:"
-                                                       " ****** HIT A NES_TIMER_TYPE_CLOSE"
-                                                       " with nothing to do!!! ******\n",
-                                                       nesqp->hwqp.qp_id, cm_id,
-                                                       atomic_read(&nesqp->refcount));
-                                       nes_rem_ref(&nesqp->ibqp);
-                               }
-                               if (cm_id)
-                                       cm_id->rem_ref(cm_id);
+                       nesqp = (struct nes_qp *)recv_entry->skb;
+                       spin_lock_irqsave(&nesqp->lock, qplockflags);
+                       if (nesqp->cm_id) {
+                               nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
+                                       "refcount = %d: HIT A "
+                                       "NES_TIMER_TYPE_CLOSE with something "
+                                       "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
+                                       atomic_read(&nesqp->refcount));
+                               nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+                               nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
+                               nesqp->ibqp_state = IB_QPS_ERR;
+                               spin_unlock_irqrestore(&nesqp->lock,
+                                       qplockflags);
+                               nes_cm_disconn(nesqp);
+                       } else {
+                               spin_unlock_irqrestore(&nesqp->lock,
+                                       qplockflags);
+                               nes_debug(NES_DBG_CM, "QP%u: cm_id = %p, "
+                                       "refcount = %d: HIT A "
+                                       "NES_TIMER_TYPE_CLOSE with nothing "
+                                       "to do!!!\n", nesqp->hwqp.qp_id, cm_id,
+                                       atomic_read(&nesqp->refcount));
                        }
+                       if (cm_id)
+                               cm_id->rem_ref(cm_id);
+
                        kfree(recv_entry);
                        spin_lock_irqsave(&cm_node->recv_list_lock, flags);
                }
                spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
 
                spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-               node_done = 0;
-               list_for_each_safe(list_core, list_node_temp, &cm_node->retrans_list) {
-                       if (node_done) {
-                               break;
-                       }
-                       send_entry = container_of(list_core, struct nes_timer_entry, list);
+               do {
+                       send_entry = cm_node->send_entry;
+                       if (!send_entry)
+                               continue;
                        if (time_after(send_entry->timetosend, jiffies)) {
                                if (cm_node->state != NES_CM_STATE_TSA) {
-                                       if ((nexttimeout > send_entry->timetosend) || !settimer) {
-                                               nexttimeout = send_entry->timetosend;
+                                       if ((nexttimeout >
+                                               send_entry->timetosend) ||
+                                               !settimer) {
+                                               nexttimeout =
+                                                       send_entry->timetosend;
                                                settimer = 1;
+                                               continue;
                                        }
-                                       node_done = 1;
-                                       continue;
                                } else {
-                                       list_del(&send_entry->list);
-                                       skb = send_entry->skb;
-                                       spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-                                       dev_kfree_skb_any(skb);
-                                       kfree(send_entry);
-                                       spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+                                       free_retrans_entry(cm_node);
                                        continue;
                                }
                        }
-                       if (send_entry->type == NES_TIMER_NODE_CLEANUP) {
-                               list_del(&send_entry->list);
-                               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-                               kfree(send_entry);
-                               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-                               continue;
-                       }
-                       if ((send_entry->seq_num < cm_node->tcp_cntxt.rem_ack_num) ||
-                                       (cm_node->state == NES_CM_STATE_TSA) ||
-                                       (cm_node->state == NES_CM_STATE_CLOSED)) {
-                               skb = send_entry->skb;
-                               list_del(&send_entry->list);
-                               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-                               kfree(send_entry);
-                               dev_kfree_skb_any(skb);
-                               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+
+                       if ((cm_node->state == NES_CM_STATE_TSA) ||
+                               (cm_node->state == NES_CM_STATE_CLOSED)) {
+                               free_retrans_entry(cm_node);
                                continue;
                        }
 
-                       if (!send_entry->retranscount || !send_entry->retrycount) {
+                       if (!send_entry->retranscount ||
+                               !send_entry->retrycount) {
                                cm_packets_dropped++;
-                               skb = send_entry->skb;
-                               list_del(&send_entry->list);
-                               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-                               dev_kfree_skb_any(skb);
-                               kfree(send_entry);
-                               if (cm_node->state == NES_CM_STATE_SYN_RCVD) {
-                                       /* this node never even generated an indication up to the cm */
+                               last_state = cm_node->state;
+                               cm_node->state = NES_CM_STATE_CLOSED;
+                               free_retrans_entry(cm_node);
+                               spin_unlock_irqrestore(
+                                       &cm_node->retrans_list_lock, flags);
+                               if (last_state == NES_CM_STATE_SYN_RCVD)
                                        rem_ref_cm_node(cm_core, cm_node);
-                               } else {
-                                       cm_node->state = NES_CM_STATE_CLOSED;
-                                       create_event(cm_node, NES_CM_EVENT_ABORTED);
-                               }
-                               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+                               else
+                                       create_event(cm_node,
+                                               NES_CM_EVENT_ABORTED);
+                               spin_lock_irqsave(&cm_node->retrans_list_lock,
+                                       flags);
                                continue;
                        }
-                       /* this seems like the correct place, but leave send entry unprotected */
-                       /* spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); */
                        atomic_inc(&send_entry->skb->users);
                        cm_packets_retrans++;
-                       nes_debug(NES_DBG_CM, "Retransmitting send_entry %p for node %p,"
-                                       " jiffies = %lu, time to send =  %lu, retranscount = %u, "
-                                       "send_entry->seq_num = 0x%08X, cm_node->tcp_cntxt.rem_ack_num = 0x%08X\n",
-                                       send_entry, cm_node, jiffies, send_entry->timetosend, send_entry->retranscount,
-                                       send_entry->seq_num, cm_node->tcp_cntxt.rem_ack_num);
-
-                       spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+                       nes_debug(NES_DBG_CM, "Retransmitting send_entry %p "
+                               "for node %p, jiffies = %lu, time to send = "
+                               "%lu, retranscount = %u, send_entry->seq_num = "
+                               "0x%08X, cm_node->tcp_cntxt.rem_ack_num = "
+                               "0x%08X\n", send_entry, cm_node, jiffies,
+                               send_entry->timetosend,
+                               send_entry->retranscount,
+                               send_entry->seq_num,
+                               cm_node->tcp_cntxt.rem_ack_num);
+
+                       spin_unlock_irqrestore(&cm_node->retrans_list_lock,
+                               flags);
                        ret = nes_nic_cm_xmit(send_entry->skb, cm_node->netdev);
+                       spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
                        if (ret != NETDEV_TX_OK) {
+                               nes_debug(NES_DBG_CM, "rexmit failed for "
+                                       "node=%p\n", cm_node);
                                cm_packets_bounced++;
                                atomic_dec(&send_entry->skb->users);
                                send_entry->retrycount--;
                                nexttimeout = jiffies + NES_SHORT_TIME;
                                settimer = 1;
-                               node_done = 1;
-                               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
                                continue;
                        } else {
                                cm_packets_sent++;
                        }
-                       spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-                       list_del(&send_entry->list);
-                       nes_debug(NES_DBG_CM, "Packet Sent: retrans count = %u, retry count = %u.\n",
-                                       send_entry->retranscount, send_entry->retrycount);
+                       nes_debug(NES_DBG_CM, "Packet Sent: retrans count = "
+                               "%u, retry count = %u.\n",
+                               send_entry->retranscount,
+                               send_entry->retrycount);
                        if (send_entry->send_retrans) {
                                send_entry->retranscount--;
-                               send_entry->timetosend = jiffies + NES_RETRY_TIMEOUT;
-                               if (nexttimeout > send_entry->timetosend || !settimer) {
+                               send_entry->timetosend = jiffies +
+                                       NES_RETRY_TIMEOUT;
+                               if (nexttimeout > send_entry->timetosend ||
+                                       !settimer) {
                                        nexttimeout = send_entry->timetosend;
                                        settimer = 1;
                                }
-                               list_add(&send_entry->list, &cm_node->retrans_list);
-                               continue;
                        } else {
                                int close_when_complete;
-                               skb = send_entry->skb;
-                               close_when_complete = send_entry->close_when_complete;
-                               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-                               if (close_when_complete) {
-                                       BUG_ON(atomic_read(&cm_node->ref_count) == 1);
-                                       rem_ref_cm_node(cm_core, cm_node);
-                               }
-                               dev_kfree_skb_any(skb);
-                               kfree(send_entry);
-                               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-                               continue;
+                               close_when_complete =
+                                       send_entry->close_when_complete;
+                               nes_debug(NES_DBG_CM, "cm_node=%p state=%d\n",
+                                       cm_node, cm_node->state);
+                               free_retrans_entry(cm_node);
+                               if (close_when_complete)
+                                       rem_ref_cm_node(cm_node->cm_core,
+                                               cm_node);
                        }
-               }
-               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-
-               rem_ref_cm_node(cm_core, cm_node);
+               } while (0);
 
+               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
                spin_lock_irqsave(&cm_core->ht_lock, flags);
-               if (ret != NETDEV_TX_OK)
+               if (ret != NETDEV_TX_OK) {
+                       nes_debug(NES_DBG_CM, "rexmit failed for cm_node=%p\n",
+                               cm_node);
                        break;
+               }
        }
        spin_unlock_irqrestore(&cm_core->ht_lock, flags);
 
@@ -667,14 +636,14 @@ static void nes_cm_timer_tick(unsigned long pass)
 /**
  * send_syn
  */
-static int send_syn(struct nes_cm_node *cm_node, u32 sendack)
+static int send_syn(struct nes_cm_node *cm_node, u32 sendack,
+       struct sk_buff *skb)
 {
        int ret;
        int flags = SET_SYN;
-       struct sk_buff *skb;
        char optionsbuffer[sizeof(struct option_mss) +
-                       sizeof(struct option_windowscale) +
-                       sizeof(struct option_base) + 1];
+               sizeof(struct option_windowscale) + sizeof(struct option_base) +
+               TCP_OPTIONS_PADDING];
 
        int optionssize = 0;
        /* Sending MSS option */
@@ -695,8 +664,7 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack)
        options->as_windowscale.shiftcount = cm_node->tcp_cntxt.rcv_wscale;
        optionssize += sizeof(struct option_windowscale);
 
-       if (sendack && !(NES_DRV_OPT_SUPRESS_OPTION_BC & nes_drv_opt)
-                       ) {
+       if (sendack && !(NES_DRV_OPT_SUPRESS_OPTION_BC & nes_drv_opt)) {
                options = (union all_known_options *)&optionsbuffer[optionssize];
                options->as_base.optionnum = OPTION_NUMBER_WRITE0;
                options->as_base.length = sizeof(struct option_base);
@@ -714,7 +682,8 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack)
        options->as_end = OPTION_NUMBER_END;
        optionssize += 1;
 
-       skb = get_free_pkt(cm_node);
+       if (!skb)
+               skb = get_free_pkt(cm_node);
        if (!skb) {
                nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
                return -1;
@@ -733,18 +702,18 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack)
 /**
  * send_reset
  */
-static int send_reset(struct nes_cm_node *cm_node)
+static int send_reset(struct nes_cm_node *cm_node, struct sk_buff *skb)
 {
        int ret;
-       struct sk_buff *skb = get_free_pkt(cm_node);
        int flags = SET_RST | SET_ACK;
 
+       if (!skb)
+               skb = get_free_pkt(cm_node);
        if (!skb) {
                nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
                return -1;
        }
 
-       add_ref_cm_node(cm_node);
        form_cm_frame(skb, cm_node, NULL, 0, NULL, 0, flags);
        ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 0, 1);
 
@@ -755,10 +724,12 @@ static int send_reset(struct nes_cm_node *cm_node)
 /**
  * send_ack
  */
-static int send_ack(struct nes_cm_node *cm_node)
+static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb)
 {
        int ret;
-       struct sk_buff *skb = get_free_pkt(cm_node);
+
+       if (!skb)
+               skb = get_free_pkt(cm_node);
 
        if (!skb) {
                nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
@@ -922,7 +893,8 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
        if (!cm_node || !cm_core)
                return -EINVAL;
 
-       nes_debug(NES_DBG_CM, "Adding Node to Active Connection HT\n");
+       nes_debug(NES_DBG_CM, "Adding Node %p to Active Connection HT\n",
+               cm_node);
 
        /* first, make an index into our hash table */
        hashkey = make_hashkey(cm_node->loc_port, cm_node->loc_addr,
@@ -946,10 +918,35 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
  * mini_cm_dec_refcnt_listen
  */
 static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
-               struct nes_cm_listener *listener, int free_hanging_nodes)
+       struct nes_cm_listener *listener, int free_hanging_nodes)
 {
        int ret = 1;
        unsigned long flags;
+       struct list_head *list_pos = NULL;
+       struct list_head *list_temp = NULL;
+       struct nes_cm_node *cm_node = NULL;
+
+       nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, "
+               "refcnt=%d\n", listener, free_hanging_nodes,
+               atomic_read(&listener->ref_count));
+       /* free non-accelerated child nodes for this listener */
+       if (free_hanging_nodes) {
+               spin_lock_irqsave(&cm_core->ht_lock, flags);
+               list_for_each_safe(list_pos, list_temp,
+                       &g_cm_core->connected_nodes) {
+                       cm_node = container_of(list_pos, struct nes_cm_node,
+                               list);
+                       if ((cm_node->listener == listener) &&
+                               (!cm_node->accelerated)) {
+                               cleanup_retrans_entry(cm_node);
+                               spin_unlock_irqrestore(&cm_core->ht_lock,
+                                       flags);
+                               send_reset(cm_node, NULL);
+                               spin_lock_irqsave(&cm_core->ht_lock, flags);
+                       }
+               }
+               spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+       }
        spin_lock_irqsave(&cm_core->listen_list_lock, flags);
        if (!atomic_dec_return(&listener->ref_count)) {
                list_del(&listener->list);
@@ -1067,18 +1064,18 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
        cm_node->loc_port = cm_info->loc_port;
        cm_node->rem_port = cm_info->rem_port;
        cm_node->send_write0 = send_first;
-       nes_debug(NES_DBG_CM, "Make node addresses : loc = " NIPQUAD_FMT ":%x, rem = " NIPQUAD_FMT ":%x\n",
-                 HIPQUAD(cm_node->loc_addr), cm_node->loc_port,
-                 HIPQUAD(cm_node->rem_addr), cm_node->rem_port);
+       nes_debug(NES_DBG_CM, "Make node addresses : loc = " NIPQUAD_FMT
+                       ":%x, rem = " NIPQUAD_FMT ":%x\n",
+                       HIPQUAD(cm_node->loc_addr), cm_node->loc_port,
+                       HIPQUAD(cm_node->rem_addr), cm_node->rem_port);
        cm_node->listener = listener;
        cm_node->netdev = nesvnic->netdev;
        cm_node->cm_id = cm_info->cm_id;
        memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN);
 
-       nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n",
-                       cm_node->listener, cm_node->cm_id);
+       nes_debug(NES_DBG_CM, "listener=%p, cm_id=%p\n", cm_node->listener,
+                       cm_node->cm_id);
 
-       INIT_LIST_HEAD(&cm_node->retrans_list);
        spin_lock_init(&cm_node->retrans_list_lock);
        INIT_LIST_HEAD(&cm_node->recv_list);
        spin_lock_init(&cm_node->recv_list_lock);
@@ -1142,10 +1139,9 @@ static int add_ref_cm_node(struct nes_cm_node *cm_node)
  * rem_ref_cm_node - destroy an instance of a cm node
  */
 static int rem_ref_cm_node(struct nes_cm_core *cm_core,
-               struct nes_cm_node *cm_node)
+       struct nes_cm_node *cm_node)
 {
        unsigned long flags, qplockflags;
-       struct nes_timer_entry *send_entry;
        struct nes_timer_entry *recv_entry;
        struct iw_cm_id *cm_id;
        struct list_head *list_core, *list_node_temp;
@@ -1169,48 +1165,33 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
                atomic_dec(&cm_node->listener->pend_accepts_cnt);
                BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
        }
-
-       spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-       list_for_each_safe(list_core, list_node_temp, &cm_node->retrans_list) {
-               send_entry = container_of(list_core, struct nes_timer_entry, list);
-               list_del(&send_entry->list);
-               spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-               dev_kfree_skb_any(send_entry->skb);
-               kfree(send_entry);
-               spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
-               continue;
-       }
-       spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
-
+       BUG_ON(cm_node->send_entry);
        spin_lock_irqsave(&cm_node->recv_list_lock, flags);
        list_for_each_safe(list_core, list_node_temp, &cm_node->recv_list) {
-               recv_entry = container_of(list_core, struct nes_timer_entry, list);
+               recv_entry = container_of(list_core, struct nes_timer_entry,
+                               list);
                list_del(&recv_entry->list);
                cm_id = cm_node->cm_id;
                spin_unlock_irqrestore(&cm_node->recv_list_lock, flags);
-               if (recv_entry->type == NES_TIMER_TYPE_CLOSE) {
-                       nesqp = (struct nes_qp *)recv_entry->skb;
-                       spin_lock_irqsave(&nesqp->lock, qplockflags);
-                       if (nesqp->cm_id) {
-                               nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: ****** HIT A NES_TIMER_TYPE_CLOSE"
-                                               " with something to do!!! ******\n",
-                                               nesqp->hwqp.qp_id, cm_id);
-                               nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
-                               nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
-                               nesqp->ibqp_state = IB_QPS_ERR;
-                               spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                               nes_cm_disconn(nesqp);
-                       } else {
-                               spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                               nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: ****** HIT A NES_TIMER_TYPE_CLOSE"
-                                               " with nothing to do!!! ******\n",
-                                               nesqp->hwqp.qp_id, cm_id);
-                               nes_rem_ref(&nesqp->ibqp);
-                       }
-                       cm_id->rem_ref(cm_id);
-               } else if (recv_entry->type == NES_TIMER_TYPE_RECV) {
-                       dev_kfree_skb_any(recv_entry->skb);
+               nesqp = (struct nes_qp *)recv_entry->skb;
+               spin_lock_irqsave(&nesqp->lock, qplockflags);
+               if (nesqp->cm_id) {
+                       nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: HIT A "
+                               "NES_TIMER_TYPE_CLOSE with something to do!\n",
+                               nesqp->hwqp.qp_id, cm_id);
+                       nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
+                       nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
+                       nesqp->ibqp_state = IB_QPS_ERR;
+                       spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+                       nes_cm_disconn(nesqp);
+               } else {
+                       spin_unlock_irqrestore(&nesqp->lock, qplockflags);
+                       nes_debug(NES_DBG_CM, "QP%u: cm_id = %p: HIT A "
+                               "NES_TIMER_TYPE_CLOSE with nothing to do!\n",
+                               nesqp->hwqp.qp_id, cm_id);
                }
+               cm_id->rem_ref(cm_id);
+
                kfree(recv_entry);
                spin_lock_irqsave(&cm_node->recv_list_lock, flags);
        }
@@ -1221,23 +1202,31 @@ static int rem_ref_cm_node(struct nes_cm_core *cm_core,
        } else {
                if (cm_node->apbvt_set && cm_node->nesvnic) {
                        nes_manage_apbvt(cm_node->nesvnic, cm_node->loc_port,
-                                       PCI_FUNC(cm_node->nesvnic->nesdev->pcidev->devfn),
-                                       NES_MANAGE_APBVT_DEL);
+                               PCI_FUNC(
+                               cm_node->nesvnic->nesdev->pcidev->devfn),
+                               NES_MANAGE_APBVT_DEL);
                }
        }
 
-       kfree(cm_node);
        atomic_dec(&cm_core->node_cnt);
        atomic_inc(&cm_nodes_destroyed);
+       nesqp = cm_node->nesqp;
+       if (nesqp) {
+               nesqp->cm_node = NULL;
+               nes_rem_ref(&nesqp->ibqp);
+               cm_node->nesqp = NULL;
+       }
 
+       cm_node->freed = 1;
+       kfree(cm_node);
        return 0;
 }
 
-
 /**
  * process_options
  */
-static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 optionsize, u32 syn_packet)
+static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc,
+       u32 optionsize, u32 syn_packet)
 {
        u32 tmp;
        u32 offset = 0;
@@ -1247,35 +1236,37 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 opti
        while (offset < optionsize) {
                all_options = (union all_known_options *)(optionsloc + offset);
                switch (all_options->as_base.optionnum) {
-                       case OPTION_NUMBER_END:
-                               offset = optionsize;
-                               break;
-                       case OPTION_NUMBER_NONE:
-                               offset += 1;
-                               continue;
-                       case OPTION_NUMBER_MSS:
-                               nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d Size: %d\n",
-                                               __func__,
-                                               all_options->as_mss.length, offset, optionsize);
-                               got_mss_option = 1;
-                               if (all_options->as_mss.length != 4) {
-                                       return 1;
-                               } else {
-                                       tmp = ntohs(all_options->as_mss.mss);
-                                       if (tmp > 0 && tmp < cm_node->tcp_cntxt.mss)
-                                               cm_node->tcp_cntxt.mss = tmp;
-                               }
-                               break;
-                       case OPTION_NUMBER_WINDOW_SCALE:
-                               cm_node->tcp_cntxt.snd_wscale = all_options->as_windowscale.shiftcount;
-                               break;
-                       case OPTION_NUMBER_WRITE0:
-                               cm_node->send_write0 = 1;
-                               break;
-                       default:
-                               nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n",
-                                               all_options->as_base.optionnum);
-                               break;
+               case OPTION_NUMBER_END:
+                       offset = optionsize;
+                       break;
+               case OPTION_NUMBER_NONE:
+                       offset += 1;
+                       continue;
+               case OPTION_NUMBER_MSS:
+                       nes_debug(NES_DBG_CM, "%s: MSS Length: %d Offset: %d "
+                               "Size: %d\n", __func__,
+                               all_options->as_mss.length, offset, optionsize);
+                       got_mss_option = 1;
+                       if (all_options->as_mss.length != 4) {
+                               return 1;
+                       } else {
+                               tmp = ntohs(all_options->as_mss.mss);
+                               if (tmp > 0 && tmp <
+                                       cm_node->tcp_cntxt.mss)
+                                       cm_node->tcp_cntxt.mss = tmp;
+                       }
+                       break;
+               case OPTION_NUMBER_WINDOW_SCALE:
+                       cm_node->tcp_cntxt.snd_wscale =
+                               all_options->as_windowscale.shiftcount;
+                       break;
+               case OPTION_NUMBER_WRITE0:
+                       cm_node->send_write0 = 1;
+                       break;
+               default:
+                       nes_debug(NES_DBG_CM, "TCP Option not understood: %x\n",
+                               all_options->as_base.optionnum);
+                       break;
                }
                offset += all_options->as_base.length;
        }
@@ -1284,300 +1275,491 @@ static int process_options(struct nes_cm_node *cm_node, u8 *optionsloc, u32 opti
        return 0;
 }
 
+static void drop_packet(struct sk_buff *skb)
+{
+       atomic_inc(&cm_accel_dropped_pkts);
+       dev_kfree_skb_any(skb);
+}
 
-/**
- * process_packet
- */
-static int process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
-                         struct nes_cm_core *cm_core)
+static void handle_fin_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       struct tcphdr *tcph)
 {
-       int optionsize;
-       int datasize;
-       int ret = 0;
-       struct tcphdr *tcph = tcp_hdr(skb);
-       u32 inc_sequence;
-       if (cm_node->state == NES_CM_STATE_SYN_SENT && tcph->syn) {
-               inc_sequence = ntohl(tcph->seq);
-               cm_node->tcp_cntxt.rcv_nxt = inc_sequence;
+       atomic_inc(&cm_resets_recvd);
+       nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
+               "refcnt=%d\n", cm_node, cm_node->state,
+               atomic_read(&cm_node->ref_count));
+       cm_node->tcp_cntxt.rcv_nxt++;
+       cleanup_retrans_entry(cm_node);
+       switch (cm_node->state) {
+       case NES_CM_STATE_SYN_RCVD:
+       case NES_CM_STATE_SYN_SENT:
+       case NES_CM_STATE_ESTABLISHED:
+       case NES_CM_STATE_MPAREQ_SENT:
+               cm_node->state = NES_CM_STATE_LAST_ACK;
+               send_fin(cm_node, skb);
+               break;
+       case NES_CM_STATE_FIN_WAIT1:
+               cm_node->state = NES_CM_STATE_CLOSING;
+               send_ack(cm_node, skb);
+               break;
+       case NES_CM_STATE_FIN_WAIT2:
+               cm_node->state = NES_CM_STATE_TIME_WAIT;
+               send_ack(cm_node, skb);
+               cm_node->state = NES_CM_STATE_CLOSED;
+               break;
+       case NES_CM_STATE_TSA:
+       default:
+               nes_debug(NES_DBG_CM, "Error Rcvd FIN for node-%p state = %d\n",
+                       cm_node, cm_node->state);
+               drop_packet(skb);
+               break;
        }
+}
 
-       if ((!tcph) || (cm_node->state == NES_CM_STATE_TSA)) {
-               BUG_ON(!tcph);
-               atomic_inc(&cm_accel_dropped_pkts);
-               return -1;
-       }
 
-       if (tcph->rst) {
-               atomic_inc(&cm_resets_recvd);
-               nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u. refcnt=%d\n",
-                               cm_node, cm_node->state, atomic_read(&cm_node->ref_count));
-               switch (cm_node->state) {
-                       case NES_CM_STATE_LISTENING:
-                               rem_ref_cm_node(cm_core, cm_node);
-                               break;
-                       case NES_CM_STATE_TSA:
-                       case NES_CM_STATE_CLOSED:
-                               break;
-                       case NES_CM_STATE_SYN_RCVD:
-                                       nes_debug(NES_DBG_CM, "Received a reset for local 0x%08X:%04X,"
-                                                       " remote 0x%08X:%04X, node state = %u\n",
-                                                       cm_node->loc_addr, cm_node->loc_port,
-                                                       cm_node->rem_addr, cm_node->rem_port,
-                                                       cm_node->state);
-                               rem_ref_cm_node(cm_core, cm_node);
-                               break;
-                       case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
-                       case NES_CM_STATE_ESTABLISHED:
-                       case NES_CM_STATE_MPAREQ_SENT:
-                       default:
-                                       nes_debug(NES_DBG_CM, "Received a reset for local 0x%08X:%04X,"
-                                                       " remote 0x%08X:%04X, node state = %u refcnt=%d\n",
-                                                       cm_node->loc_addr, cm_node->loc_port,
-                                                       cm_node->rem_addr, cm_node->rem_port,
-                                                       cm_node->state, atomic_read(&cm_node->ref_count));
-                               /* create event */
-                               cm_node->state = NES_CM_STATE_CLOSED;
+static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       struct tcphdr *tcph)
+{
 
-                               create_event(cm_node, NES_CM_EVENT_ABORTED);
-                               break;
+       int     reset = 0;      /* whether to send reset in case of err.. */
+       atomic_inc(&cm_resets_recvd);
+       nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u."
+                       " refcnt=%d\n", cm_node, cm_node->state,
+                       atomic_read(&cm_node->ref_count));
+       cleanup_retrans_entry(cm_node);
+       switch (cm_node->state) {
+       case NES_CM_STATE_SYN_SENT:
+       case NES_CM_STATE_MPAREQ_SENT:
+               nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
+                       "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+                       cm_node->listener, cm_node->state);
+               active_open_err(cm_node, skb, reset);
+               break;
+       /* For PASSIVE open states, remove the cm_node event */
+       case NES_CM_STATE_ESTABLISHED:
+       case NES_CM_STATE_SYN_RCVD:
+       case NES_CM_STATE_LISTENING:
+               nes_debug(NES_DBG_CM, "Bad state %s[%u]\n", __func__, __LINE__);
+               passive_open_err(cm_node, skb, reset);
+               break;
+       case NES_CM_STATE_TSA:
+       default:
+               break;
+       }
+}
 
+static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       enum nes_cm_event_type type)
+{
+
+       int     ret;
+       int datasize = skb->len;
+       u8 *dataloc = skb->data;
+       ret = parse_mpa(cm_node, dataloc, datasize);
+       if (ret < 0) {
+               nes_debug(NES_DBG_CM, "didn't like MPA Request\n");
+               if (type == NES_CM_EVENT_CONNECTED) {
+                       nes_debug(NES_DBG_CM, "%s[%u] create abort for "
+                               "cm_node=%p listener=%p state=%d\n", __func__,
+                               __LINE__, cm_node, cm_node->listener,
+                               cm_node->state);
+                       active_open_err(cm_node, skb, 1);
+               } else {
+                       passive_open_err(cm_node, skb, 1);
                }
-               return -1;
+       } else {
+               cleanup_retrans_entry(cm_node);
+               dev_kfree_skb_any(skb);
+               if (type == NES_CM_EVENT_CONNECTED)
+                       cm_node->state = NES_CM_STATE_TSA;
+               create_event(cm_node, type);
+
+       }
+       return ;
+}
+
+static void indicate_pkt_err(struct nes_cm_node *cm_node, struct sk_buff *skb)
+{
+       switch (cm_node->state) {
+       case NES_CM_STATE_SYN_SENT:
+       case NES_CM_STATE_MPAREQ_SENT:
+               nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
+                       "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+                       cm_node->listener, cm_node->state);
+               active_open_err(cm_node, skb, 1);
+               break;
+       case NES_CM_STATE_ESTABLISHED:
+       case NES_CM_STATE_SYN_RCVD:
+               passive_open_err(cm_node, skb, 1);
+               break;
+       case NES_CM_STATE_TSA:
+       default:
+               drop_packet(skb);
        }
+}
+
+static int check_syn(struct nes_cm_node *cm_node, struct tcphdr *tcph,
+       struct sk_buff *skb)
+{
+       int err;
+
+       err = ((ntohl(tcph->ack_seq) == cm_node->tcp_cntxt.loc_seq_num))? 0 : 1;
+       if (err)
+               active_open_err(cm_node, skb, 1);
+
+       return err;
+}
+
+static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
+       struct sk_buff *skb)
+{
+       int err = 0;
+       u32 seq;
+       u32 ack_seq;
+       u32 loc_seq_num = cm_node->tcp_cntxt.loc_seq_num;
+       u32 rcv_nxt = cm_node->tcp_cntxt.rcv_nxt;
+       u32 rcv_wnd;
+       seq = ntohl(tcph->seq);
+       ack_seq = ntohl(tcph->ack_seq);
+       rcv_wnd = cm_node->tcp_cntxt.rcv_wnd;
+       if (ack_seq != loc_seq_num)
+               err = 1;
+       else if ((seq + rcv_wnd) < rcv_nxt)
+               err = 1;
+       if (err) {
+               nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
+                       "listener=%p state=%d\n", __func__, __LINE__, cm_node,
+                       cm_node->listener, cm_node->state);
+               indicate_pkt_err(cm_node, skb);
+               nes_debug(NES_DBG_CM, "seq ERROR cm_node =%p seq=0x%08X "
+                       "rcv_nxt=0x%08X rcv_wnd=0x%x\n", cm_node, seq, rcv_nxt,
+                       rcv_wnd);
+       }
+       return err;
+}
+
+/*
+ * handle_syn_pkt() is for Passive node. The syn packet is received when a node
+ * is created with a listener or it may comein as rexmitted packet which in
+ * that case will be just dropped.
+ */
+
+static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       struct tcphdr *tcph)
+{
+       int ret;
+       u32 inc_sequence;
+       int optionsize;
 
        optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
+       skb_pull(skb, tcph->doff << 2);
+       inc_sequence = ntohl(tcph->seq);
 
-       skb_pull(skb, ip_hdr(skb)->ihl << 2);
+       switch (cm_node->state) {
+       case NES_CM_STATE_SYN_SENT:
+       case NES_CM_STATE_MPAREQ_SENT:
+               /* Rcvd syn on active open connection*/
+               active_open_err(cm_node, skb, 1);
+               break;
+       case NES_CM_STATE_LISTENING:
+               /* Passive OPEN */
+               cm_node->accept_pend = 1;
+               atomic_inc(&cm_node->listener->pend_accepts_cnt);
+               if (atomic_read(&cm_node->listener->pend_accepts_cnt) >
+                               cm_node->listener->backlog) {
+                       nes_debug(NES_DBG_CM, "drop syn due to backlog "
+                               "pressure \n");
+                       cm_backlog_drops++;
+                       passive_open_err(cm_node, skb, 0);
+                       break;
+               }
+               ret = handle_tcp_options(cm_node, tcph, skb, optionsize,
+                       1);
+               if (ret) {
+                       passive_open_err(cm_node, skb, 0);
+                       /* drop pkt */
+                       break;
+               }
+               cm_node->tcp_cntxt.rcv_nxt = inc_sequence + 1;
+               BUG_ON(cm_node->send_entry);
+               cm_node->state = NES_CM_STATE_SYN_RCVD;
+               send_syn(cm_node, 1, skb);
+               break;
+       case NES_CM_STATE_TSA:
+       case NES_CM_STATE_ESTABLISHED:
+       case NES_CM_STATE_FIN_WAIT1:
+       case NES_CM_STATE_FIN_WAIT2:
+       case NES_CM_STATE_MPAREQ_RCVD:
+       case NES_CM_STATE_LAST_ACK:
+       case NES_CM_STATE_CLOSING:
+       case NES_CM_STATE_UNKNOWN:
+       case NES_CM_STATE_CLOSED:
+       default:
+               drop_packet(skb);
+               break;
+       }
+}
+
+static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       struct tcphdr *tcph)
+{
+
+       int ret;
+       u32 inc_sequence;
+       int optionsize;
+
+       optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
        skb_pull(skb, tcph->doff << 2);
+       inc_sequence = ntohl(tcph->seq);
+       switch (cm_node->state) {
+       case NES_CM_STATE_SYN_SENT:
+               /* active open */
+               if (check_syn(cm_node, tcph, skb))
+                       return;
+               cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
+               /* setup options */
+               ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 0);
+               if (ret) {
+                       nes_debug(NES_DBG_CM, "cm_node=%p tcp_options failed\n",
+                               cm_node);
+                       break;
+               }
+               cleanup_retrans_entry(cm_node);
+               cm_node->tcp_cntxt.rcv_nxt = inc_sequence + 1;
+               send_mpa_request(cm_node, skb);
+               cm_node->state = NES_CM_STATE_MPAREQ_SENT;
+               break;
+       case NES_CM_STATE_MPAREQ_RCVD:
+               /* passive open, so should not be here */
+               passive_open_err(cm_node, skb, 1);
+               break;
+       case NES_CM_STATE_ESTABLISHED:
+       case NES_CM_STATE_FIN_WAIT1:
+       case NES_CM_STATE_FIN_WAIT2:
+       case NES_CM_STATE_LAST_ACK:
+       case NES_CM_STATE_TSA:
+       case NES_CM_STATE_CLOSING:
+       case NES_CM_STATE_UNKNOWN:
+       case NES_CM_STATE_CLOSED:
+       case NES_CM_STATE_MPAREQ_SENT:
+       default:
+               drop_packet(skb);
+               break;
+       }
+}
 
-       datasize = skb->len;
+static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       struct tcphdr *tcph)
+{
+       int datasize = 0;
+       u32 inc_sequence;
+       u32 rem_seq_ack;
+       u32 rem_seq;
+       if (check_seq(cm_node, tcph, skb))
+               return;
+
+       skb_pull(skb, tcph->doff << 2);
        inc_sequence = ntohl(tcph->seq);
-       nes_debug(NES_DBG_CM, "datasize = %u, sequence = 0x%08X, ack_seq = 0x%08X,"
-                       " rcv_nxt = 0x%08X Flags: %s %s.\n",
-                       datasize, inc_sequence, ntohl(tcph->ack_seq),
-                       cm_node->tcp_cntxt.rcv_nxt, (tcph->syn ? "SYN":""),
-                       (tcph->ack ? "ACK":""));
-
-       if (!tcph->syn && (inc_sequence != cm_node->tcp_cntxt.rcv_nxt)
-               ) {
-               nes_debug(NES_DBG_CM, "dropping packet, datasize = %u, sequence = 0x%08X,"
-                               " ack_seq = 0x%08X, rcv_nxt = 0x%08X Flags: %s.\n",
-                               datasize, inc_sequence, ntohl(tcph->ack_seq),
-                               cm_node->tcp_cntxt.rcv_nxt, (tcph->ack ? "ACK":""));
-               if (cm_node->state == NES_CM_STATE_LISTENING) {
-                       rem_ref_cm_node(cm_core, cm_node);
+       rem_seq = ntohl(tcph->seq);
+       rem_seq_ack =  ntohl(tcph->ack_seq);
+       datasize = skb->len;
+
+       switch (cm_node->state) {
+       case NES_CM_STATE_SYN_RCVD:
+               /* Passive OPEN */
+               cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
+               cm_node->state = NES_CM_STATE_ESTABLISHED;
+               if (datasize) {
+                       cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
+                       cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
+                       handle_rcv_mpa(cm_node, skb, NES_CM_EVENT_MPA_REQ);
+                } else { /* rcvd ACK only */
+                       dev_kfree_skb_any(skb);
+                       cleanup_retrans_entry(cm_node);
+                }
+               break;
+       case NES_CM_STATE_ESTABLISHED:
+               /* Passive OPEN */
+               /* We expect mpa frame to be received only */
+               if (datasize) {
+                       cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
+                       cm_node->state = NES_CM_STATE_MPAREQ_RCVD;
+                       handle_rcv_mpa(cm_node, skb,
+                               NES_CM_EVENT_MPA_REQ);
+               } else
+                       drop_packet(skb);
+               break;
+       case NES_CM_STATE_MPAREQ_SENT:
+               cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
+               if (datasize) {
+                       cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
+                       handle_rcv_mpa(cm_node, skb, NES_CM_EVENT_CONNECTED);
+               } else { /* Could be just an ack pkt.. */
+                       cleanup_retrans_entry(cm_node);
+                       dev_kfree_skb_any(skb);
                }
-               return -1;
+               break;
+       case NES_CM_STATE_FIN_WAIT1:
+       case NES_CM_STATE_SYN_SENT:
+       case NES_CM_STATE_FIN_WAIT2:
+       case NES_CM_STATE_TSA:
+       case NES_CM_STATE_CLOSED:
+       case NES_CM_STATE_MPAREQ_RCVD:
+       case NES_CM_STATE_LAST_ACK:
+       case NES_CM_STATE_CLOSING:
+       case NES_CM_STATE_UNKNOWN:
+       default:
+               drop_packet(skb);
+               break;
        }
+}
 
-               cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
 
 
+static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
+       struct sk_buff *skb, int optionsize, int passive)
+{
+       u8 *optionsloc = (u8 *)&tcph[1];
        if (optionsize) {
-               u8 *optionsloc = (u8 *)&tcph[1];
-               if (process_options(cm_node, optionsloc, optionsize, (u32)tcph->syn)) {
-                       nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n", __func__, cm_node);
-                       send_reset(cm_node);
-                       if (cm_node->state != NES_CM_STATE_SYN_SENT)
-                       rem_ref_cm_node(cm_core, cm_node);
-                       return 0;
+               if (process_options(cm_node, optionsloc, optionsize,
+                       (u32)tcph->syn)) {
+                       nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n",
+                               __func__, cm_node);
+                       if (passive)
+                               passive_open_err(cm_node, skb, 0);
+                       else
+                               active_open_err(cm_node, skb, 0);
+                       return 1;
                }
-       } else if (tcph->syn)
-               cm_node->tcp_cntxt.mss = NES_CM_DEFAULT_MSS;
+       }
 
        cm_node->tcp_cntxt.snd_wnd = ntohs(tcph->window) <<
                        cm_node->tcp_cntxt.snd_wscale;
 
-       if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd) {
+       if (cm_node->tcp_cntxt.snd_wnd > cm_node->tcp_cntxt.max_snd_wnd)
                cm_node->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.snd_wnd;
-       }
+       return 0;
+}
 
-       if (tcph->ack) {
-               cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
-               switch (cm_node->state) {
-                       case NES_CM_STATE_SYN_RCVD:
-                       case NES_CM_STATE_SYN_SENT:
-                               /* read and stash current sequence number */
-                               if (cm_node->tcp_cntxt.rem_ack_num != cm_node->tcp_cntxt.loc_seq_num) {
-                                       nes_debug(NES_DBG_CM, "ERROR - cm_node->tcp_cntxt.rem_ack_num !="
-                                                       " cm_node->tcp_cntxt.loc_seq_num\n");
-                                       send_reset(cm_node);
-                                       return 0;
-                               }
-                               if (cm_node->state == NES_CM_STATE_SYN_SENT)
-                                       cm_node->state = NES_CM_STATE_ONE_SIDE_ESTABLISHED;
-                               else {
-                                               cm_node->state = NES_CM_STATE_ESTABLISHED;
-                               }
-                               break;
-                       case NES_CM_STATE_LAST_ACK:
-                               cm_node->state = NES_CM_STATE_CLOSED;
-                               break;
-                       case NES_CM_STATE_FIN_WAIT1:
-                               cm_node->state = NES_CM_STATE_FIN_WAIT2;
-                               break;
-                       case NES_CM_STATE_CLOSING:
-                               cm_node->state = NES_CM_STATE_TIME_WAIT;
-                               /* need to schedule this to happen in 2MSL timeouts */
-                               cm_node->state = NES_CM_STATE_CLOSED;
-                               break;
-                       case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
-                       case NES_CM_STATE_ESTABLISHED:
-                       case NES_CM_STATE_MPAREQ_SENT:
-                       case NES_CM_STATE_CLOSE_WAIT:
-                       case NES_CM_STATE_TIME_WAIT:
-                       case NES_CM_STATE_CLOSED:
-                               break;
-                       case NES_CM_STATE_LISTENING:
-                               nes_debug(NES_DBG_CM, "Received an ACK on a listening port (SYN %d)\n", tcph->syn);
-                               cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
-                               send_reset(cm_node);
-                               /* send_reset bumps refcount, this should have been a new node */
-                               rem_ref_cm_node(cm_core, cm_node);
-                               return -1;
-                               break;
-                       case NES_CM_STATE_TSA:
-                               nes_debug(NES_DBG_CM, "Received a packet with the ack bit set while in TSA state\n");
-                               break;
-                       case NES_CM_STATE_UNKNOWN:
-                       case NES_CM_STATE_INITED:
-                       case NES_CM_STATE_ACCEPTING:
-                       case NES_CM_STATE_FIN_WAIT2:
-                       default:
-                               nes_debug(NES_DBG_CM, "Received ack from unknown state: %x\n",
-                                               cm_node->state);
-                               send_reset(cm_node);
-                               break;
-               }
-       }
+/*
+ * active_open_err() will send reset() if flag set..
+ * It will also send ABORT event.
+ */
 
-       if (tcph->syn) {
-               if (cm_node->state == NES_CM_STATE_LISTENING) {
-                       /* do not exceed backlog */
-                       atomic_inc(&cm_node->listener->pend_accepts_cnt);
-                       if (atomic_read(&cm_node->listener->pend_accepts_cnt) >
-                                       cm_node->listener->backlog) {
-                               nes_debug(NES_DBG_CM, "drop syn due to backlog pressure \n");
-                               cm_backlog_drops++;
-                               atomic_dec(&cm_node->listener->pend_accepts_cnt);
-                               rem_ref_cm_node(cm_core, cm_node);
-                               return 0;
-                       }
-                       cm_node->accept_pend = 1;
+static void active_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       int reset)
+{
+       cleanup_retrans_entry(cm_node);
+       if (reset) {
+               nes_debug(NES_DBG_CM, "ERROR active err called for cm_node=%p, "
+                               "state=%d\n", cm_node, cm_node->state);
+               add_ref_cm_node(cm_node);
+               send_reset(cm_node, skb);
+       } else
+               dev_kfree_skb_any(skb);
 
-               }
-               if (datasize == 0)
-                       cm_node->tcp_cntxt.rcv_nxt ++;
+       cm_node->state = NES_CM_STATE_CLOSED;
+       create_event(cm_node, NES_CM_EVENT_ABORTED);
+}
 
-               if (cm_node->state == NES_CM_STATE_LISTENING) {
-                       cm_node->state = NES_CM_STATE_SYN_RCVD;
-                       send_syn(cm_node, 1);
-               }
-               if (cm_node->state == NES_CM_STATE_ONE_SIDE_ESTABLISHED) {
-                       cm_node->state = NES_CM_STATE_ESTABLISHED;
-                       /* send final handshake ACK */
-                       ret = send_ack(cm_node);
-                       if (ret < 0)
-                               return ret;
+/*
+ * passive_open_err() will either do a reset() or will free up the skb and
+ * remove the cm_node.
+ */
 
-                               cm_node->state = NES_CM_STATE_MPAREQ_SENT;
-                               ret = send_mpa_request(cm_node);
-                               if (ret < 0)
-                                       return ret;
-               }
+static void passive_open_err(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       int reset)
+{
+       cleanup_retrans_entry(cm_node);
+       cm_node->state = NES_CM_STATE_CLOSED;
+       if (reset) {
+               nes_debug(NES_DBG_CM, "passive_open_err sending RST for "
+                       "cm_node=%p state =%d\n", cm_node, cm_node->state);
+               send_reset(cm_node, skb);
+       } else {
+               dev_kfree_skb_any(skb);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
        }
+}
 
-       if (tcph->fin) {
-               cm_node->tcp_cntxt.rcv_nxt++;
-               switch (cm_node->state) {
-                       case NES_CM_STATE_SYN_RCVD:
-                       case NES_CM_STATE_SYN_SENT:
-                       case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
-                       case NES_CM_STATE_ESTABLISHED:
-                       case NES_CM_STATE_ACCEPTING:
-                       case NES_CM_STATE_MPAREQ_SENT:
-                               cm_node->state = NES_CM_STATE_CLOSE_WAIT;
-                               cm_node->state = NES_CM_STATE_LAST_ACK;
-                               ret = send_fin(cm_node, NULL);
-                               break;
-                       case NES_CM_STATE_FIN_WAIT1:
-                               cm_node->state = NES_CM_STATE_CLOSING;
-                               ret = send_ack(cm_node);
-                               break;
-                       case NES_CM_STATE_FIN_WAIT2:
-                               cm_node->state = NES_CM_STATE_TIME_WAIT;
-                               cm_node->tcp_cntxt.loc_seq_num ++;
-                               ret = send_ack(cm_node);
-                               /* need to schedule this to happen in 2MSL timeouts */
-                               cm_node->state = NES_CM_STATE_CLOSED;
-                               break;
-                       case NES_CM_STATE_CLOSE_WAIT:
-                       case NES_CM_STATE_LAST_ACK:
-                       case NES_CM_STATE_CLOSING:
-                       case NES_CM_STATE_TSA:
-                       default:
-                               nes_debug(NES_DBG_CM, "Received a fin while in %x state\n",
-                                               cm_node->state);
-                               ret = -EINVAL;
-                               break;
-               }
+/*
+ * free_retrans_entry() routines assumes that the retrans_list_lock has
+ * been acquired before calling.
+ */
+static void free_retrans_entry(struct nes_cm_node *cm_node)
+{
+       struct nes_timer_entry *send_entry;
+       send_entry = cm_node->send_entry;
+       if (send_entry) {
+               cm_node->send_entry = NULL;
+               dev_kfree_skb_any(send_entry->skb);
+               kfree(send_entry);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
        }
+}
 
-       if (datasize) {
-               u8 *dataloc = skb->data;
-               /* figure out what state we are in and handle transition to next state */
-               switch (cm_node->state) {
-                       case NES_CM_STATE_LISTENING:
-                       case NES_CM_STATE_SYN_RCVD:
-                       case NES_CM_STATE_SYN_SENT:
-                       case NES_CM_STATE_FIN_WAIT1:
-                       case NES_CM_STATE_FIN_WAIT2:
-                       case NES_CM_STATE_CLOSE_WAIT:
-                       case NES_CM_STATE_LAST_ACK:
-                       case NES_CM_STATE_CLOSING:
-                               break;
-                       case  NES_CM_STATE_MPAREQ_SENT:
-                               /* recv the mpa res frame, ret=frame len (incl priv data) */
-                               ret = parse_mpa(cm_node, dataloc, datasize);
-                               if (ret < 0)
-                                       break;
-                               /* set the req frame payload len in skb */
-                               /* we are done handling this state, set node to a TSA state */
-                               cm_node->state = NES_CM_STATE_TSA;
-                               send_ack(cm_node);
-                               create_event(cm_node, NES_CM_EVENT_CONNECTED);
-                               break;
-
-                       case  NES_CM_STATE_ESTABLISHED:
-                               /* we are expecting an MPA req frame */
-                               ret = parse_mpa(cm_node, dataloc, datasize);
-                               if (ret < 0) {
-                                       break;
-                               }
-                               cm_node->state = NES_CM_STATE_TSA;
-                               send_ack(cm_node);
-                               /* we got a valid MPA request, create an event */
-                               create_event(cm_node, NES_CM_EVENT_MPA_REQ);
-                               break;
-                       case  NES_CM_STATE_TSA:
-                               handle_exception_pkt(cm_node, skb);
-                               break;
-                       case NES_CM_STATE_UNKNOWN:
-                       case NES_CM_STATE_INITED:
-                       default:
-                               ret = -1;
-               }
-       }
+static void cleanup_retrans_entry(struct nes_cm_node *cm_node)
+{
+       unsigned long flags;
 
-       return ret;
+       spin_lock_irqsave(&cm_node->retrans_list_lock, flags);
+       free_retrans_entry(cm_node);
+       spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
 }
 
+/**
+ * process_packet
+ * Returns skb if to be freed, else it will return NULL if already used..
+ */
+static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
+       struct nes_cm_core *cm_core)
+{
+       enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
+       struct tcphdr *tcph = tcp_hdr(skb);
+       skb_pull(skb, ip_hdr(skb)->ihl << 2);
+
+       nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d "
+               "ack=%d rst=%d fin=%d\n", cm_node, cm_node->state, tcph->syn,
+               tcph->ack, tcph->rst, tcph->fin);
+
+       if (tcph->rst)
+               pkt_type = NES_PKT_TYPE_RST;
+       else if (tcph->syn) {
+               pkt_type = NES_PKT_TYPE_SYN;
+               if (tcph->ack)
+                       pkt_type = NES_PKT_TYPE_SYNACK;
+       } else if (tcph->fin)
+               pkt_type = NES_PKT_TYPE_FIN;
+       else if (tcph->ack)
+               pkt_type = NES_PKT_TYPE_ACK;
+
+       switch (pkt_type) {
+       case NES_PKT_TYPE_SYN:
+               handle_syn_pkt(cm_node, skb, tcph);
+               break;
+       case NES_PKT_TYPE_SYNACK:
+               handle_synack_pkt(cm_node, skb, tcph);
+               break;
+       case NES_PKT_TYPE_ACK:
+               handle_ack_pkt(cm_node, skb, tcph);
+               break;
+       case NES_PKT_TYPE_RST:
+               handle_rst_pkt(cm_node, skb, tcph);
+               break;
+       case NES_PKT_TYPE_FIN:
+               handle_fin_pkt(cm_node, skb, tcph);
+               break;
+       default:
+               drop_packet(skb);
+               break;
+       }
+}
 
 /**
  * mini_cm_listen - create a listen node with params
  */
 static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
-               struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
+       struct nes_vnic *nesvnic, struct nes_cm_info *cm_info)
 {
        struct nes_cm_listener *listener;
        unsigned long flags;
@@ -1644,37 +1826,36 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
 /**
  * mini_cm_connect - make a connection node with params
  */
-static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
-                                          struct nes_vnic *nesvnic,
-                                          struct ietf_mpa_frame *mpa_frame,
-                                          struct nes_cm_info *cm_info)
+struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
+       struct nes_vnic *nesvnic, u16 private_data_len,
+       void *private_data, struct nes_cm_info *cm_info)
 {
        int ret = 0;
        struct nes_cm_node *cm_node;
        struct nes_cm_listener *loopbackremotelistener;
        struct nes_cm_node *loopbackremotenode;
        struct nes_cm_info loopback_cm_info;
-
-       u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) +
-                       ntohs(mpa_frame->priv_data_len);
-
-       cm_info->loc_addr = htonl(cm_info->loc_addr);
-       cm_info->rem_addr = htonl(cm_info->rem_addr);
-       cm_info->loc_port = htons(cm_info->loc_port);
-       cm_info->rem_port = htons(cm_info->rem_port);
+       u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) + private_data_len;
+       struct ietf_mpa_frame *mpa_frame = NULL;
 
        /* create a CM connection node */
        cm_node = make_cm_node(cm_core, nesvnic, cm_info, NULL);
        if (!cm_node)
                return NULL;
+       mpa_frame = &cm_node->mpa_frame;
+       strcpy(mpa_frame->key, IEFT_MPA_KEY_REQ);
+       mpa_frame->flags = IETF_MPA_FLAGS_CRC;
+       mpa_frame->rev =  IETF_MPA_VERSION;
+       mpa_frame->priv_data_len = htons(private_data_len);
 
        /* set our node side to client (active) side */
        cm_node->tcp_cntxt.client = 1;
        cm_node->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
 
        if (cm_info->loc_addr == cm_info->rem_addr) {
-               loopbackremotelistener = find_listener(cm_core, cm_node->rem_addr,
-                               cm_node->rem_port, NES_CM_LISTENER_ACTIVE_STATE);
+               loopbackremotelistener = find_listener(cm_core,
+                               ntohl(nesvnic->local_ipaddr), cm_node->rem_port,
+                               NES_CM_LISTENER_ACTIVE_STATE);
                if (loopbackremotelistener == NULL) {
                        create_event(cm_node, NES_CM_EVENT_ABORTED);
                } else {
@@ -1683,26 +1864,35 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
                        loopback_cm_info.loc_port = cm_info->rem_port;
                        loopback_cm_info.rem_port = cm_info->loc_port;
                        loopback_cm_info.cm_id = loopbackremotelistener->cm_id;
-                       loopbackremotenode = make_cm_node(cm_core, nesvnic, &loopback_cm_info,
-                                       loopbackremotelistener);
+                       loopbackremotenode = make_cm_node(cm_core, nesvnic,
+                               &loopback_cm_info, loopbackremotelistener);
                        loopbackremotenode->loopbackpartner = cm_node;
-                       loopbackremotenode->tcp_cntxt.rcv_wscale = NES_CM_DEFAULT_RCV_WND_SCALE;
+                       loopbackremotenode->tcp_cntxt.rcv_wscale =
+                               NES_CM_DEFAULT_RCV_WND_SCALE;
                        cm_node->loopbackpartner = loopbackremotenode;
-                       memcpy(loopbackremotenode->mpa_frame_buf, &mpa_frame->priv_data,
-                                       mpa_frame_size);
-                       loopbackremotenode->mpa_frame_size = mpa_frame_size -
-                                       sizeof(struct ietf_mpa_frame);
+                       memcpy(loopbackremotenode->mpa_frame_buf, private_data,
+                               private_data_len);
+                       loopbackremotenode->mpa_frame_size = private_data_len;
 
-                       /* we are done handling this state, set node to a TSA state */
+                       /* we are done handling this state. */
+                       /* set node to a TSA state */
                        cm_node->state = NES_CM_STATE_TSA;
-                       cm_node->tcp_cntxt.rcv_nxt = loopbackremotenode->tcp_cntxt.loc_seq_num;
-                       loopbackremotenode->tcp_cntxt.rcv_nxt = cm_node->tcp_cntxt.loc_seq_num;
-                       cm_node->tcp_cntxt.max_snd_wnd = loopbackremotenode->tcp_cntxt.rcv_wnd;
-                       loopbackremotenode->tcp_cntxt.max_snd_wnd = cm_node->tcp_cntxt.rcv_wnd;
-                       cm_node->tcp_cntxt.snd_wnd = loopbackremotenode->tcp_cntxt.rcv_wnd;
-                       loopbackremotenode->tcp_cntxt.snd_wnd = cm_node->tcp_cntxt.rcv_wnd;
-                       cm_node->tcp_cntxt.snd_wscale = loopbackremotenode->tcp_cntxt.rcv_wscale;
-                       loopbackremotenode->tcp_cntxt.snd_wscale = cm_node->tcp_cntxt.rcv_wscale;
+                       cm_node->tcp_cntxt.rcv_nxt =
+                               loopbackremotenode->tcp_cntxt.loc_seq_num;
+                       loopbackremotenode->tcp_cntxt.rcv_nxt =
+                               cm_node->tcp_cntxt.loc_seq_num;
+                       cm_node->tcp_cntxt.max_snd_wnd =
+                               loopbackremotenode->tcp_cntxt.rcv_wnd;
+                       loopbackremotenode->tcp_cntxt.max_snd_wnd =
+                               cm_node->tcp_cntxt.rcv_wnd;
+                       cm_node->tcp_cntxt.snd_wnd =
+                               loopbackremotenode->tcp_cntxt.rcv_wnd;
+                       loopbackremotenode->tcp_cntxt.snd_wnd =
+                               cm_node->tcp_cntxt.rcv_wnd;
+                       cm_node->tcp_cntxt.snd_wscale =
+                               loopbackremotenode->tcp_cntxt.rcv_wscale;
+                       loopbackremotenode->tcp_cntxt.snd_wscale =
+                               cm_node->tcp_cntxt.rcv_wscale;
 
                        create_event(loopbackremotenode, NES_CM_EVENT_MPA_REQ);
                }
@@ -1712,16 +1902,29 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
        /* set our node side to client (active) side */
        cm_node->tcp_cntxt.client = 1;
        /* init our MPA frame ptr */
-       memcpy(&cm_node->mpa_frame, mpa_frame, mpa_frame_size);
+       memcpy(mpa_frame->priv_data, private_data, private_data_len);
+
        cm_node->mpa_frame_size = mpa_frame_size;
 
        /* send a syn and goto syn sent state */
        cm_node->state = NES_CM_STATE_SYN_SENT;
-       ret = send_syn(cm_node, 0);
+       ret = send_syn(cm_node, 0, NULL);
+
+       if (ret) {
+               /* error in sending the syn free up the cm_node struct */
+               nes_debug(NES_DBG_CM, "Api - connect() FAILED: dest "
+                       "addr=0x%08X, port=0x%04x, cm_node=%p, cm_id = %p.\n",
+                       cm_node->rem_addr, cm_node->rem_port, cm_node,
+                       cm_node->cm_id);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
+               cm_node = NULL;
+       }
 
-       nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X, port=0x%04x,"
-                       " cm_node=%p, cm_id = %p.\n",
-                       cm_node->rem_addr, cm_node->rem_port, cm_node, cm_node->cm_id);
+       if (cm_node)
+               nes_debug(NES_DBG_CM, "Api - connect(): dest addr=0x%08X,"
+                       "port=0x%04x, cm_node=%p, cm_id = %p.\n",
+                       cm_node->rem_addr, cm_node->rem_port, cm_node,
+                       cm_node->cm_id);
 
        return cm_node;
 }
@@ -1731,8 +1934,8 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
  * mini_cm_accept - accept a connection
  * This function is never called
  */
-static int mini_cm_accept(struct nes_cm_core *cm_core, struct ietf_mpa_frame *mpa_frame,
-                         struct nes_cm_node *cm_node)
+static int mini_cm_accept(struct nes_cm_core *cm_core,
+       struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
 {
        return 0;
 }
@@ -1742,32 +1945,26 @@ static int mini_cm_accept(struct nes_cm_core *cm_core, struct ietf_mpa_frame *mp
  * mini_cm_reject - reject and teardown a connection
  */
 static int mini_cm_reject(struct nes_cm_core *cm_core,
-                         struct ietf_mpa_frame *mpa_frame,
-                         struct nes_cm_node *cm_node)
+       struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
 {
        int ret = 0;
-       struct sk_buff *skb;
-       u16 mpa_frame_size = sizeof(struct ietf_mpa_frame) +
-                       ntohs(mpa_frame->priv_data_len);
 
-       skb = get_free_pkt(cm_node);
-       if (!skb) {
-               nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
-               return -1;
-       }
-
-       /* send an MPA Request frame */
-       form_cm_frame(skb, cm_node, NULL, 0, mpa_frame, mpa_frame_size, SET_ACK | SET_FIN);
-       ret = schedule_nes_timer(cm_node, skb, NES_TIMER_TYPE_SEND, 1, 0);
+       nes_debug(NES_DBG_CM, "%s cm_node=%p type=%d state=%d\n",
+               __func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
 
+       if (cm_node->tcp_cntxt.client)
+               return ret;
+       cleanup_retrans_entry(cm_node);
        cm_node->state = NES_CM_STATE_CLOSED;
        ret = send_fin(cm_node, NULL);
 
-       if (ret < 0) {
-               printk(KERN_INFO PFX "failed to send MPA Reply (reject)\n");
-               return ret;
+       if (cm_node->accept_pend) {
+               BUG_ON(!cm_node->listener);
+               atomic_dec(&cm_node->listener->pend_accepts_cnt);
+               BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
        }
 
+       ret = send_reset(cm_node, NULL);
        return ret;
 }
 
@@ -1783,35 +1980,39 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
                return -EINVAL;
 
        switch (cm_node->state) {
-               /* if passed in node is null, create a reference key node for node search */
-               /* check if we found an owner node for this pkt */
-               case NES_CM_STATE_SYN_RCVD:
-               case NES_CM_STATE_SYN_SENT:
-               case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
-               case NES_CM_STATE_ESTABLISHED:
-               case NES_CM_STATE_ACCEPTING:
-               case NES_CM_STATE_MPAREQ_SENT:
-                       cm_node->state = NES_CM_STATE_FIN_WAIT1;
-                       send_fin(cm_node, NULL);
-                       break;
-               case NES_CM_STATE_CLOSE_WAIT:
-                       cm_node->state = NES_CM_STATE_LAST_ACK;
-                       send_fin(cm_node, NULL);
-                       break;
-               case NES_CM_STATE_FIN_WAIT1:
-               case NES_CM_STATE_FIN_WAIT2:
-               case NES_CM_STATE_LAST_ACK:
-               case NES_CM_STATE_TIME_WAIT:
-               case NES_CM_STATE_CLOSING:
-                       ret = -1;
-                       break;
-               case NES_CM_STATE_LISTENING:
-               case NES_CM_STATE_UNKNOWN:
-               case NES_CM_STATE_INITED:
-               case NES_CM_STATE_CLOSED:
-               case NES_CM_STATE_TSA:
-                       ret = rem_ref_cm_node(cm_core, cm_node);
-                       break;
+       case NES_CM_STATE_SYN_RCVD:
+       case NES_CM_STATE_SYN_SENT:
+       case NES_CM_STATE_ONE_SIDE_ESTABLISHED:
+       case NES_CM_STATE_ESTABLISHED:
+       case NES_CM_STATE_ACCEPTING:
+       case NES_CM_STATE_MPAREQ_SENT:
+       case NES_CM_STATE_MPAREQ_RCVD:
+               cleanup_retrans_entry(cm_node);
+               send_reset(cm_node, NULL);
+               break;
+       case NES_CM_STATE_CLOSE_WAIT:
+               cm_node->state = NES_CM_STATE_LAST_ACK;
+               send_fin(cm_node, NULL);
+               break;
+       case NES_CM_STATE_FIN_WAIT1:
+       case NES_CM_STATE_FIN_WAIT2:
+       case NES_CM_STATE_LAST_ACK:
+       case NES_CM_STATE_TIME_WAIT:
+       case NES_CM_STATE_CLOSING:
+               ret = -1;
+               break;
+       case NES_CM_STATE_LISTENING:
+       case NES_CM_STATE_UNKNOWN:
+       case NES_CM_STATE_INITED:
+       case NES_CM_STATE_CLOSED:
+               ret = rem_ref_cm_node(cm_core, cm_node);
+               break;
+       case NES_CM_STATE_TSA:
+               if (cm_node->send_entry)
+                       printk(KERN_ERR "ERROR Close got called from STATE_TSA "
+                               "send_entry=%p\n", cm_node->send_entry);
+               ret = rem_ref_cm_node(cm_core, cm_node);
+               break;
        }
        cm_node->cm_id = NULL;
        return ret;
@@ -1822,25 +2023,30 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
  * recv_pkt - recv an ETHERNET packet, and process it through CM
  * node state machine
  */
-static int mini_cm_recv_pkt(struct nes_cm_core *cm_core, struct nes_vnic *nesvnic,
-                           struct sk_buff *skb)
+static void mini_cm_recv_pkt(struct nes_cm_core *cm_core,
+       struct nes_vnic *nesvnic, struct sk_buff *skb)
 {
        struct nes_cm_node *cm_node = NULL;
        struct nes_cm_listener *listener = NULL;
        struct iphdr *iph;
        struct tcphdr *tcph;
        struct nes_cm_info nfo;
-       int ret = 0;
 
-       if (!skb || skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
-               ret = -EINVAL;
-               goto out;
+       if (!skb)
+               return;
+       if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
+               dev_kfree_skb_any(skb);
+               return;
        }
 
        iph = (struct iphdr *)skb->data;
        tcph = (struct tcphdr *)(skb->data + sizeof(struct iphdr));
        skb_reset_network_header(skb);
        skb_set_transport_header(skb, sizeof(*tcph));
+       if (!tcph) {
+               dev_kfree_skb_any(skb);
+               return;
+       }
        skb->len = ntohs(iph->tot_len);
 
        nfo.loc_addr = ntohl(iph->daddr);
@@ -1853,61 +2059,60 @@ static int mini_cm_recv_pkt(struct nes_cm_core *cm_core, struct nes_vnic *nesvni
                  NIPQUAD(iph->daddr), tcph->dest,
                  NIPQUAD(iph->saddr), tcph->source);
 
-       /* note: this call is going to increment cm_node ref count */
-       cm_node = find_node(cm_core,
+       do {
+               cm_node = find_node(cm_core,
                        nfo.rem_port, nfo.rem_addr,
                        nfo.loc_port, nfo.loc_addr);
 
-       if (!cm_node) {
-               listener = find_listener(cm_core, nfo.loc_addr, nfo.loc_port,
-                               NES_CM_LISTENER_ACTIVE_STATE);
-               if (listener) {
-                       nfo.cm_id = listener->cm_id;
-                       nfo.conn_type = listener->conn_type;
-               } else {
-                       nfo.cm_id = NULL;
-                       nfo.conn_type = 0;
-               }
-
-               cm_node = make_cm_node(cm_core, nesvnic, &nfo, listener);
                if (!cm_node) {
-                       nes_debug(NES_DBG_CM, "Unable to allocate node\n");
+                       /* Only type of packet accepted are for */
+                       /* the PASSIVE open (syn only) */
+                       if ((!tcph->syn) || (tcph->ack)) {
+                               cm_packets_dropped++;
+                               break;
+                       }
+                       listener = find_listener(cm_core, nfo.loc_addr,
+                               nfo.loc_port,
+                               NES_CM_LISTENER_ACTIVE_STATE);
                        if (listener) {
-                               nes_debug(NES_DBG_CM, "unable to allocate node and decrementing listener refcount\n");
+                               nfo.cm_id = listener->cm_id;
+                               nfo.conn_type = listener->conn_type;
+                       } else {
+                               nes_debug(NES_DBG_CM, "Unable to find listener "
+                                       "for the pkt\n");
+                               cm_packets_dropped++;
+                               dev_kfree_skb_any(skb);
+                               break;
+                       }
+
+                       cm_node = make_cm_node(cm_core, nesvnic, &nfo,
+                               listener);
+                       if (!cm_node) {
+                               nes_debug(NES_DBG_CM, "Unable to allocate "
+                                       "node\n");
+                               cm_packets_dropped++;
                                atomic_dec(&listener->ref_count);
+                               dev_kfree_skb_any(skb);
+                               break;
                        }
-                       ret = -1;
-                       goto out;
-               }
-               if (!listener) {
-                       nes_debug(NES_DBG_CM, "Packet found for unknown port %x refcnt=%d\n",
-                                       nfo.loc_port, atomic_read(&cm_node->ref_count));
-                       if (!tcph->rst) {
-                               nes_debug(NES_DBG_CM, "Packet found for unknown port=%d"
-                                               " rem_port=%d refcnt=%d\n",
-                                               nfo.loc_port, nfo.rem_port, atomic_read(&cm_node->ref_count));
-
-                               cm_node->tcp_cntxt.rcv_nxt = ntohl(tcph->seq);
-                               cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
-                               send_reset(cm_node);
+                       if (!tcph->rst && !tcph->fin) {
+                               cm_node->state = NES_CM_STATE_LISTENING;
+                       } else {
+                               cm_packets_dropped++;
+                               rem_ref_cm_node(cm_core, cm_node);
+                               dev_kfree_skb_any(skb);
+                               break;
                        }
+                       add_ref_cm_node(cm_node);
+               } else if (cm_node->state == NES_CM_STATE_TSA) {
                        rem_ref_cm_node(cm_core, cm_node);
-                       ret = -1;
-                       goto out;
+                       atomic_inc(&cm_accel_dropped_pkts);
+                       dev_kfree_skb_any(skb);
+                       break;
                }
-               add_ref_cm_node(cm_node);
-               cm_node->state = NES_CM_STATE_LISTENING;
-       }
-
-       nes_debug(NES_DBG_CM, "Processing Packet for node %p, data = (%p):\n",
-                       cm_node, skb->data);
-       process_packet(cm_node, skb, cm_core);
-
-       rem_ref_cm_node(cm_core, cm_node);
-       out:
-       if (skb)
-               dev_kfree_skb_any(skb);
-       return ret;
+               process_packet(cm_node, skb, cm_core);
+               rem_ref_cm_node(cm_core, cm_node);
+       } while (0);
 }
 
 
@@ -2107,15 +2312,12 @@ int nes_cm_disconn(struct nes_qp *nesqp)
        if (nesqp->disconn_pending == 0) {
                nesqp->disconn_pending++;
                spin_unlock_irqrestore(&nesqp->lock, flags);
-               /* nes_add_ref(&nesqp->ibqp); */
                /* init our disconnect work element, to */
                INIT_WORK(&nesqp->disconn_work, nes_disconnect_worker);
 
                queue_work(g_cm_core->disconn_wq, &nesqp->disconn_work);
-       } else {
+       } else
                spin_unlock_irqrestore(&nesqp->lock, flags);
-               nes_rem_ref(&nesqp->ibqp);
-       }
 
        return 0;
 }
@@ -2161,7 +2363,6 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
                nes_debug(NES_DBG_CM, "QP%u disconnect_worker cmid is NULL\n",
                                nesqp->hwqp.qp_id);
                spin_unlock_irqrestore(&nesqp->lock, flags);
-               nes_rem_ref(&nesqp->ibqp);
                return -1;
        }
 
@@ -2182,30 +2383,31 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
                        atomic_inc(&cm_disconnects);
                        cm_event.event = IW_CM_EVENT_DISCONNECT;
                        if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) {
-                               issued_disconnect_reset = 1;
                                cm_event.status = IW_CM_EVENT_STATUS_RESET;
-                               nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event (status reset) for "
-                                               " QP%u, cm_id = %p. \n",
-                                               nesqp->hwqp.qp_id, cm_id);
-                       } else {
+                               nes_debug(NES_DBG_CM, "Generating a CM "
+                                       "Disconnect Event (status reset) for "
+                                       "QP%u, cm_id = %p. \n",
+                                       nesqp->hwqp.qp_id, cm_id);
+                       } else
                                cm_event.status = IW_CM_EVENT_STATUS_OK;
-                       }
 
                        cm_event.local_addr = cm_id->local_addr;
                        cm_event.remote_addr = cm_id->remote_addr;
                        cm_event.private_data = NULL;
                        cm_event.private_data_len = 0;
 
-                       nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event for "
-                                       " QP%u, SQ Head = %u, SQ Tail = %u. cm_id = %p, refcount = %u.\n",
-                                       nesqp->hwqp.qp_id,
-                                       nesqp->hwqp.sq_head, nesqp->hwqp.sq_tail, cm_id,
-                                       atomic_read(&nesqp->refcount));
+                       nes_debug(NES_DBG_CM, "Generating a CM Disconnect Event"
+                               " for  QP%u, SQ Head = %u, SQ Tail = %u. "
+                               "cm_id = %p, refcount = %u.\n",
+                               nesqp->hwqp.qp_id, nesqp->hwqp.sq_head,
+                               nesqp->hwqp.sq_tail, cm_id,
+                               atomic_read(&nesqp->refcount));
 
                        spin_unlock_irqrestore(&nesqp->lock, flags);
                        ret = cm_id->event_handler(cm_id, &cm_event);
                        if (ret)
-                               nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
+                               nes_debug(NES_DBG_CM, "OFA CM event_handler "
+                                       "returned, ret=%d\n", ret);
                        spin_lock_irqsave(&nesqp->lock, flags);
                }
 
@@ -2247,31 +2449,24 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
                        if (nesqp->flush_issued == 0) {
                                nesqp->flush_issued = 1;
                                spin_unlock_irqrestore(&nesqp->lock, flags);
-                               flush_wqes(nesvnic->nesdev, nesqp, NES_CQP_FLUSH_RQ, 1);
-                       } else {
+                               flush_wqes(nesvnic->nesdev, nesqp,
+                                       NES_CQP_FLUSH_RQ, 1);
+                       } else
                                spin_unlock_irqrestore(&nesqp->lock, flags);
-                       }
-
-                       /* This reference is from either ModifyQP or the AE processing,
-                                       there is still a race here with modifyqp */
-                       nes_rem_ref(&nesqp->ibqp);
-
                } else {
                        cm_id = nesqp->cm_id;
                        spin_unlock_irqrestore(&nesqp->lock, flags);
                        /* check to see if the inbound reset beat the outbound reset */
                        if ((!cm_id) && (last_ae==NES_AEQE_AEID_RESET_SENT)) {
-                               nes_debug(NES_DBG_CM, "QP%u: Decing refcount due to inbound reset"
-                                               " beating the outbound reset.\n",
-                                               nesqp->hwqp.qp_id);
-                               nes_rem_ref(&nesqp->ibqp);
+                               nes_debug(NES_DBG_CM, "QP%u: Decing refcount "
+                                       "due to inbound reset beating the "
+                                       "outbound reset.\n", nesqp->hwqp.qp_id);
                        }
                }
        } else {
                nesqp->disconn_pending = 0;
                spin_unlock_irqrestore(&nesqp->lock, flags);
        }
-       nes_rem_ref(&nesqp->ibqp);
 
        return 0;
 }
@@ -2349,71 +2544,82 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        nesdev = nesvnic->nesdev;
        adapter = nesdev->nesadapter;
 
-       nes_debug(NES_DBG_CM, "nesvnic=%p, netdev=%p, %s\n",
-                       nesvnic, nesvnic->netdev, nesvnic->netdev->name);
-
-       /* since this is from a listen, we were able to put node handle into cm_id */
        cm_node = (struct nes_cm_node *)cm_id->provider_data;
+       nes_debug(NES_DBG_CM, "nes_accept: cm_node= %p nesvnic=%p, netdev=%p,"
+               "%s\n", cm_node, nesvnic, nesvnic->netdev,
+               nesvnic->netdev->name);
 
        /* associate the node with the QP */
        nesqp->cm_node = (void *)cm_node;
+       cm_node->nesqp = nesqp;
+       nes_add_ref(&nesqp->ibqp);
 
-       nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu\n",
-                       nesqp->hwqp.qp_id, cm_node, jiffies);
+       nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu listener = %p\n",
+               nesqp->hwqp.qp_id, cm_node, jiffies, cm_node->listener);
        atomic_inc(&cm_accepts);
 
        nes_debug(NES_DBG_CM, "netdev refcnt = %u.\n",
                        atomic_read(&nesvnic->netdev->refcnt));
 
-               /* allocate the ietf frame and space for private data */
-               nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev,
-                               sizeof(struct ietf_mpa_frame) + conn_param->private_data_len,
-                               &nesqp->ietf_frame_pbase);
-
-               if (!nesqp->ietf_frame) {
-                       nes_debug(NES_DBG_CM, "Unable to allocate memory for private data\n");
-                       return -ENOMEM;
-               }
+       /* allocate the ietf frame and space for private data */
+       nesqp->ietf_frame = pci_alloc_consistent(nesdev->pcidev,
+               sizeof(struct ietf_mpa_frame) + conn_param->private_data_len,
+               &nesqp->ietf_frame_pbase);
 
+       if (!nesqp->ietf_frame) {
+               nes_debug(NES_DBG_CM, "Unable to allocate memory for private "
+                       "data\n");
+               return -ENOMEM;
+       }
 
-               /* setup the MPA frame */
-               nesqp->private_data_len = conn_param->private_data_len;
-               memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
 
-               memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
-                               conn_param->private_data_len);
+       /* setup the MPA frame */
+       nesqp->private_data_len = conn_param->private_data_len;
+       memcpy(nesqp->ietf_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
 
-               nesqp->ietf_frame->priv_data_len = cpu_to_be16(conn_param->private_data_len);
-               nesqp->ietf_frame->rev = mpa_version;
-               nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
+       memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
+                       conn_param->private_data_len);
 
-               /* setup our first outgoing iWarp send WQE (the IETF frame response) */
-               wqe = &nesqp->hwqp.sq_vbase[0];
+       nesqp->ietf_frame->priv_data_len =
+               cpu_to_be16(conn_param->private_data_len);
+       nesqp->ietf_frame->rev = mpa_version;
+       nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
 
-               if (cm_id->remote_addr.sin_addr.s_addr != cm_id->local_addr.sin_addr.s_addr) {
-                       u64temp = (unsigned long)nesqp;
-                       u64temp |= NES_SW_CONTEXT_ALIGN>>1;
-                       set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
-                                           u64temp);
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
-                                       cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING | NES_IWARP_SQ_WQE_WRPDU);
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
-                                       cpu_to_le32(conn_param->private_data_len + sizeof(struct ietf_mpa_frame));
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] =
-                                       cpu_to_le32((u32)nesqp->ietf_frame_pbase);
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] =
-                                       cpu_to_le32((u32)((u64)nesqp->ietf_frame_pbase >> 32));
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] =
-                                       cpu_to_le32(conn_param->private_data_len + sizeof(struct ietf_mpa_frame));
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
-
-                       nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(
-                                       NES_QPCONTEXT_ORDIRD_LSMM_PRESENT | NES_QPCONTEXT_ORDIRD_WRPDU);
-               } else {
-                       nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
-                                       NES_QPCONTEXT_ORDIRD_WRPDU | NES_QPCONTEXT_ORDIRD_ALSMM));
-               }
-               nesqp->skip_lsmm = 1;
+       /* setup our first outgoing iWarp send WQE (the IETF frame response) */
+       wqe = &nesqp->hwqp.sq_vbase[0];
+
+       if (cm_id->remote_addr.sin_addr.s_addr !=
+                       cm_id->local_addr.sin_addr.s_addr) {
+               u64temp = (unsigned long)nesqp;
+               u64temp |= NES_SW_CONTEXT_ALIGN>>1;
+               set_wqe_64bit_value(wqe->wqe_words,
+                       NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
+                       u64temp);
+               wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+                       cpu_to_le32(NES_IWARP_SQ_WQE_STREAMING |
+                       NES_IWARP_SQ_WQE_WRPDU);
+               wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] =
+                       cpu_to_le32(conn_param->private_data_len +
+                       sizeof(struct ietf_mpa_frame));
+               wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] =
+                       cpu_to_le32((u32)nesqp->ietf_frame_pbase);
+               wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] =
+                       cpu_to_le32((u32)((u64)nesqp->ietf_frame_pbase >> 32));
+               wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] =
+                       cpu_to_le32(conn_param->private_data_len +
+                       sizeof(struct ietf_mpa_frame));
+               wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+
+               nesqp->nesqp_context->ird_ord_sizes |=
+                       cpu_to_le32(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+                       NES_QPCONTEXT_ORDIRD_WRPDU);
+       } else {
+               nesqp->nesqp_context->ird_ord_sizes |=
+                       cpu_to_le32((NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+                       NES_QPCONTEXT_ORDIRD_WRPDU |
+                       NES_QPCONTEXT_ORDIRD_ALSMM));
+       }
+       nesqp->skip_lsmm = 1;
 
 
        /* Cache the cm_id in the qp */
@@ -2424,55 +2630,75 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        cm_id->provider_data = nesqp;
        nesqp->active_conn   = 0;
 
+       if (cm_node->state == NES_CM_STATE_TSA)
+               nes_debug(NES_DBG_CM, "Already state = TSA for cm_node=%p\n",
+                       cm_node);
+
        nes_cm_init_tsa_conn(nesqp, cm_node);
 
-       nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(cm_id->local_addr.sin_port));
-       nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(cm_id->remote_addr.sin_port));
-       nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
+       nesqp->nesqp_context->tcpPorts[0] =
+               cpu_to_le16(ntohs(cm_id->local_addr.sin_port));
+       nesqp->nesqp_context->tcpPorts[1] =
+               cpu_to_le16(ntohs(cm_id->remote_addr.sin_port));
+
+       if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr))
+               nesqp->nesqp_context->ip0 =
+                       cpu_to_le32(ntohl(nesvnic->local_ipaddr));
+       else
+               nesqp->nesqp_context->ip0 =
+                       cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
 
        nesqp->nesqp_context->misc2 |= cpu_to_le32(
-                       (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
+                       (u32)PCI_FUNC(nesdev->pcidev->devfn) <<
+                       NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
 
-       nesqp->nesqp_context->arp_index_vlan |= cpu_to_le32(
-                       nes_arp_table(nesdev, le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
+       nesqp->nesqp_context->arp_index_vlan |=
+               cpu_to_le32(nes_arp_table(nesdev,
+                       le32_to_cpu(nesqp->nesqp_context->ip0), NULL,
                        NES_ARP_RESOLVE) << 16);
 
        nesqp->nesqp_context->ts_val_delta = cpu_to_le32(
-                       jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
+               jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
 
        nesqp->nesqp_context->ird_index = cpu_to_le32(nesqp->hwqp.qp_id);
 
        nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32(
-                       ((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT));
-       nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
+               ((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT));
+       nesqp->nesqp_context->ird_ord_sizes |=
+               cpu_to_le32((u32)conn_param->ord);
 
        memset(&nes_quad, 0, sizeof(nes_quad));
-       nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
-       nes_quad.SrcIpadr      = cm_id->remote_addr.sin_addr.s_addr;
-       nes_quad.TcpPorts[0]   = cm_id->remote_addr.sin_port;
-       nes_quad.TcpPorts[1]   = cm_id->local_addr.sin_port;
+       nes_quad.DstIpAdrIndex =
+               cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
+       if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr))
+               nes_quad.SrcIpadr = nesvnic->local_ipaddr;
+       else
+               nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr;
+       nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port;
+       nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port;
 
        /* Produce hash key */
        crc_value = get_crc_value(&nes_quad);
        nesqp->hte_index = cpu_to_be32(crc_value ^ 0xffffffff);
        nes_debug(NES_DBG_CM, "HTE Index = 0x%08X, CRC = 0x%08X\n",
-                       nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
+               nesqp->hte_index, nesqp->hte_index & adapter->hte_index_mask);
 
        nesqp->hte_index &= adapter->hte_index_mask;
        nesqp->nesqp_context->hte_index = cpu_to_le32(nesqp->hte_index);
 
        cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
 
-       nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = 0x%08X:0x%04X,"
-                       " rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + private data length=%zu.\n",
-                       nesqp->hwqp.qp_id,
+       nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = "
+                       "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + "
+                       "private data length=%zu.\n", nesqp->hwqp.qp_id,
                        ntohl(cm_id->remote_addr.sin_addr.s_addr),
                        ntohs(cm_id->remote_addr.sin_port),
                        ntohl(cm_id->local_addr.sin_addr.s_addr),
                        ntohs(cm_id->local_addr.sin_port),
                        le32_to_cpu(nesqp->nesqp_context->rcv_nxt),
                        le32_to_cpu(nesqp->nesqp_context->snd_nxt),
-                       conn_param->private_data_len+sizeof(struct ietf_mpa_frame));
+                       conn_param->private_data_len +
+                       sizeof(struct ietf_mpa_frame));
 
        attr.qp_state = IB_QPS_RTS;
        nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
@@ -2489,15 +2715,16 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        cm_event.private_data_len = 0;
        ret = cm_id->event_handler(cm_id, &cm_event);
        if (cm_node->loopbackpartner) {
-               cm_node->loopbackpartner->mpa_frame_size = nesqp->private_data_len;
+               cm_node->loopbackpartner->mpa_frame_size =
+                       nesqp->private_data_len;
                /* copy entire MPA frame to our cm_node's frame */
-               memcpy(cm_node->loopbackpartner->mpa_frame_buf, nesqp->ietf_frame->priv_data,
-                          nesqp->private_data_len);
+               memcpy(cm_node->loopbackpartner->mpa_frame_buf,
+                       nesqp->ietf_frame->priv_data, nesqp->private_data_len);
                create_event(cm_node->loopbackpartner, NES_CM_EVENT_CONNECTED);
        }
        if (ret)
-               printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
-                               __func__, __LINE__, ret);
+               printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
+                       "ret=%d\n", __func__, __LINE__, ret);
 
        return 0;
 }
@@ -2555,74 +2782,61 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        if (!nesdev)
                return -EINVAL;
 
-       atomic_inc(&cm_connects);
-
-       nesqp->ietf_frame = kzalloc(sizeof(struct ietf_mpa_frame) +
-                       conn_param->private_data_len, GFP_KERNEL);
-       if (!nesqp->ietf_frame)
-               return -ENOMEM;
+       nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = "
+               "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
+               ntohl(nesvnic->local_ipaddr),
+               ntohl(cm_id->remote_addr.sin_addr.s_addr),
+               ntohs(cm_id->remote_addr.sin_port),
+               ntohl(cm_id->local_addr.sin_addr.s_addr),
+               ntohs(cm_id->local_addr.sin_port));
 
-       /* set qp as having an active connection */
+       atomic_inc(&cm_connects);
        nesqp->active_conn = 1;
 
-       nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = 0x%08X:0x%04X.\n",
-                       nesqp->hwqp.qp_id,
-                       ntohl(cm_id->remote_addr.sin_addr.s_addr),
-                       ntohs(cm_id->remote_addr.sin_port),
-                       ntohl(cm_id->local_addr.sin_addr.s_addr),
-                       ntohs(cm_id->local_addr.sin_port));
-
        /* cache the cm_id in the qp */
        nesqp->cm_id = cm_id;
 
        cm_id->provider_data = nesqp;
 
-       /* copy the private data */
-       if (conn_param->private_data_len) {
-               memcpy(nesqp->ietf_frame->priv_data, conn_param->private_data,
-                               conn_param->private_data_len);
-       }
-
        nesqp->private_data_len = conn_param->private_data_len;
        nesqp->nesqp_context->ird_ord_sizes |= cpu_to_le32((u32)conn_param->ord);
        nes_debug(NES_DBG_CM, "requested ord = 0x%08X.\n", (u32)conn_param->ord);
-       nes_debug(NES_DBG_CM, "mpa private data len =%u\n", conn_param->private_data_len);
-
-       strcpy(&nesqp->ietf_frame->key[0], IEFT_MPA_KEY_REQ);
-       nesqp->ietf_frame->flags = IETF_MPA_FLAGS_CRC;
-       nesqp->ietf_frame->rev = IETF_MPA_VERSION;
-       nesqp->ietf_frame->priv_data_len = htons(conn_param->private_data_len);
+       nes_debug(NES_DBG_CM, "mpa private data len =%u\n",
+               conn_param->private_data_len);
 
-       if (cm_id->local_addr.sin_addr.s_addr != cm_id->remote_addr.sin_addr.s_addr)
+       if (cm_id->local_addr.sin_addr.s_addr !=
+               cm_id->remote_addr.sin_addr.s_addr)
                nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
-                               PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+                       PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
 
        /* set up the connection params for the node */
-       cm_info.loc_addr = (cm_id->local_addr.sin_addr.s_addr);
-       cm_info.loc_port = (cm_id->local_addr.sin_port);
-       cm_info.rem_addr = (cm_id->remote_addr.sin_addr.s_addr);
-       cm_info.rem_port = (cm_id->remote_addr.sin_port);
+       cm_info.loc_addr = htonl(cm_id->local_addr.sin_addr.s_addr);
+       cm_info.loc_port = htons(cm_id->local_addr.sin_port);
+       cm_info.rem_addr = htonl(cm_id->remote_addr.sin_addr.s_addr);
+       cm_info.rem_port = htons(cm_id->remote_addr.sin_port);
        cm_info.cm_id = cm_id;
        cm_info.conn_type = NES_CM_IWARP_CONN_TYPE;
 
        cm_id->add_ref(cm_id);
-       nes_add_ref(&nesqp->ibqp);
 
        /* create a connect CM node connection */
-       cm_node = g_cm_core->api->connect(g_cm_core, nesvnic, nesqp->ietf_frame, &cm_info);
+       cm_node = g_cm_core->api->connect(g_cm_core, nesvnic,
+               conn_param->private_data_len, (void *)conn_param->private_data,
+               &cm_info);
        if (!cm_node) {
-               if (cm_id->local_addr.sin_addr.s_addr != cm_id->remote_addr.sin_addr.s_addr)
+               if (cm_id->local_addr.sin_addr.s_addr !=
+                               cm_id->remote_addr.sin_addr.s_addr)
                        nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
-                                       PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL);
-               nes_rem_ref(&nesqp->ibqp);
-               kfree(nesqp->ietf_frame);
-               nesqp->ietf_frame = NULL;
+                               PCI_FUNC(nesdev->pcidev->devfn),
+                               NES_MANAGE_APBVT_DEL);
+
                cm_id->rem_ref(cm_id);
                return -ENOMEM;
        }
 
        cm_node->apbvt_set = 1;
        nesqp->cm_node = cm_node;
+       cm_node->nesqp = nesqp;
 
        return 0;
 }
@@ -2664,7 +2878,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
 
        cm_node = g_cm_core->api->listen(g_cm_core, nesvnic, &cm_info);
        if (!cm_node) {
-               printk("%s[%u] Error returned from listen API call\n",
+               printk(KERN_ERR "%s[%u] Error returned from listen API call\n",
                                __func__, __LINE__);
                return -ENOMEM;
        }
@@ -2672,10 +2886,13 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
        cm_id->provider_data = cm_node;
 
        if (!cm_node->reused_node) {
-               err = nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
-                               PCI_FUNC(nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+               err = nes_manage_apbvt(nesvnic,
+                       ntohs(cm_id->local_addr.sin_port),
+                       PCI_FUNC(nesvnic->nesdev->pcidev->devfn),
+                       NES_MANAGE_APBVT_ADD);
                if (err) {
-                       printk("nes_manage_apbvt call returned %d.\n", err);
+                       printk(KERN_ERR "nes_manage_apbvt call returned %d.\n",
+                               err);
                        g_cm_core->api->stop_listener(g_cm_core, (void *)cm_node);
                        return err;
                }
@@ -2795,53 +3012,70 @@ static void cm_event_connected(struct nes_cm_event *event)
        nes_cm_init_tsa_conn(nesqp, cm_node);
 
        /* set the QP tsa context */
-       nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(cm_id->local_addr.sin_port));
-       nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(cm_id->remote_addr.sin_port));
-       nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
+       nesqp->nesqp_context->tcpPorts[0] =
+               cpu_to_le16(ntohs(cm_id->local_addr.sin_port));
+       nesqp->nesqp_context->tcpPorts[1] =
+               cpu_to_le16(ntohs(cm_id->remote_addr.sin_port));
+       if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr))
+               nesqp->nesqp_context->ip0 =
+                       cpu_to_le32(ntohl(nesvnic->local_ipaddr));
+       else
+               nesqp->nesqp_context->ip0 =
+                       cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr));
 
        nesqp->nesqp_context->misc2 |= cpu_to_le32(
-                       (u32)PCI_FUNC(nesdev->pcidev->devfn) << NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
+                       (u32)PCI_FUNC(nesdev->pcidev->devfn) <<
+                       NES_QPCONTEXT_MISC2_SRC_IP_SHIFT);
        nesqp->nesqp_context->arp_index_vlan |= cpu_to_le32(
-                       nes_arp_table(nesdev, le32_to_cpu(nesqp->nesqp_context->ip0),
+                       nes_arp_table(nesdev,
+                       le32_to_cpu(nesqp->nesqp_context->ip0),
                        NULL, NES_ARP_RESOLVE) << 16);
        nesqp->nesqp_context->ts_val_delta = cpu_to_le32(
                        jiffies - nes_read_indexed(nesdev, NES_IDX_TCP_NOW));
        nesqp->nesqp_context->ird_index = cpu_to_le32(nesqp->hwqp.qp_id);
        nesqp->nesqp_context->ird_ord_sizes |=
-                       cpu_to_le32((u32)1 << NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT);
+                       cpu_to_le32((u32)1 <<
+                       NES_QPCONTEXT_ORDIRD_IWARP_MODE_SHIFT);
 
        /* Adjust tail for not having a LSMM */
        nesqp->hwqp.sq_tail = 1;
 
 #if defined(NES_SEND_FIRST_WRITE)
-               if (cm_node->send_write0) {
-                       nes_debug(NES_DBG_CM, "Sending first write.\n");
-                       wqe = &nesqp->hwqp.sq_vbase[0];
-                       u64temp = (unsigned long)nesqp;
-                       u64temp |= NES_SW_CONTEXT_ALIGN>>1;
-                       set_wqe_64bit_value(wqe->wqe_words, NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX,
-                                           u64temp);
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] = cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
-                       wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
-
-                       /* use the reserved spot on the WQ for the extra first WQE */
-                       nesqp->nesqp_context->ird_ord_sizes &= cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
-                                       NES_QPCONTEXT_ORDIRD_WRPDU | NES_QPCONTEXT_ORDIRD_ALSMM));
-                       nesqp->skip_lsmm = 1;
-                       nesqp->hwqp.sq_tail = 0;
-                       nes_write32(nesdev->regs + NES_WQE_ALLOC,
-                                       (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
-               }
+       if (cm_node->send_write0) {
+               nes_debug(NES_DBG_CM, "Sending first write.\n");
+               wqe = &nesqp->hwqp.sq_vbase[0];
+               u64temp = (unsigned long)nesqp;
+               u64temp |= NES_SW_CONTEXT_ALIGN>>1;
+               set_wqe_64bit_value(wqe->wqe_words,
+                               NES_IWARP_SQ_WQE_COMP_CTX_LOW_IDX, u64temp);
+               wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
+                       cpu_to_le32(NES_IWARP_SQ_OP_RDMAW);
+               wqe->wqe_words[NES_IWARP_SQ_WQE_TOTAL_PAYLOAD_IDX] = 0;
+               wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_LOW_IDX] = 0;
+               wqe->wqe_words[NES_IWARP_SQ_WQE_FRAG0_HIGH_IDX] = 0;
+               wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
+               wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
+
+               /* use the reserved spot on the WQ for the extra first WQE */
+               nesqp->nesqp_context->ird_ord_sizes &=
+                       cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
+                                               NES_QPCONTEXT_ORDIRD_WRPDU |
+                                               NES_QPCONTEXT_ORDIRD_ALSMM));
+               nesqp->skip_lsmm = 1;
+               nesqp->hwqp.sq_tail = 0;
+               nes_write32(nesdev->regs + NES_WQE_ALLOC,
+                               (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id);
+       }
 #endif
 
        memset(&nes_quad, 0, sizeof(nes_quad));
 
-       nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
-       nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr;
+       nes_quad.DstIpAdrIndex =
+               cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24);
+       if (ipv4_is_loopback(cm_id->remote_addr.sin_addr.s_addr))
+               nes_quad.SrcIpadr = nesvnic->local_ipaddr;
+       else
+               nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr;
        nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port;
        nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port;
 
@@ -2858,10 +3092,6 @@ static void cm_event_connected(struct nes_cm_event *event)
        nesqp->private_data_len = (u8) cm_node->mpa_frame_size;
        cm_node->cm_core->api->accelerated(cm_node->cm_core, cm_node);
 
-       /* modify QP state to rts */
-       attr.qp_state = IB_QPS_RTS;
-       nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
-
        /* notify OF layer we successfully created the requested connection */
        cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
        cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
@@ -2870,20 +3100,21 @@ static void cm_event_connected(struct nes_cm_event *event)
        cm_event.local_addr.sin_port = cm_id->local_addr.sin_port;
        cm_event.remote_addr = cm_id->remote_addr;
 
-               cm_event.private_data = (void *)event->cm_node->mpa_frame_buf;
-               cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size;
+       cm_event.private_data = (void *)event->cm_node->mpa_frame_buf;
+       cm_event.private_data_len = (u8) event->cm_node->mpa_frame_size;
 
        cm_event.local_addr.sin_addr.s_addr = event->cm_info.rem_addr;
        ret = cm_id->event_handler(cm_id, &cm_event);
        nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
 
        if (ret)
-               printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
-                               __func__, __LINE__, ret);
-       nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = %lu\n",
-                       nesqp->hwqp.qp_id, jiffies );
+               printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
+                       "ret=%d\n", __func__, __LINE__, ret);
+       attr.qp_state = IB_QPS_RTS;
+       nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
 
-       nes_rem_ref(&nesqp->ibqp);
+       nes_debug(NES_DBG_CM, "Exiting connect thread for QP%u. jiffies = "
+               "%lu\n", nesqp->hwqp.qp_id, jiffies);
 
        return;
 }
@@ -2927,17 +3158,19 @@ static void cm_event_connect_error(struct nes_cm_event *event)
        cm_event.private_data = NULL;
        cm_event.private_data_len = 0;
 
-       nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, remove_addr=%08x\n",
-                       cm_event.local_addr.sin_addr.s_addr, cm_event.remote_addr.sin_addr.s_addr);
+       nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, "
+               "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr,
+               cm_event.remote_addr.sin_addr.s_addr);
 
        ret = cm_id->event_handler(cm_id, &cm_event);
        nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
        if (ret)
-               printk("%s[%u] OFA CM event_handler returned, ret=%d\n",
-                               __func__, __LINE__, ret);
+               printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
+                       "ret=%d\n", __func__, __LINE__, ret);
        nes_rem_ref(&nesqp->ibqp);
-               cm_id->rem_ref(cm_id);
+       cm_id->rem_ref(cm_id);
 
+       rem_ref_cm_node(event->cm_node->cm_core, event->cm_node);
        return;
 }
 
@@ -3040,7 +3273,8 @@ static int nes_cm_post_event(struct nes_cm_event *event)
        add_ref_cm_node(event->cm_node);
        event->cm_info.cm_id->add_ref(event->cm_info.cm_id);
        INIT_WORK(&event->event_work, nes_cm_event_handler);
-       nes_debug(NES_DBG_CM, "queue_work, event=%p\n", event);
+       nes_debug(NES_DBG_CM, "cm_node=%p queue_work, event=%p\n",
+               event->cm_node, event);
 
        queue_work(event->cm_node->cm_core->event_wq, &event->event_work);
 
@@ -3056,46 +3290,48 @@ static int nes_cm_post_event(struct nes_cm_event *event)
  */
 static void nes_cm_event_handler(struct work_struct *work)
 {
-       struct nes_cm_event *event = container_of(work, struct nes_cm_event, event_work);
+       struct nes_cm_event *event = container_of(work, struct nes_cm_event,
+                       event_work);
        struct nes_cm_core *cm_core;
 
-       if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core)) {
+       if ((!event) || (!event->cm_node) || (!event->cm_node->cm_core))
                return;
-       }
+
        cm_core = event->cm_node->cm_core;
        nes_debug(NES_DBG_CM, "event=%p, event->type=%u, events posted=%u\n",
-                       event, event->type, atomic_read(&cm_core->events_posted));
+               event, event->type, atomic_read(&cm_core->events_posted));
 
        switch (event->type) {
-               case NES_CM_EVENT_MPA_REQ:
-                       cm_event_mpa_req(event);
-                       nes_debug(NES_DBG_CM, "CM Event: MPA REQUEST\n");
-                       break;
-               case NES_CM_EVENT_RESET:
-                       nes_debug(NES_DBG_CM, "CM Event: RESET\n");
-                       cm_event_reset(event);
-                       break;
-               case NES_CM_EVENT_CONNECTED:
-                       if ((!event->cm_node->cm_id) ||
-                               (event->cm_node->state != NES_CM_STATE_TSA)) {
-                               break;
-                       }
-                       cm_event_connected(event);
-                       nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n");
+       case NES_CM_EVENT_MPA_REQ:
+               cm_event_mpa_req(event);
+               nes_debug(NES_DBG_CM, "cm_node=%p CM Event: MPA REQUEST\n",
+                       event->cm_node);
+               break;
+       case NES_CM_EVENT_RESET:
+               nes_debug(NES_DBG_CM, "cm_node = %p CM Event: RESET\n",
+                       event->cm_node);
+               cm_event_reset(event);
+               break;
+       case NES_CM_EVENT_CONNECTED:
+               if ((!event->cm_node->cm_id) ||
+                       (event->cm_node->state != NES_CM_STATE_TSA))
                        break;
-               case NES_CM_EVENT_ABORTED:
-                       if ((!event->cm_node->cm_id) || (event->cm_node->state == NES_CM_STATE_TSA)) {
-                               break;
-                       }
-                       cm_event_connect_error(event);
-                       nes_debug(NES_DBG_CM, "CM Event: ABORTED\n");
-                       break;
-               case NES_CM_EVENT_DROPPED_PKT:
-                       nes_debug(NES_DBG_CM, "CM Event: DROPPED PKT\n");
-                       break;
-               default:
-                       nes_debug(NES_DBG_CM, "CM Event: UNKNOWN EVENT TYPE\n");
+               cm_event_connected(event);
+               nes_debug(NES_DBG_CM, "CM Event: CONNECTED\n");
+               break;
+       case NES_CM_EVENT_ABORTED:
+               if ((!event->cm_node->cm_id) ||
+                       (event->cm_node->state == NES_CM_STATE_TSA))
                        break;
+               cm_event_connect_error(event);
+               nes_debug(NES_DBG_CM, "CM Event: ABORTED\n");
+               break;
+       case NES_CM_EVENT_DROPPED_PKT:
+               nes_debug(NES_DBG_CM, "CM Event: DROPPED PKT\n");
+               break;
+       default:
+               nes_debug(NES_DBG_CM, "CM Event: UNKNOWN EVENT TYPE\n");
+               break;
        }
 
        atomic_dec(&cm_core->events_posted);
index 7717cb2ab50057e959095fb6a1047a8b3785a20b..367b3d29014074252424c798f110821b5ce0c7c4 100644 (file)
@@ -83,6 +83,8 @@ enum nes_timer_type {
 #define SET_FIN 4
 #define SET_RST 8
 
+#define TCP_OPTIONS_PADDING    3
+
 struct option_base {
        u8 optionnum;
        u8 length;
@@ -177,6 +179,7 @@ enum nes_cm_node_state {
        NES_CM_STATE_ESTABLISHED,
        NES_CM_STATE_ACCEPTING,
        NES_CM_STATE_MPAREQ_SENT,
+       NES_CM_STATE_MPAREQ_RCVD,
        NES_CM_STATE_TSA,
        NES_CM_STATE_FIN_WAIT1,
        NES_CM_STATE_FIN_WAIT2,
@@ -187,6 +190,16 @@ enum nes_cm_node_state {
        NES_CM_STATE_CLOSED
 };
 
+enum nes_tcpip_pkt_type {
+       NES_PKT_TYPE_UNKNOWN,
+       NES_PKT_TYPE_SYN,
+       NES_PKT_TYPE_SYNACK,
+       NES_PKT_TYPE_ACK,
+       NES_PKT_TYPE_FIN,
+       NES_PKT_TYPE_RST
+};
+
+
 /* type of nes connection */
 enum nes_cm_conn_type {
        NES_CM_IWARP_CONN_TYPE,
@@ -257,7 +270,9 @@ struct nes_cm_node {
        struct net_device         *netdev;
 
        struct nes_cm_node        *loopbackpartner;
-       struct list_head          retrans_list;
+
+       struct nes_timer_entry  *send_entry;
+
        spinlock_t                retrans_list_lock;
        struct list_head          recv_list;
        spinlock_t                recv_list_lock;
@@ -276,6 +291,8 @@ struct nes_cm_node {
        struct nes_vnic           *nesvnic;
        int                       apbvt_set;
        int                       accept_pend;
+       int                     freed;
+       struct nes_qp           *nesqp;
 };
 
 /* structure for client or CM to fill when making CM api calls. */
@@ -366,14 +383,14 @@ struct nes_cm_ops {
                        struct nes_cm_info *);
        int (*stop_listener)(struct nes_cm_core *, struct nes_cm_listener *);
        struct nes_cm_node * (*connect)(struct nes_cm_core *,
-                       struct nes_vnic *, struct ietf_mpa_frame *,
+                       struct nes_vnic *, u16, void *,
                        struct nes_cm_info *);
        int (*close)(struct nes_cm_core *, struct nes_cm_node *);
        int (*accept)(struct nes_cm_core *, struct ietf_mpa_frame *,
                        struct nes_cm_node *);
        int (*reject)(struct nes_cm_core *, struct ietf_mpa_frame *,
                        struct nes_cm_node *);
-       int (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
+       void (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
                        struct sk_buff *);
        int (*destroy_cm_core)(struct nes_cm_core *);
        int (*get)(struct nes_cm_core *);
index 85f26d19a32b26187a9289a90fa6c600e33fc0b3..1513d4066f1b965b2dc55a878f7cb28353c3e91f 100644 (file)
@@ -2814,7 +2814,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                        nesqp = *((struct nes_qp **)&context);
                        if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
                                nesqp->cm_id->add_ref(nesqp->cm_id);
-                               nes_add_ref(&nesqp->ibqp);
                                schedule_nes_timer(nesqp->cm_node, (struct sk_buff *)nesqp,
                                                NES_TIMER_TYPE_CLOSE, 1, 0);
                                nes_debug(NES_DBG_AEQ, "QP%u Not decrementing QP refcount (%d),"
@@ -2838,7 +2837,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                        if (async_event_id == NES_AEQE_AEID_RESET_SENT) {
                                tcp_state = NES_AEQE_TCP_STATE_CLOSED;
                        }
-                       nes_add_ref(&nesqp->ibqp);
                        spin_lock_irqsave(&nesqp->lock, flags);
                        nesqp->hw_iwarp_state = iwarp_state;
                        nesqp->hw_tcp_state = tcp_state;
@@ -2876,7 +2874,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                }
                                spin_unlock_irqrestore(&nesqp->lock, flags);
                                if (next_iwarp_state) {
-                                       nes_add_ref(&nesqp->ibqp);
                                        nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X,"
                                                        " also added another reference\n",
                                                        nesqp->hwqp.qp_id, next_iwarp_state);
@@ -2888,7 +2885,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                        /* FIN Received but ib state not RTS,
                                                        close complete will be on its way */
                                        spin_unlock_irqrestore(&nesqp->lock, flags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return;
                                }
                                spin_unlock_irqrestore(&nesqp->lock, flags);
@@ -2922,7 +2918,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                        if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
                                        ((nesqp->ibqp_state == IB_QPS_RTS)&&
                                        (async_event_id == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
-                               nes_add_ref(&nesqp->ibqp);
                                nes_cm_disconn(nesqp);
                        } else {
                                nesqp->in_disconnect = 0;
@@ -2931,7 +2926,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                        break;
                case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES:
                        nesqp = *((struct nes_qp **)&context);
-                       nes_add_ref(&nesqp->ibqp);
                        spin_lock_irqsave(&nesqp->lock, flags);
                        nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_ERROR;
                        nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
@@ -3042,7 +3036,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
                        }
                        /* tell cm to disconnect, cm will queue work to thread */
-                       nes_add_ref(&nesqp->ibqp);
                        nes_cm_disconn(nesqp);
                        break;
                case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE:
@@ -3062,7 +3055,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
                        }
                        /* tell cm to disconnect, cm will queue work to thread */
-                       nes_add_ref(&nesqp->ibqp);
                        nes_cm_disconn(nesqp);
                        break;
                case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR:
@@ -3082,7 +3074,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
                        }
                        /* tell cm to disconnect, cm will queue work to thread */
-                       nes_add_ref(&nesqp->ibqp);
                        nes_cm_disconn(nesqp);
                        break;
                        /* TODO: additional AEs need to be here */
index e3939d13484ef4358453c25127d8e26134c47ec6..d79942e849799341e8f676f2436dacd6be2bd793 100644 (file)
@@ -2867,7 +2867,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                        nesqp->hwqp.qp_id, attr->qp_state, nesqp->ibqp_state,
                        nesqp->iwarp_state, atomic_read(&nesqp->refcount));
 
-       nes_add_ref(&nesqp->ibqp);
        spin_lock_irqsave(&nesqp->lock, qplockflags);
 
        nes_debug(NES_DBG_MOD_QP, "QP%u: hw_iwarp_state=0x%X, hw_tcp_state=0x%X,"
@@ -2882,7 +2881,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                nesqp->hwqp.qp_id);
                                if (nesqp->iwarp_state > (u32)NES_CQP_QP_IWARP_STATE_IDLE) {
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return -EINVAL;
                                }
                                next_iwarp_state = NES_CQP_QP_IWARP_STATE_IDLE;
@@ -2893,7 +2891,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                nesqp->hwqp.qp_id);
                                if (nesqp->iwarp_state>(u32)NES_CQP_QP_IWARP_STATE_IDLE) {
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return -EINVAL;
                                }
                                next_iwarp_state = NES_CQP_QP_IWARP_STATE_IDLE;
@@ -2904,14 +2901,12 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                nesqp->hwqp.qp_id);
                                if (nesqp->iwarp_state>(u32)NES_CQP_QP_IWARP_STATE_RTS) {
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return -EINVAL;
                                }
                                if (nesqp->cm_id == NULL) {
                                        nes_debug(NES_DBG_MOD_QP, "QP%u: Failing attempt to move QP to RTS without a CM_ID. \n",
                                                        nesqp->hwqp.qp_id );
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return -EINVAL;
                                }
                                next_iwarp_state = NES_CQP_QP_IWARP_STATE_RTS;
@@ -2929,7 +2924,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                nesqp->hwqp.qp_id, nesqp->hwqp.sq_head, nesqp->hwqp.sq_tail);
                                if (nesqp->iwarp_state == (u32)NES_CQP_QP_IWARP_STATE_CLOSING) {
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return 0;
                                } else {
                                        if (nesqp->iwarp_state > (u32)NES_CQP_QP_IWARP_STATE_CLOSING) {
@@ -2937,7 +2931,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                                " ignored due to current iWARP state\n",
                                                                nesqp->hwqp.qp_id);
                                                spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                               nes_rem_ref(&nesqp->ibqp);
                                                return -EINVAL;
                                        }
                                        if (nesqp->hw_iwarp_state != NES_AEQE_IWARP_STATE_RTS) {
@@ -2969,7 +2962,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                nesqp->hwqp.qp_id);
                                if (nesqp->iwarp_state>=(u32)NES_CQP_QP_IWARP_STATE_TERMINATE) {
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return -EINVAL;
                                }
                                /* next_iwarp_state = (NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000); */
@@ -2982,7 +2974,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                        case IB_QPS_RESET:
                                if (nesqp->iwarp_state == (u32)NES_CQP_QP_IWARP_STATE_ERROR) {
                                        spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                                       nes_rem_ref(&nesqp->ibqp);
                                        return -EINVAL;
                                }
                                nes_debug(NES_DBG_MOD_QP, "QP%u: new state = error\n",
@@ -3008,7 +2999,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                break;
                        default:
                                spin_unlock_irqrestore(&nesqp->lock, qplockflags);
-                               nes_rem_ref(&nesqp->ibqp);
                                return -EINVAL;
                                break;
                }
@@ -3088,7 +3078,6 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                                        nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
                                                        original_last_aeq, nesqp->last_aeq);
                                        /* this one is for the cm_disconnect thread */
-                                       nes_add_ref(&nesqp->ibqp);
                                        spin_lock_irqsave(&nesqp->lock, qplockflags);
                                        nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
                                        nesqp->last_aeq = NES_AEQE_AEID_RESET_SENT;
@@ -3097,14 +3086,12 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                } else {
                                        nes_debug(NES_DBG_MOD_QP, "QP%u No fake disconnect, QP refcount=%d\n",
                                                        nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount));
-                                       nes_rem_ref(&nesqp->ibqp);
                                }
                        } else {
                                spin_lock_irqsave(&nesqp->lock, qplockflags);
                                if (nesqp->cm_id) {
                                        /* These two are for the timer thread */
                                        if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
-                                               nes_add_ref(&nesqp->ibqp);
                                                nesqp->cm_id->add_ref(nesqp->cm_id);
                                                nes_debug(NES_DBG_MOD_QP, "QP%u Not decrementing QP refcount (%d),"
                                                                " need ae to finish up, original_last_aeq = 0x%04X."
@@ -3128,14 +3115,12 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                        " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
                                        nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
                                        original_last_aeq, nesqp->last_aeq);
-                       nes_rem_ref(&nesqp->ibqp);
                }
        } else {
                nes_debug(NES_DBG_MOD_QP, "QP%u Decrementing QP refcount (%d), No ae to finish up,"
                                " original_last_aeq = 0x%04X. last_aeq = 0x%04X.\n",
                                nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
                                original_last_aeq, nesqp->last_aeq);
-               nes_rem_ref(&nesqp->ibqp);
        }
 
        err = 0;
index 691525cf394a34f4b2e4f68bf9ee5ee0f6b1cb85..9d9a9dc51f18141227cc097ee63477e75235ac48 100644 (file)
@@ -11,16 +11,17 @@ config INFINIBAND_IPOIB
 
 config INFINIBAND_IPOIB_CM
        bool "IP-over-InfiniBand Connected Mode support"
-       depends on INFINIBAND_IPOIB && EXPERIMENTAL
+       depends on INFINIBAND_IPOIB
        default n
        ---help---
-         This option enables experimental support for IPoIB connected mode.
-         After enabling this option, you need to switch to connected mode through
-         /sys/class/net/ibXXX/mode to actually create connections, and then increase
-         the interface MTU with e.g. ifconfig ib0 mtu 65520.
+         This option enables support for IPoIB connected mode.  After
+         enabling this option, you need to switch to connected mode
+         through /sys/class/net/ibXXX/mode to actually create
+         connections, and then increase the interface MTU with
+         e.g. ifconfig ib0 mtu 65520.
 
-         WARNING: Enabling connected mode will trigger some
-         packet drops for multicast and UD mode traffic from this interface,
+         WARNING: Enabling connected mode will trigger some packet
+         drops for multicast and UD mode traffic from this interface,
          unless you limit mtu for these destinations to 2044.
 
 config INFINIBAND_IPOIB_DEBUG
@@ -33,9 +34,10 @@ config INFINIBAND_IPOIB_DEBUG
          debug_level and mcast_debug_level module parameters (which
          can also be set after the driver is loaded through sysfs).
 
-         This option also creates an "ipoib_debugfs," which can be
-         mounted to expose debugging information about IB multicast
-         groups used by the IPoIB driver.
+         This option also creates a directory tree under ipoib/ in
+         debugfs, which contains files that expose debugging
+         information about IB multicast groups used by the IPoIB
+         driver.
 
 config INFINIBAND_IPOIB_DEBUG_DATA
        bool "IP-over-InfiniBand data path debugging"
index 66f946aa30b3fb0830b8a858e0ef91d9a47cfd73..3d113c6e4a703242e32c0d9f57cc09be29516690 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 menuconfig ISDN
-       tristate "ISDN support"
+       bool "ISDN support"
        depends on NET
        depends on !S390
        ---help---
@@ -21,6 +21,8 @@ menuconfig ISDN
 
 if ISDN
 
+source "drivers/isdn/mISDN/Kconfig"
+
 menuconfig ISDN_I4L
        tristate "Old ISDN4Linux (deprecated)"
        ---help---
index 988142c30a6d374c4db55b0d041bc30932ed0db6..8380a4568d11dda17aa5c0b92839a8ab334e5dbf 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_ISDN_I4L)                 += i4l/
 obj-$(CONFIG_ISDN_CAPI)                        += capi/
+obj-$(CONFIG_MISDN)                    += mISDN/
 obj-$(CONFIG_ISDN_CAPI)                        += hardware/
 obj-$(CONFIG_ISDN_DIVERSION)           += divert/
 obj-$(CONFIG_ISDN_DRV_HISAX)           += hisax/
index 11c8a183948c60cddf556602b60c1c032bd2dc09..a5d8fce4c4c496acec316ddea08431a4ec274ebb 100644 (file)
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_CAPI_AVM)         += avm/
 obj-$(CONFIG_CAPI_EICON)       += eicon/
+obj-$(CONFIG_MISDN)            += mISDN/
diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
new file mode 100644 (file)
index 0000000..9cd5f5f
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Hardware for mISDN
+#
+comment "mISDN hardware drivers"
+
+config MISDN_HFCPCI
+       tristate "Support for HFC PCI cards"
+       depends on MISDN
+       depends on PCI
+       depends on VIRT_TO_BUS
+       help
+         Enable support for cards with Cologne Chip AG's
+          HFC PCI chip.
+
+config MISDN_HFCMULTI
+       tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"
+       depends on PCI
+       depends on MISDN
+       help
+         Enable support for cards with Cologne Chip AG's HFC multiport
+         chip. There are three types of chips that are quite similar,
+         but the interface is different:
+          * HFC-4S (4 S/T interfaces on one chip)
+          * HFC-8S (8 S/T interfaces on one chip)
+          * HFC-E1 (E1 interface for 2Mbit ISDN)
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
new file mode 100644 (file)
index 0000000..1e7ca53
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the modular ISDN hardware drivers
+#
+#
+
+obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
+obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
new file mode 100644 (file)
index 0000000..a33d87a
--- /dev/null
@@ -0,0 +1,1204 @@
+/*
+ * see notice in hfc_multi.c
+ */
+
+extern void ztdummy_extern_interrupt(void);
+extern void ztdummy_register_interrupt(void);
+extern int ztdummy_unregister_interrupt(void);
+
+#define DEBUG_HFCMULTI_FIFO    0x00010000
+#define        DEBUG_HFCMULTI_CRC      0x00020000
+#define        DEBUG_HFCMULTI_INIT     0x00040000
+#define        DEBUG_HFCMULTI_PLXSD    0x00080000
+#define        DEBUG_HFCMULTI_MODE     0x00100000
+#define        DEBUG_HFCMULTI_MSG      0x00200000
+#define        DEBUG_HFCMULTI_STATE    0x00400000
+#define        DEBUG_HFCMULTI_SYNC     0x01000000
+#define        DEBUG_HFCMULTI_DTMF     0x02000000
+#define        DEBUG_HFCMULTI_LOCK     0x80000000
+
+#define        PCI_ENA_REGIO   0x01
+#define        PCI_ENA_MEMIO   0x02
+
+/*
+ * NOTE: some registers are assigned multiple times due to different modes
+ *       also registers are assigned differen for HFC-4s/8s and HFC-E1
+ */
+
+/*
+#define MAX_FRAME_SIZE 2048
+*/
+
+struct hfc_chan {
+       struct dchannel *dch;   /* link if channel is a D-channel */
+       struct bchannel *bch;   /* link if channel is a B-channel */
+       int             port;   /* the interface port this */
+                               /* channel is associated with */
+       int             nt_timer; /* -1 if off, 0 if elapsed, >0 if running */
+       int             los, ais, slip_tx, slip_rx, rdi; /* current alarms */
+       int             jitter;
+       u_long          cfg;    /* port configuration */
+       int             sync;   /* sync state (used by E1) */
+       u_int           protocol; /* current protocol */
+       int             slot_tx; /* current pcm slot */
+       int             bank_tx; /* current pcm bank */
+       int             slot_rx;
+       int             bank_rx;
+       int             conf;   /* conference setting of TX slot */
+       int             txpending;      /* if there is currently data in */
+                                       /* the FIFO 0=no, 1=yes, 2=splloop */
+       int             rx_off; /* set to turn fifo receive off */
+       int             coeff_count; /* curren coeff block */
+       s32             *coeff; /* memory pointer to 8 coeff blocks */
+};
+
+
+struct hfcm_hw {
+       u_char  r_ctrl;
+       u_char  r_irq_ctrl;
+       u_char  r_cirm;
+       u_char  r_ram_sz;
+       u_char  r_pcm_md0;
+       u_char  r_irqmsk_misc;
+       u_char  r_dtmf;
+       u_char  r_st_sync;
+       u_char  r_sci_msk;
+       u_char  r_tx0, r_tx1;
+       u_char  a_st_ctrl0[8];
+       timer_t timer;
+};
+
+
+/* for each stack these flags are used (cfg) */
+#define        HFC_CFG_NONCAP_TX       1 /* S/T TX interface has less capacity */
+#define        HFC_CFG_DIS_ECHANNEL    2 /* disable E-channel processing */
+#define        HFC_CFG_REG_ECHANNEL    3 /* register E-channel */
+#define        HFC_CFG_OPTICAL         4 /* the E1 interface is optical */
+#define        HFC_CFG_REPORT_LOS      5 /* the card should report loss of signal */
+#define        HFC_CFG_REPORT_AIS      6 /* the card should report alarm ind. sign. */
+#define        HFC_CFG_REPORT_SLIP     7 /* the card should report bit slips */
+#define        HFC_CFG_REPORT_RDI      8 /* the card should report remote alarm */
+#define        HFC_CFG_DTMF            9 /* enable DTMF-detection */
+#define        HFC_CFG_CRC4            10 /* disable CRC-4 Multiframe mode, */
+                                       /* use double frame instead. */
+
+#define        HFC_CHIP_EXRAM_128      0 /* external ram 128k */
+#define        HFC_CHIP_EXRAM_512      1 /* external ram 256k */
+#define        HFC_CHIP_REVISION0      2 /* old fifo handling */
+#define        HFC_CHIP_PCM_SLAVE      3 /* PCM is slave */
+#define        HFC_CHIP_PCM_MASTER     4 /* PCM is master */
+#define        HFC_CHIP_RX_SYNC        5 /* disable pll sync for pcm */
+#define        HFC_CHIP_DTMF           6 /* DTMF decoding is enabled */
+#define        HFC_CHIP_ULAW           7 /* ULAW mode */
+#define        HFC_CHIP_CLOCK2         8 /* double clock mode */
+#define        HFC_CHIP_E1CLOCK_GET    9 /* always get clock from E1 interface */
+#define        HFC_CHIP_E1CLOCK_PUT    10 /* always put clock from E1 interface */
+#define        HFC_CHIP_WATCHDOG       11 /* whether we should send signals */
+                                       /* to the watchdog */
+#define        HFC_CHIP_B410P          12 /* whether we have a b410p with echocan in */
+                                       /* hw */
+#define        HFC_CHIP_PLXSD          13 /* whether we have a Speech-Design PLX */
+
+#define HFC_IO_MODE_PCIMEM     0x00 /* normal memory mapped IO */
+#define HFC_IO_MODE_REGIO      0x01 /* PCI io access */
+#define HFC_IO_MODE_PLXSD      0x02 /* access HFC via PLX9030 */
+
+/* table entry in the PCI devices list */
+struct hm_map {
+       char *vendor_name;
+       char *card_name;
+       int type;
+       int ports;
+       int clock2;
+       int leds;
+       int opticalsupport;
+       int dip_type;
+       int io_mode;
+};
+
+struct hfc_multi {
+       struct list_head        list;
+       struct hm_map   *mtyp;
+       int             id;
+       int             pcm;    /* id of pcm bus */
+       int             type;
+       int             ports;
+
+       u_int           irq;    /* irq used by card */
+       u_int           irqcnt;
+       struct pci_dev  *pci_dev;
+       int             io_mode; /* selects mode */
+#ifdef HFC_REGISTER_DEBUG
+       void            (*HFC_outb)(struct hfc_multi *hc, u_char reg,
+                               u_char val, const char *function, int line);
+       void            (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+                               u_char val, const char *function, int line);
+       u_char          (*HFC_inb)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       u_char          (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       u_short         (*HFC_inw)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       u_short         (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       void            (*HFC_wait)(struct hfc_multi *hc,
+                               const char *function, int line);
+       void            (*HFC_wait_nodebug)(struct hfc_multi *hc,
+                               const char *function, int line);
+#else
+       void            (*HFC_outb)(struct hfc_multi *hc, u_char reg,
+                               u_char val);
+       void            (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+                               u_char val);
+       u_char          (*HFC_inb)(struct hfc_multi *hc, u_char reg);
+       u_char          (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg);
+       u_short         (*HFC_inw)(struct hfc_multi *hc, u_char reg);
+       u_short         (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg);
+       void            (*HFC_wait)(struct hfc_multi *hc);
+       void            (*HFC_wait_nodebug)(struct hfc_multi *hc);
+#endif
+       void            (*read_fifo)(struct hfc_multi *hc, u_char *data,
+                               int len);
+       void            (*write_fifo)(struct hfc_multi *hc, u_char *data,
+                               int len);
+       u_long          pci_origmembase, plx_origmembase, dsp_origmembase;
+       u_char          *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */
+       u_char          *plx_membase; /* PLX memory */
+       u_char          *dsp_membase; /* DSP on PLX */
+       u_long          pci_iobase; /* PCI IO */
+       struct hfcm_hw  hw;     /* remember data of write-only-registers */
+
+       u_long          chip;   /* chip configuration */
+       int             masterclk; /* port that provides master clock -1=off */
+       int             dtmf;   /* flag that dtmf is currently in process */
+       int             Flen;   /* F-buffer size */
+       int             Zlen;   /* Z-buffer size (must be int for calculation)*/
+       int             max_trans; /* maximum transparent fifo fill */
+       int             Zmin;   /* Z-buffer offset */
+       int             DTMFbase; /* base address of DTMF coefficients */
+
+       u_int           slots;  /* number of PCM slots */
+       u_int           leds;   /* type of leds */
+       u_int           ledcount; /* used to animate leds */
+       u_long          ledstate; /* save last state of leds */
+       int             opticalsupport; /* has the e1 board */
+                                       /* an optical Interface */
+       int             dslot;  /* channel # of d-channel (E1) default 16 */
+
+       u_long          wdcount;        /* every 500 ms we need to */
+                                       /* send the watchdog a signal */
+       u_char          wdbyte; /* watchdog toggle byte */
+       u_int           activity[8];    /* if there is any action on this */
+                                       /* port (will be cleared after */
+                                       /* showing led-states) */
+       int             e1_state; /* keep track of last state */
+       int             e1_getclock; /* if sync is retrieved from interface */
+       int             syncronized; /* keep track of existing sync interface */
+       int             e1_resync; /* resync jobs */
+
+       spinlock_t      lock;   /* the lock */
+
+       /*
+        * the channel index is counted from 0, regardless where the channel
+        * is located on the hfc-channel.
+        * the bch->channel is equvalent to the hfc-channel
+        */
+       struct hfc_chan chan[32];
+       u_char          created[8]; /* what port is created */
+       signed char     slot_owner[256]; /* owner channel of slot */
+};
+
+/* PLX GPIOs */
+#define        PLX_GPIO4_DIR_BIT       13
+#define        PLX_GPIO4_BIT           14
+#define        PLX_GPIO5_DIR_BIT       16
+#define        PLX_GPIO5_BIT           17
+#define        PLX_GPIO6_DIR_BIT       19
+#define        PLX_GPIO6_BIT           20
+#define        PLX_GPIO7_DIR_BIT       22
+#define        PLX_GPIO7_BIT           23
+#define PLX_GPIO8_DIR_BIT      25
+#define PLX_GPIO8_BIT          26
+
+#define        PLX_GPIO4               (1 << PLX_GPIO4_BIT)
+#define        PLX_GPIO5               (1 << PLX_GPIO5_BIT)
+#define        PLX_GPIO6               (1 << PLX_GPIO6_BIT)
+#define        PLX_GPIO7               (1 << PLX_GPIO7_BIT)
+#define PLX_GPIO8              (1 << PLX_GPIO8_BIT)
+
+#define        PLX_GPIO4_DIR           (1 << PLX_GPIO4_DIR_BIT)
+#define        PLX_GPIO5_DIR           (1 << PLX_GPIO5_DIR_BIT)
+#define        PLX_GPIO6_DIR           (1 << PLX_GPIO6_DIR_BIT)
+#define        PLX_GPIO7_DIR           (1 << PLX_GPIO7_DIR_BIT)
+#define PLX_GPIO8_DIR          (1 << PLX_GPIO8_DIR_BIT)
+
+#define        PLX_TERM_ON                     PLX_GPIO7
+#define        PLX_SLAVE_EN_N          PLX_GPIO5
+#define        PLX_MASTER_EN           PLX_GPIO6
+#define        PLX_SYNC_O_EN           PLX_GPIO4
+#define PLX_DSP_RES_N          PLX_GPIO8
+/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */
+#define PLX_GPIOC_INIT         (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \
+                       | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N)
+
+/* PLX Interrupt Control/STATUS */
+#define PLX_INTCSR_LINTI1_ENABLE 0x01
+#define PLX_INTCSR_LINTI1_STATUS 0x04
+#define PLX_INTCSR_LINTI2_ENABLE 0x08
+#define PLX_INTCSR_LINTI2_STATUS 0x20
+#define PLX_INTCSR_PCIINT_ENABLE 0x40
+
+/* PLX Registers */
+#define PLX_INTCSR 0x4c
+#define PLX_CNTRL  0x50
+#define PLX_GPIOC  0x54
+
+
+/*
+ * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1
+ */
+
+/* write only registers */
+#define R_CIRM                 0x00
+#define R_CTRL                 0x01
+#define R_BRG_PCM_CFG          0x02
+#define R_RAM_ADDR0            0x08
+#define R_RAM_ADDR1            0x09
+#define R_RAM_ADDR2            0x0A
+#define R_FIRST_FIFO           0x0B
+#define R_RAM_SZ               0x0C
+#define R_FIFO_MD              0x0D
+#define R_INC_RES_FIFO         0x0E
+#define R_FSM_IDX              0x0F
+#define R_FIFO                 0x0F
+#define R_SLOT                 0x10
+#define R_IRQMSK_MISC          0x11
+#define R_SCI_MSK              0x12
+#define R_IRQ_CTRL             0x13
+#define R_PCM_MD0              0x14
+#define R_PCM_MD1              0x15
+#define R_PCM_MD2              0x15
+#define R_SH0H                 0x15
+#define R_SH1H                 0x15
+#define R_SH0L                 0x15
+#define R_SH1L                 0x15
+#define R_SL_SEL0              0x15
+#define R_SL_SEL1              0x15
+#define R_SL_SEL2              0x15
+#define R_SL_SEL3              0x15
+#define R_SL_SEL4              0x15
+#define R_SL_SEL5              0x15
+#define R_SL_SEL6              0x15
+#define R_SL_SEL7              0x15
+#define R_ST_SEL               0x16
+#define R_ST_SYNC              0x17
+#define R_CONF_EN              0x18
+#define R_TI_WD                        0x1A
+#define R_BERT_WD_MD           0x1B
+#define R_DTMF                 0x1C
+#define R_DTMF_N               0x1D
+#define R_E1_WR_STA            0x20
+#define R_E1_RD_STA            0x20
+#define R_LOS0                 0x22
+#define R_LOS1                 0x23
+#define R_RX0                  0x24
+#define R_RX_FR0               0x25
+#define R_RX_FR1               0x26
+#define R_TX0                  0x28
+#define R_TX1                  0x29
+#define R_TX_FR0               0x2C
+
+#define R_TX_FR1               0x2D
+#define R_TX_FR2               0x2E
+#define R_JATT_ATT             0x2F /* undocumented */
+#define A_ST_RD_STATE          0x30
+#define A_ST_WR_STATE          0x30
+#define R_RX_OFF               0x30
+#define A_ST_CTRL0             0x31
+#define R_SYNC_OUT             0x31
+#define A_ST_CTRL1             0x32
+#define A_ST_CTRL2             0x33
+#define A_ST_SQ_WR             0x34
+#define R_TX_OFF               0x34
+#define R_SYNC_CTRL            0x35
+#define A_ST_CLK_DLY           0x37
+#define R_PWM0                 0x38
+#define R_PWM1                 0x39
+#define A_ST_B1_TX             0x3C
+#define A_ST_B2_TX             0x3D
+#define A_ST_D_TX              0x3E
+#define R_GPIO_OUT0            0x40
+#define R_GPIO_OUT1            0x41
+#define R_GPIO_EN0             0x42
+#define R_GPIO_EN1             0x43
+#define R_GPIO_SEL             0x44
+#define R_BRG_CTRL             0x45
+#define R_PWM_MD               0x46
+#define R_BRG_MD               0x47
+#define R_BRG_TIM0             0x48
+#define R_BRG_TIM1             0x49
+#define R_BRG_TIM2             0x4A
+#define R_BRG_TIM3             0x4B
+#define R_BRG_TIM_SEL01                0x4C
+#define R_BRG_TIM_SEL23                0x4D
+#define R_BRG_TIM_SEL45                0x4E
+#define R_BRG_TIM_SEL67                0x4F
+#define A_SL_CFG               0xD0
+#define A_CONF                 0xD1
+#define A_CH_MSK               0xF4
+#define A_CON_HDLC             0xFA
+#define A_SUBCH_CFG            0xFB
+#define A_CHANNEL              0xFC
+#define A_FIFO_SEQ             0xFD
+#define A_IRQ_MSK              0xFF
+
+/* read only registers */
+#define A_Z12                  0x04
+#define A_Z1L                  0x04
+#define A_Z1                   0x04
+#define A_Z1H                  0x05
+#define A_Z2L                  0x06
+#define A_Z2                   0x06
+#define A_Z2H                  0x07
+#define A_F1                   0x0C
+#define A_F12                  0x0C
+#define A_F2                   0x0D
+#define R_IRQ_OVIEW            0x10
+#define R_IRQ_MISC             0x11
+#define R_IRQ_STATECH          0x12
+#define R_CONF_OFLOW           0x14
+#define R_RAM_USE              0x15
+#define R_CHIP_ID              0x16
+#define R_BERT_STA             0x17
+#define R_F0_CNTL              0x18
+#define R_F0_CNTH              0x19
+#define R_BERT_EC              0x1A
+#define R_BERT_ECL             0x1A
+#define R_BERT_ECH             0x1B
+#define R_STATUS               0x1C
+#define R_CHIP_RV              0x1F
+#define R_STATE                        0x20
+#define R_SYNC_STA             0x24
+#define R_RX_SL0_0             0x25
+#define R_RX_SL0_1             0x26
+#define R_RX_SL0_2             0x27
+#define R_JATT_DIR             0x2b /* undocumented */
+#define R_SLIP                 0x2c
+#define A_ST_RD_STA            0x30
+#define R_FAS_EC               0x30
+#define R_FAS_ECL              0x30
+#define R_FAS_ECH              0x31
+#define R_VIO_EC               0x32
+#define R_VIO_ECL              0x32
+#define R_VIO_ECH              0x33
+#define A_ST_SQ_RD             0x34
+#define R_CRC_EC               0x34
+#define R_CRC_ECL              0x34
+#define R_CRC_ECH              0x35
+#define R_E_EC                 0x36
+#define R_E_ECL                        0x36
+#define R_E_ECH                        0x37
+#define R_SA6_SA13_EC          0x38
+#define R_SA6_SA13_ECL         0x38
+#define R_SA6_SA13_ECH         0x39
+#define R_SA6_SA23_EC          0x3A
+#define R_SA6_SA23_ECL         0x3A
+#define R_SA6_SA23_ECH         0x3B
+#define A_ST_B1_RX             0x3C
+#define A_ST_B2_RX             0x3D
+#define A_ST_D_RX              0x3E
+#define A_ST_E_RX              0x3F
+#define R_GPIO_IN0             0x40
+#define R_GPIO_IN1             0x41
+#define R_GPI_IN0              0x44
+#define R_GPI_IN1              0x45
+#define R_GPI_IN2              0x46
+#define R_GPI_IN3              0x47
+#define R_INT_DATA             0x88
+#define R_IRQ_FIFO_BL0         0xC8
+#define R_IRQ_FIFO_BL1         0xC9
+#define R_IRQ_FIFO_BL2         0xCA
+#define R_IRQ_FIFO_BL3         0xCB
+#define R_IRQ_FIFO_BL4         0xCC
+#define R_IRQ_FIFO_BL5         0xCD
+#define R_IRQ_FIFO_BL6         0xCE
+#define R_IRQ_FIFO_BL7         0xCF
+
+/* read and write registers */
+#define A_FIFO_DATA0           0x80
+#define A_FIFO_DATA1           0x80
+#define A_FIFO_DATA2           0x80
+#define A_FIFO_DATA0_NOINC     0x84
+#define A_FIFO_DATA1_NOINC     0x84
+#define A_FIFO_DATA2_NOINC     0x84
+#define R_RAM_DATA             0xC0
+
+
+/*
+ * BIT SETTING FOR HFC-4S/8S AND HFC-E1
+ */
+
+/* chapter 2: universal bus interface */
+/* R_CIRM */
+#define V_IRQ_SEL              0x01
+#define V_SRES                 0x08
+#define V_HFCRES               0x10
+#define V_PCMRES               0x20
+#define V_STRES                        0x40
+#define V_ETRES                        0x40
+#define V_RLD_EPR              0x80
+/* R_CTRL */
+#define V_FIFO_LPRIO           0x02
+#define V_SLOW_RD              0x04
+#define V_EXT_RAM              0x08
+#define V_CLK_OFF              0x20
+#define V_ST_CLK               0x40
+/* R_RAM_ADDR0 */
+#define V_RAM_ADDR2            0x01
+#define V_ADDR_RES             0x40
+#define V_ADDR_INC             0x80
+/* R_RAM_SZ */
+#define V_RAM_SZ               0x01
+#define V_PWM0_16KHZ           0x10
+#define V_PWM1_16KHZ           0x20
+#define V_FZ_MD                        0x80
+/* R_CHIP_ID */
+#define V_PNP_IRQ              0x01
+#define V_CHIP_ID              0x10
+
+/* chapter 3: data flow */
+/* R_FIRST_FIFO */
+#define V_FIRST_FIRO_DIR       0x01
+#define V_FIRST_FIFO_NUM       0x02
+/* R_FIFO_MD */
+#define V_FIFO_MD              0x01
+#define V_CSM_MD               0x04
+#define V_FSM_MD               0x08
+#define V_FIFO_SZ              0x10
+/* R_FIFO */
+#define V_FIFO_DIR             0x01
+#define V_FIFO_NUM             0x02
+#define V_REV                  0x80
+/* R_SLOT */
+#define V_SL_DIR               0x01
+#define V_SL_NUM               0x02
+/* A_SL_CFG */
+#define V_CH_DIR               0x01
+#define V_CH_SEL               0x02
+#define V_ROUTING              0x40
+/* A_CON_HDLC */
+#define V_IFF                  0x01
+#define V_HDLC_TRP             0x02
+#define V_TRP_IRQ              0x04
+#define V_DATA_FLOW            0x20
+/* A_SUBCH_CFG */
+#define V_BIT_CNT              0x01
+#define V_START_BIT            0x08
+#define V_LOOP_FIFO            0x40
+#define V_INV_DATA             0x80
+/* A_CHANNEL */
+#define V_CH_DIR0              0x01
+#define V_CH_NUM0              0x02
+/* A_FIFO_SEQ */
+#define V_NEXT_FIFO_DIR                0x01
+#define V_NEXT_FIFO_NUM                0x02
+#define V_SEQ_END              0x40
+
+/* chapter 4: FIFO handling and HDLC controller */
+/* R_INC_RES_FIFO */
+#define V_INC_F                        0x01
+#define V_RES_F                        0x02
+#define V_RES_LOST             0x04
+
+/* chapter 5: S/T interface */
+/* R_SCI_MSK */
+#define V_SCI_MSK_ST0          0x01
+#define V_SCI_MSK_ST1          0x02
+#define V_SCI_MSK_ST2          0x04
+#define V_SCI_MSK_ST3          0x08
+#define V_SCI_MSK_ST4          0x10
+#define V_SCI_MSK_ST5          0x20
+#define V_SCI_MSK_ST6          0x40
+#define V_SCI_MSK_ST7          0x80
+/* R_ST_SEL */
+#define V_ST_SEL               0x01
+#define V_MULT_ST              0x08
+/* R_ST_SYNC */
+#define V_SYNC_SEL             0x01
+#define V_AUTO_SYNC            0x08
+/* A_ST_WR_STA */
+#define V_ST_SET_STA           0x01
+#define V_ST_LD_STA            0x10
+#define V_ST_ACT               0x20
+#define V_SET_G2_G3            0x80
+/* A_ST_CTRL0 */
+#define V_B1_EN                        0x01
+#define V_B2_EN                        0x02
+#define V_ST_MD                        0x04
+#define V_D_PRIO               0x08
+#define V_SQ_EN                        0x10
+#define V_96KHZ                        0x20
+#define V_TX_LI                        0x40
+#define V_ST_STOP              0x80
+/* A_ST_CTRL1 */
+#define V_G2_G3_EN             0x01
+#define V_D_HI                 0x04
+#define V_E_IGNO               0x08
+#define V_E_LO                 0x10
+#define V_B12_SWAP             0x80
+/* A_ST_CTRL2 */
+#define V_B1_RX_EN             0x01
+#define V_B2_RX_EN             0x02
+#define V_ST_TRIS              0x40
+/* A_ST_CLK_DLY */
+#define V_ST_CK_DLY            0x01
+#define V_ST_SMPL              0x10
+/* A_ST_D_TX */
+#define V_ST_D_TX              0x40
+/* R_IRQ_STATECH */
+#define V_SCI_ST0              0x01
+#define V_SCI_ST1              0x02
+#define V_SCI_ST2              0x04
+#define V_SCI_ST3              0x08
+#define V_SCI_ST4              0x10
+#define V_SCI_ST5              0x20
+#define V_SCI_ST6              0x40
+#define V_SCI_ST7              0x80
+/* A_ST_RD_STA */
+#define V_ST_STA               0x01
+#define V_FR_SYNC_ST           0x10
+#define V_TI2_EXP              0x20
+#define V_INFO0                        0x40
+#define V_G2_G3                        0x80
+/* A_ST_SQ_RD */
+#define V_ST_SQ                        0x01
+#define V_MF_RX_RDY            0x10
+#define V_MF_TX_RDY            0x80
+/* A_ST_D_RX */
+#define V_ST_D_RX              0x40
+/* A_ST_E_RX */
+#define V_ST_E_RX              0x40
+
+/* chapter 5: E1 interface */
+/* R_E1_WR_STA */
+/* R_E1_RD_STA */
+#define V_E1_SET_STA           0x01
+#define V_E1_LD_STA            0x10
+/* R_RX0 */
+#define V_RX_CODE              0x01
+#define V_RX_FBAUD             0x04
+#define V_RX_CMI               0x08
+#define V_RX_INV_CMI           0x10
+#define V_RX_INV_CLK           0x20
+#define V_RX_INV_DATA          0x40
+#define V_AIS_ITU              0x80
+/* R_RX_FR0 */
+#define V_NO_INSYNC            0x01
+#define V_AUTO_RESYNC          0x02
+#define V_AUTO_RECO            0x04
+#define V_SWORD_COND           0x08
+#define V_SYNC_LOSS            0x10
+#define V_XCRC_SYNC            0x20
+#define V_MF_RESYNC            0x40
+#define V_RESYNC               0x80
+/* R_RX_FR1 */
+#define V_RX_MF                        0x01
+#define V_RX_MF_SYNC           0x02
+#define V_RX_SL0_RAM           0x04
+#define V_ERR_SIM              0x20
+#define V_RES_NMF              0x40
+/* R_TX0 */
+#define V_TX_CODE              0x01
+#define V_TX_FBAUD             0x04
+#define V_TX_CMI_CODE          0x08
+#define V_TX_INV_CMI_CODE      0x10
+#define V_TX_INV_CLK           0x20
+#define V_TX_INV_DATA          0x40
+#define V_OUT_EN               0x80
+/* R_TX1 */
+#define V_INV_CLK              0x01
+#define V_EXCHG_DATA_LI                0x02
+#define V_AIS_OUT              0x04
+#define V_ATX                  0x20
+#define V_NTRI                 0x40
+#define V_AUTO_ERR_RES         0x80
+/* R_TX_FR0 */
+#define V_TRP_FAS              0x01
+#define V_TRP_NFAS             0x02
+#define V_TRP_RAL              0x04
+#define V_TRP_SA               0x08
+/* R_TX_FR1 */
+#define V_TX_FAS               0x01
+#define V_TX_NFAS              0x02
+#define V_TX_RAL               0x04
+#define V_TX_SA                        0x08
+/* R_TX_FR2 */
+#define V_TX_MF                        0x01
+#define V_TRP_SL0              0x02
+#define V_TX_SL0_RAM           0x04
+#define V_TX_E                 0x10
+#define V_NEG_E                        0x20
+#define V_XS12_ON              0x40
+#define V_XS15_ON              0x80
+/* R_RX_OFF */
+#define V_RX_SZ                        0x01
+#define V_RX_INIT              0x04
+/* R_SYNC_OUT */
+#define V_SYNC_E1_RX           0x01
+#define V_IPATS0               0x20
+#define V_IPATS1               0x40
+#define V_IPATS2               0x80
+/* R_TX_OFF */
+#define V_TX_SZ                        0x01
+#define V_TX_INIT              0x04
+/* R_SYNC_CTRL */
+#define V_EXT_CLK_SYNC         0x01
+#define V_SYNC_OFFS            0x02
+#define V_PCM_SYNC             0x04
+#define V_NEG_CLK              0x08
+#define V_HCLK                 0x10
+/*
+#define V_JATT_AUTO_DEL                0x20
+#define V_JATT_AUTO            0x40
+*/
+#define V_JATT_OFF             0x80
+/* R_STATE */
+#define V_E1_STA               0x01
+#define V_ALT_FR_RX            0x40
+#define V_ALT_FR_TX            0x80
+/* R_SYNC_STA */
+#define V_RX_STA               0x01
+#define V_FR_SYNC_E1           0x04
+#define V_SIG_LOS              0x08
+#define V_MFA_STA              0x10
+#define V_AIS                  0x40
+#define V_NO_MF_SYNC           0x80
+/* R_RX_SL0_0 */
+#define V_SI_FAS               0x01
+#define V_SI_NFAS              0x02
+#define V_A                    0x04
+#define V_CRC_OK               0x08
+#define V_TX_E1                        0x10
+#define V_TX_E2                        0x20
+#define V_RX_E1                        0x40
+#define V_RX_E2                        0x80
+/* R_SLIP */
+#define V_SLIP_RX              0x01
+#define V_FOSLIP_RX            0x08
+#define V_SLIP_TX              0x10
+#define V_FOSLIP_TX            0x80
+
+/* chapter 6: PCM interface */
+/* R_PCM_MD0 */
+#define V_PCM_MD               0x01
+#define V_C4_POL               0x02
+#define V_F0_NEG               0x04
+#define V_F0_LEN               0x08
+#define V_PCM_ADDR             0x10
+/* R_SL_SEL0 */
+#define V_SL_SEL0              0x01
+#define V_SH_SEL0              0x80
+/* R_SL_SEL1 */
+#define V_SL_SEL1              0x01
+#define V_SH_SEL1              0x80
+/* R_SL_SEL2 */
+#define V_SL_SEL2              0x01
+#define V_SH_SEL2              0x80
+/* R_SL_SEL3 */
+#define V_SL_SEL3              0x01
+#define V_SH_SEL3              0x80
+/* R_SL_SEL4 */
+#define V_SL_SEL4              0x01
+#define V_SH_SEL4              0x80
+/* R_SL_SEL5 */
+#define V_SL_SEL5              0x01
+#define V_SH_SEL5              0x80
+/* R_SL_SEL6 */
+#define V_SL_SEL6              0x01
+#define V_SH_SEL6              0x80
+/* R_SL_SEL7 */
+#define V_SL_SEL7              0x01
+#define V_SH_SEL7              0x80
+/* R_PCM_MD1 */
+#define V_ODEC_CON             0x01
+#define V_PLL_ADJ              0x04
+#define V_PCM_DR               0x10
+#define V_PCM_LOOP             0x40
+/* R_PCM_MD2 */
+#define V_SYNC_PLL             0x02
+#define V_SYNC_SRC             0x04
+#define V_SYNC_OUT             0x08
+#define V_ICR_FR_TIME          0x40
+#define V_EN_PLL               0x80
+
+/* chapter 7: pulse width modulation */
+/* R_PWM_MD */
+#define V_EXT_IRQ_EN           0x08
+#define V_PWM0_MD              0x10
+#define V_PWM1_MD              0x40
+
+/* chapter 8: multiparty audio conferences */
+/* R_CONF_EN */
+#define V_CONF_EN              0x01
+#define V_ULAW                 0x80
+/* A_CONF */
+#define V_CONF_NUM             0x01
+#define V_NOISE_SUPPR          0x08
+#define V_ATT_LEV              0x20
+#define V_CONF_SL              0x80
+/* R_CONF_OFLOW */
+#define V_CONF_OFLOW0          0x01
+#define V_CONF_OFLOW1          0x02
+#define V_CONF_OFLOW2          0x04
+#define V_CONF_OFLOW3          0x08
+#define V_CONF_OFLOW4          0x10
+#define V_CONF_OFLOW5          0x20
+#define V_CONF_OFLOW6          0x40
+#define V_CONF_OFLOW7          0x80
+
+/* chapter 9: DTMF contoller */
+/* R_DTMF0 */
+#define V_DTMF_EN              0x01
+#define V_HARM_SEL             0x02
+#define V_DTMF_RX_CH           0x04
+#define V_DTMF_STOP            0x08
+#define V_CHBL_SEL             0x10
+#define V_RST_DTMF             0x40
+#define V_ULAW_SEL             0x80
+
+/* chapter 10: BERT */
+/* R_BERT_WD_MD */
+#define V_PAT_SEQ              0x01
+#define V_BERT_ERR             0x08
+#define V_AUTO_WD_RES          0x20
+#define V_WD_RES               0x80
+/* R_BERT_STA */
+#define V_BERT_SYNC_SRC                0x01
+#define V_BERT_SYNC            0x10
+#define V_BERT_INV_DATA                0x20
+
+/* chapter 11: auxiliary interface */
+/* R_BRG_PCM_CFG */
+#define V_BRG_EN               0x01
+#define V_BRG_MD               0x02
+#define V_PCM_CLK              0x20
+#define V_ADDR_WRDLY           0x40
+/* R_BRG_CTRL */
+#define V_BRG_CS               0x01
+#define V_BRG_ADDR             0x08
+#define V_BRG_CS_SRC           0x80
+/* R_BRG_MD */
+#define V_BRG_MD0              0x01
+#define V_BRG_MD1              0x02
+#define V_BRG_MD2              0x04
+#define V_BRG_MD3              0x08
+#define V_BRG_MD4              0x10
+#define V_BRG_MD5              0x20
+#define V_BRG_MD6              0x40
+#define V_BRG_MD7              0x80
+/* R_BRG_TIM0 */
+#define V_BRG_TIM0_IDLE                0x01
+#define V_BRG_TIM0_CLK         0x10
+/* R_BRG_TIM1 */
+#define V_BRG_TIM1_IDLE                0x01
+#define V_BRG_TIM1_CLK         0x10
+/* R_BRG_TIM2 */
+#define V_BRG_TIM2_IDLE                0x01
+#define V_BRG_TIM2_CLK         0x10
+/* R_BRG_TIM3 */
+#define V_BRG_TIM3_IDLE                0x01
+#define V_BRG_TIM3_CLK         0x10
+/* R_BRG_TIM_SEL01 */
+#define V_BRG_WR_SEL0          0x01
+#define V_BRG_RD_SEL0          0x04
+#define V_BRG_WR_SEL1          0x10
+#define V_BRG_RD_SEL1          0x40
+/* R_BRG_TIM_SEL23 */
+#define V_BRG_WR_SEL2          0x01
+#define V_BRG_RD_SEL2          0x04
+#define V_BRG_WR_SEL3          0x10
+#define V_BRG_RD_SEL3          0x40
+/* R_BRG_TIM_SEL45 */
+#define V_BRG_WR_SEL4          0x01
+#define V_BRG_RD_SEL4          0x04
+#define V_BRG_WR_SEL5          0x10
+#define V_BRG_RD_SEL5          0x40
+/* R_BRG_TIM_SEL67 */
+#define V_BRG_WR_SEL6          0x01
+#define V_BRG_RD_SEL6          0x04
+#define V_BRG_WR_SEL7          0x10
+#define V_BRG_RD_SEL7          0x40
+
+/* chapter 12: clock, reset, interrupt, timer and watchdog */
+/* R_IRQMSK_MISC */
+#define V_STA_IRQMSK           0x01
+#define V_TI_IRQMSK            0x02
+#define V_PROC_IRQMSK          0x04
+#define V_DTMF_IRQMSK          0x08
+#define V_IRQ1S_MSK            0x10
+#define V_SA6_IRQMSK           0x20
+#define V_RX_EOMF_MSK          0x40
+#define V_TX_EOMF_MSK          0x80
+/* R_IRQ_CTRL */
+#define V_FIFO_IRQ             0x01
+#define V_GLOB_IRQ_EN          0x08
+#define V_IRQ_POL              0x10
+/* R_TI_WD */
+#define V_EV_TS                        0x01
+#define V_WD_TS                        0x10
+/* A_IRQ_MSK */
+#define V_IRQ                  0x01
+#define V_BERT_EN              0x02
+#define V_MIX_IRQ              0x04
+/* R_IRQ_OVIEW */
+#define V_IRQ_FIFO_BL0         0x01
+#define V_IRQ_FIFO_BL1         0x02
+#define V_IRQ_FIFO_BL2         0x04
+#define V_IRQ_FIFO_BL3         0x08
+#define V_IRQ_FIFO_BL4         0x10
+#define V_IRQ_FIFO_BL5         0x20
+#define V_IRQ_FIFO_BL6         0x40
+#define V_IRQ_FIFO_BL7         0x80
+/* R_IRQ_MISC */
+#define V_STA_IRQ              0x01
+#define V_TI_IRQ               0x02
+#define V_IRQ_PROC             0x04
+#define V_DTMF_IRQ             0x08
+#define V_IRQ1S                        0x10
+#define V_SA6_IRQ              0x20
+#define V_RX_EOMF              0x40
+#define V_TX_EOMF              0x80
+/* R_STATUS */
+#define V_BUSY                 0x01
+#define V_PROC                 0x02
+#define V_DTMF_STA             0x04
+#define V_LOST_STA             0x08
+#define V_SYNC_IN              0x10
+#define V_EXT_IRQSTA           0x20
+#define V_MISC_IRQSTA          0x40
+#define V_FR_IRQSTA            0x80
+/* R_IRQ_FIFO_BL0 */
+#define V_IRQ_FIFO0_TX         0x01
+#define V_IRQ_FIFO0_RX         0x02
+#define V_IRQ_FIFO1_TX         0x04
+#define V_IRQ_FIFO1_RX         0x08
+#define V_IRQ_FIFO2_TX         0x10
+#define V_IRQ_FIFO2_RX         0x20
+#define V_IRQ_FIFO3_TX         0x40
+#define V_IRQ_FIFO3_RX         0x80
+/* R_IRQ_FIFO_BL1 */
+#define V_IRQ_FIFO4_TX         0x01
+#define V_IRQ_FIFO4_RX         0x02
+#define V_IRQ_FIFO5_TX         0x04
+#define V_IRQ_FIFO5_RX         0x08
+#define V_IRQ_FIFO6_TX         0x10
+#define V_IRQ_FIFO6_RX         0x20
+#define V_IRQ_FIFO7_TX         0x40
+#define V_IRQ_FIFO7_RX         0x80
+/* R_IRQ_FIFO_BL2 */
+#define V_IRQ_FIFO8_TX         0x01
+#define V_IRQ_FIFO8_RX         0x02
+#define V_IRQ_FIFO9_TX         0x04
+#define V_IRQ_FIFO9_RX         0x08
+#define V_IRQ_FIFO10_TX                0x10
+#define V_IRQ_FIFO10_RX                0x20
+#define V_IRQ_FIFO11_TX                0x40
+#define V_IRQ_FIFO11_RX                0x80
+/* R_IRQ_FIFO_BL3 */
+#define V_IRQ_FIFO12_TX                0x01
+#define V_IRQ_FIFO12_RX                0x02
+#define V_IRQ_FIFO13_TX                0x04
+#define V_IRQ_FIFO13_RX                0x08
+#define V_IRQ_FIFO14_TX                0x10
+#define V_IRQ_FIFO14_RX                0x20
+#define V_IRQ_FIFO15_TX                0x40
+#define V_IRQ_FIFO15_RX                0x80
+/* R_IRQ_FIFO_BL4 */
+#define V_IRQ_FIFO16_TX                0x01
+#define V_IRQ_FIFO16_RX                0x02
+#define V_IRQ_FIFO17_TX                0x04
+#define V_IRQ_FIFO17_RX                0x08
+#define V_IRQ_FIFO18_TX                0x10
+#define V_IRQ_FIFO18_RX                0x20
+#define V_IRQ_FIFO19_TX                0x40
+#define V_IRQ_FIFO19_RX                0x80
+/* R_IRQ_FIFO_BL5 */
+#define V_IRQ_FIFO20_TX                0x01
+#define V_IRQ_FIFO20_RX                0x02
+#define V_IRQ_FIFO21_TX                0x04
+#define V_IRQ_FIFO21_RX                0x08
+#define V_IRQ_FIFO22_TX                0x10
+#define V_IRQ_FIFO22_RX                0x20
+#define V_IRQ_FIFO23_TX                0x40
+#define V_IRQ_FIFO23_RX                0x80
+/* R_IRQ_FIFO_BL6 */
+#define V_IRQ_FIFO24_TX                0x01
+#define V_IRQ_FIFO24_RX                0x02
+#define V_IRQ_FIFO25_TX                0x04
+#define V_IRQ_FIFO25_RX                0x08
+#define V_IRQ_FIFO26_TX                0x10
+#define V_IRQ_FIFO26_RX                0x20
+#define V_IRQ_FIFO27_TX                0x40
+#define V_IRQ_FIFO27_RX                0x80
+/* R_IRQ_FIFO_BL7 */
+#define V_IRQ_FIFO28_TX                0x01
+#define V_IRQ_FIFO28_RX                0x02
+#define V_IRQ_FIFO29_TX                0x04
+#define V_IRQ_FIFO29_RX                0x08
+#define V_IRQ_FIFO30_TX                0x10
+#define V_IRQ_FIFO30_RX                0x20
+#define V_IRQ_FIFO31_TX                0x40
+#define V_IRQ_FIFO31_RX                0x80
+
+/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */
+/* R_GPIO_OUT0 */
+#define V_GPIO_OUT0            0x01
+#define V_GPIO_OUT1            0x02
+#define V_GPIO_OUT2            0x04
+#define V_GPIO_OUT3            0x08
+#define V_GPIO_OUT4            0x10
+#define V_GPIO_OUT5            0x20
+#define V_GPIO_OUT6            0x40
+#define V_GPIO_OUT7            0x80
+/* R_GPIO_OUT1 */
+#define V_GPIO_OUT8            0x01
+#define V_GPIO_OUT9            0x02
+#define V_GPIO_OUT10           0x04
+#define V_GPIO_OUT11           0x08
+#define V_GPIO_OUT12           0x10
+#define V_GPIO_OUT13           0x20
+#define V_GPIO_OUT14           0x40
+#define V_GPIO_OUT15           0x80
+/* R_GPIO_EN0 */
+#define V_GPIO_EN0             0x01
+#define V_GPIO_EN1             0x02
+#define V_GPIO_EN2             0x04
+#define V_GPIO_EN3             0x08
+#define V_GPIO_EN4             0x10
+#define V_GPIO_EN5             0x20
+#define V_GPIO_EN6             0x40
+#define V_GPIO_EN7             0x80
+/* R_GPIO_EN1 */
+#define V_GPIO_EN8             0x01
+#define V_GPIO_EN9             0x02
+#define V_GPIO_EN10            0x04
+#define V_GPIO_EN11            0x08
+#define V_GPIO_EN12            0x10
+#define V_GPIO_EN13            0x20
+#define V_GPIO_EN14            0x40
+#define V_GPIO_EN15            0x80
+/* R_GPIO_SEL */
+#define V_GPIO_SEL0            0x01
+#define V_GPIO_SEL1            0x02
+#define V_GPIO_SEL2            0x04
+#define V_GPIO_SEL3            0x08
+#define V_GPIO_SEL4            0x10
+#define V_GPIO_SEL5            0x20
+#define V_GPIO_SEL6            0x40
+#define V_GPIO_SEL7            0x80
+/* R_GPIO_IN0 */
+#define V_GPIO_IN0             0x01
+#define V_GPIO_IN1             0x02
+#define V_GPIO_IN2             0x04
+#define V_GPIO_IN3             0x08
+#define V_GPIO_IN4             0x10
+#define V_GPIO_IN5             0x20
+#define V_GPIO_IN6             0x40
+#define V_GPIO_IN7             0x80
+/* R_GPIO_IN1 */
+#define V_GPIO_IN8             0x01
+#define V_GPIO_IN9             0x02
+#define V_GPIO_IN10            0x04
+#define V_GPIO_IN11            0x08
+#define V_GPIO_IN12            0x10
+#define V_GPIO_IN13            0x20
+#define V_GPIO_IN14            0x40
+#define V_GPIO_IN15            0x80
+/* R_GPI_IN0 */
+#define V_GPI_IN0              0x01
+#define V_GPI_IN1              0x02
+#define V_GPI_IN2              0x04
+#define V_GPI_IN3              0x08
+#define V_GPI_IN4              0x10
+#define V_GPI_IN5              0x20
+#define V_GPI_IN6              0x40
+#define V_GPI_IN7              0x80
+/* R_GPI_IN1 */
+#define V_GPI_IN8              0x01
+#define V_GPI_IN9              0x02
+#define V_GPI_IN10             0x04
+#define V_GPI_IN11             0x08
+#define V_GPI_IN12             0x10
+#define V_GPI_IN13             0x20
+#define V_GPI_IN14             0x40
+#define V_GPI_IN15             0x80
+/* R_GPI_IN2 */
+#define V_GPI_IN16             0x01
+#define V_GPI_IN17             0x02
+#define V_GPI_IN18             0x04
+#define V_GPI_IN19             0x08
+#define V_GPI_IN20             0x10
+#define V_GPI_IN21             0x20
+#define V_GPI_IN22             0x40
+#define V_GPI_IN23             0x80
+/* R_GPI_IN3 */
+#define V_GPI_IN24             0x01
+#define V_GPI_IN25             0x02
+#define V_GPI_IN26             0x04
+#define V_GPI_IN27             0x08
+#define V_GPI_IN28             0x10
+#define V_GPI_IN29             0x20
+#define V_GPI_IN30             0x40
+#define V_GPI_IN31             0x80
+
+/* map of all registers, used for debugging */
+
+#ifdef HFC_REGISTER_DEBUG
+struct hfc_register_names {
+       char *name;
+       u_char reg;
+} hfc_register_names[] = {
+       /* write registers */
+       {"R_CIRM",              0x00},
+       {"R_CTRL",              0x01},
+       {"R_BRG_PCM_CFG ",      0x02},
+       {"R_RAM_ADDR0",         0x08},
+       {"R_RAM_ADDR1",         0x09},
+       {"R_RAM_ADDR2",         0x0A},
+       {"R_FIRST_FIFO",        0x0B},
+       {"R_RAM_SZ",            0x0C},
+       {"R_FIFO_MD",           0x0D},
+       {"R_INC_RES_FIFO",      0x0E},
+       {"R_FIFO / R_FSM_IDX",  0x0F},
+       {"R_SLOT",              0x10},
+       {"R_IRQMSK_MISC",       0x11},
+       {"R_SCI_MSK",           0x12},
+       {"R_IRQ_CTRL",          0x13},
+       {"R_PCM_MD0",           0x14},
+       {"R_0x15",              0x15},
+       {"R_ST_SEL",            0x16},
+       {"R_ST_SYNC",           0x17},
+       {"R_CONF_EN",           0x18},
+       {"R_TI_WD",             0x1A},
+       {"R_BERT_WD_MD",        0x1B},
+       {"R_DTMF",              0x1C},
+       {"R_DTMF_N",            0x1D},
+       {"R_E1_XX_STA",         0x20},
+       {"R_LOS0",              0x22},
+       {"R_LOS1",              0x23},
+       {"R_RX0",               0x24},
+       {"R_RX_FR0",            0x25},
+       {"R_RX_FR1",            0x26},
+       {"R_TX0",               0x28},
+       {"R_TX1",               0x29},
+       {"R_TX_FR0",            0x2C},
+       {"R_TX_FR1",            0x2D},
+       {"R_TX_FR2",            0x2E},
+       {"R_JATT_ATT",          0x2F},
+       {"A_ST_xx_STA/R_RX_OFF", 0x30},
+       {"A_ST_CTRL0/R_SYNC_OUT", 0x31},
+       {"A_ST_CTRL1",          0x32},
+       {"A_ST_CTRL2",          0x33},
+       {"A_ST_SQ_WR",          0x34},
+       {"R_TX_OFF",            0x34},
+       {"R_SYNC_CTRL",         0x35},
+       {"A_ST_CLK_DLY",        0x37},
+       {"R_PWM0",              0x38},
+       {"R_PWM1",              0x39},
+       {"A_ST_B1_TX",          0x3C},
+       {"A_ST_B2_TX",          0x3D},
+       {"A_ST_D_TX",           0x3E},
+       {"R_GPIO_OUT0",         0x40},
+       {"R_GPIO_OUT1",         0x41},
+       {"R_GPIO_EN0",          0x42},
+       {"R_GPIO_EN1",          0x43},
+       {"R_GPIO_SEL",          0x44},
+       {"R_BRG_CTRL",          0x45},
+       {"R_PWM_MD",            0x46},
+       {"R_BRG_MD",            0x47},
+       {"R_BRG_TIM0",          0x48},
+       {"R_BRG_TIM1",          0x49},
+       {"R_BRG_TIM2",          0x4A},
+       {"R_BRG_TIM3",          0x4B},
+       {"R_BRG_TIM_SEL01",     0x4C},
+       {"R_BRG_TIM_SEL23",     0x4D},
+       {"R_BRG_TIM_SEL45",     0x4E},
+       {"R_BRG_TIM_SEL67",     0x4F},
+       {"A_FIFO_DATA0-2",      0x80},
+       {"A_FIFO_DATA0-2_NOINC", 0x84},
+       {"R_RAM_DATA",          0xC0},
+       {"A_SL_CFG",            0xD0},
+       {"A_CONF",              0xD1},
+       {"A_CH_MSK",            0xF4},
+       {"A_CON_HDLC",          0xFA},
+       {"A_SUBCH_CFG",         0xFB},
+       {"A_CHANNEL",           0xFC},
+       {"A_FIFO_SEQ",          0xFD},
+       {"A_IRQ_MSK",           0xFF},
+       {NULL, 0},
+
+       /* read registers */
+       {"A_Z1",                0x04},
+       {"A_Z1H",               0x05},
+       {"A_Z2",                0x06},
+       {"A_Z2H",               0x07},
+       {"A_F1",                0x0C},
+       {"A_F2",                0x0D},
+       {"R_IRQ_OVIEW",         0x10},
+       {"R_IRQ_MISC",          0x11},
+       {"R_IRQ_STATECH",       0x12},
+       {"R_CONF_OFLOW",        0x14},
+       {"R_RAM_USE",           0x15},
+       {"R_CHIP_ID",           0x16},
+       {"R_BERT_STA",          0x17},
+       {"R_F0_CNTL",           0x18},
+       {"R_F0_CNTH",           0x19},
+       {"R_BERT_ECL",          0x1A},
+       {"R_BERT_ECH",          0x1B},
+       {"R_STATUS",            0x1C},
+       {"R_CHIP_RV",           0x1F},
+       {"R_STATE",             0x20},
+       {"R_SYNC_STA",          0x24},
+       {"R_RX_SL0_0",          0x25},
+       {"R_RX_SL0_1",          0x26},
+       {"R_RX_SL0_2",          0x27},
+       {"R_JATT_DIR",          0x2b},
+       {"R_SLIP",              0x2c},
+       {"A_ST_RD_STA",         0x30},
+       {"R_FAS_ECL",           0x30},
+       {"R_FAS_ECH",           0x31},
+       {"R_VIO_ECL",           0x32},
+       {"R_VIO_ECH",           0x33},
+       {"R_CRC_ECL / A_ST_SQ_RD", 0x34},
+       {"R_CRC_ECH",           0x35},
+       {"R_E_ECL",             0x36},
+       {"R_E_ECH",             0x37},
+       {"R_SA6_SA13_ECL",      0x38},
+       {"R_SA6_SA13_ECH",      0x39},
+       {"R_SA6_SA23_ECL",      0x3A},
+       {"R_SA6_SA23_ECH",      0x3B},
+       {"A_ST_B1_RX",          0x3C},
+       {"A_ST_B2_RX",          0x3D},
+       {"A_ST_D_RX",           0x3E},
+       {"A_ST_E_RX",           0x3F},
+       {"R_GPIO_IN0",          0x40},
+       {"R_GPIO_IN1",          0x41},
+       {"R_GPI_IN0",           0x44},
+       {"R_GPI_IN1",           0x45},
+       {"R_GPI_IN2",           0x46},
+       {"R_GPI_IN3",           0x47},
+       {"A_FIFO_DATA0-2",      0x80},
+       {"A_FIFO_DATA0-2_NOINC", 0x84},
+       {"R_INT_DATA",          0x88},
+       {"R_RAM_DATA",          0xC0},
+       {"R_IRQ_FIFO_BL0",      0xC8},
+       {"R_IRQ_FIFO_BL1",      0xC9},
+       {"R_IRQ_FIFO_BL2",      0xCA},
+       {"R_IRQ_FIFO_BL3",      0xCB},
+       {"R_IRQ_FIFO_BL4",      0xCC},
+       {"R_IRQ_FIFO_BL5",      0xCD},
+       {"R_IRQ_FIFO_BL6",      0xCE},
+       {"R_IRQ_FIFO_BL7",      0xCF},
+};
+#endif /* HFC_REGISTER_DEBUG */
+
diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h
new file mode 100644 (file)
index 0000000..fd2c9be
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ *  specific defines for CCD's HFC 2BDS0 PCI chips
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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.
+ *
+ */
+
+/*
+ * thresholds for transparent B-channel mode
+ * change mask and threshold simultaneously
+ */
+#define HFCPCI_BTRANS_THRESHOLD 128
+#define HFCPCI_BTRANS_MAX      256
+#define HFCPCI_BTRANS_THRESMASK 0x00
+
+/* defines for PCI config */
+#define PCI_ENA_MEMIO          0x02
+#define PCI_ENA_MASTER         0x04
+
+/* GCI/IOM bus monitor registers */
+#define HCFPCI_C_I             0x08
+#define HFCPCI_TRxR            0x0C
+#define HFCPCI_MON1_D          0x28
+#define HFCPCI_MON2_D          0x2C
+
+/* GCI/IOM bus timeslot registers */
+#define HFCPCI_B1_SSL          0x80
+#define HFCPCI_B2_SSL          0x84
+#define HFCPCI_AUX1_SSL                0x88
+#define HFCPCI_AUX2_SSL                0x8C
+#define HFCPCI_B1_RSL          0x90
+#define HFCPCI_B2_RSL          0x94
+#define HFCPCI_AUX1_RSL                0x98
+#define HFCPCI_AUX2_RSL                0x9C
+
+/* GCI/IOM bus data registers */
+#define HFCPCI_B1_D            0xA0
+#define HFCPCI_B2_D            0xA4
+#define HFCPCI_AUX1_D          0xA8
+#define HFCPCI_AUX2_D          0xAC
+
+/* GCI/IOM bus configuration registers */
+#define HFCPCI_MST_EMOD                0xB4
+#define HFCPCI_MST_MODE                0xB8
+#define HFCPCI_CONNECT                 0xBC
+
+
+/* Interrupt and status registers */
+#define HFCPCI_FIFO_EN         0x44
+#define HFCPCI_TRM             0x48
+#define HFCPCI_B_MODE          0x4C
+#define HFCPCI_CHIP_ID         0x58
+#define HFCPCI_CIRM            0x60
+#define HFCPCI_CTMT            0x64
+#define HFCPCI_INT_M1          0x68
+#define HFCPCI_INT_M2          0x6C
+#define HFCPCI_INT_S1          0x78
+#define HFCPCI_INT_S2          0x7C
+#define HFCPCI_STATUS          0x70
+
+/* S/T section registers */
+#define HFCPCI_STATES          0xC0
+#define HFCPCI_SCTRL           0xC4
+#define HFCPCI_SCTRL_E         0xC8
+#define HFCPCI_SCTRL_R         0xCC
+#define HFCPCI_SQ              0xD0
+#define HFCPCI_CLKDEL          0xDC
+#define HFCPCI_B1_REC          0xF0
+#define HFCPCI_B1_SEND         0xF0
+#define HFCPCI_B2_REC          0xF4
+#define HFCPCI_B2_SEND         0xF4
+#define HFCPCI_D_REC           0xF8
+#define HFCPCI_D_SEND          0xF8
+#define HFCPCI_E_REC           0xFC
+
+
+/* bits in status register (READ) */
+#define HFCPCI_PCI_PROC                0x02
+#define HFCPCI_NBUSY           0x04
+#define HFCPCI_TIMER_ELAP      0x10
+#define HFCPCI_STATINT         0x20
+#define HFCPCI_FRAMEINT                0x40
+#define HFCPCI_ANYINT          0x80
+
+/* bits in CTMT (Write) */
+#define HFCPCI_CLTIMER         0x80
+#define HFCPCI_TIM3_125                0x04
+#define HFCPCI_TIM25           0x10
+#define HFCPCI_TIM50           0x14
+#define HFCPCI_TIM400          0x18
+#define HFCPCI_TIM800          0x1C
+#define HFCPCI_AUTO_TIMER      0x20
+#define HFCPCI_TRANSB2         0x02
+#define HFCPCI_TRANSB1         0x01
+
+/* bits in CIRM (Write) */
+#define HFCPCI_AUX_MSK         0x07
+#define HFCPCI_RESET           0x08
+#define HFCPCI_B1_REV          0x40
+#define HFCPCI_B2_REV          0x80
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCPCI_INTS_B1TRANS    0x01
+#define HFCPCI_INTS_B2TRANS    0x02
+#define HFCPCI_INTS_DTRANS     0x04
+#define HFCPCI_INTS_B1REC      0x08
+#define HFCPCI_INTS_B2REC      0x10
+#define HFCPCI_INTS_DREC       0x20
+#define HFCPCI_INTS_L1STATE    0x40
+#define HFCPCI_INTS_TIMER      0x80
+
+/* bits in INT_M2 */
+#define HFCPCI_PROC_TRANS      0x01
+#define HFCPCI_GCI_I_CHG       0x02
+#define HFCPCI_GCI_MON_REC     0x04
+#define HFCPCI_IRQ_ENABLE      0x08
+#define HFCPCI_PMESEL          0x80
+
+/* bits in STATES */
+#define HFCPCI_STATE_MSK       0x0F
+#define HFCPCI_LOAD_STATE      0x10
+#define HFCPCI_ACTIVATE                0x20
+#define HFCPCI_DO_ACTION       0x40
+#define HFCPCI_NT_G2_G3                0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCPCI_MASTER          0x01
+#define HFCPCI_SLAVE           0x00
+#define HFCPCI_F0IO_POSITIV    0x02
+#define HFCPCI_F0_NEGATIV      0x04
+#define HFCPCI_F0_2C4          0x08
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA           0x01
+#define SCTRL_B2_ENA           0x02
+#define SCTRL_MODE_TE          0x00
+#define SCTRL_MODE_NT          0x04
+#define SCTRL_LOW_PRIO         0x08
+#define SCTRL_SQ_ENA           0x10
+#define SCTRL_TEST             0x20
+#define SCTRL_NONE_CAP         0x40
+#define SCTRL_PWR_DOWN         0x80
+
+/* bits in SCTRL_E  */
+#define HFCPCI_AUTO_AWAKE      0x01
+#define HFCPCI_DBIT_1          0x04
+#define HFCPCI_IGNORE_COL      0x08
+#define HFCPCI_CHG_B1_B2       0x80
+
+/* bits in FIFO_EN register */
+#define HFCPCI_FIFOEN_B1       0x03
+#define HFCPCI_FIFOEN_B2       0x0C
+#define HFCPCI_FIFOEN_DTX      0x10
+#define HFCPCI_FIFOEN_B1TX     0x01
+#define HFCPCI_FIFOEN_B1RX     0x02
+#define HFCPCI_FIFOEN_B2TX     0x04
+#define HFCPCI_FIFOEN_B2RX     0x08
+
+
+/* definitions of fifo memory area */
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL    0x200
+#define B_FIFO_SIZE  (0x2000 - B_SUB_VAL)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+struct zt {
+       unsigned short z1;  /* Z1 pointer 16 Bit */
+       unsigned short z2;  /* Z2 pointer 16 Bit */
+};
+
+struct dfifo {
+       u_char data[D_FIFO_SIZE]; /* FIFO data space */
+       u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */
+       u_char f1, f2; /* f pointers */
+       u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */
+       /* mask index with D_FREG_MASK for access */
+       struct zt za[MAX_D_FRAMES+1];
+       u_char fill3[0x4000-0x2100]; /* align 16K */
+};
+
+struct bzfifo {
+       struct zt       za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */
+       u_char          f1, f2; /* f pointers */
+       u_char          fill[0x2100-0x2082]; /* alignment */
+};
+
+
+union fifo_area {
+       struct {
+               struct dfifo d_tx; /* D-send channel */
+               struct dfifo d_rx; /* D-receive channel */
+       } d_chan;
+       struct {
+               u_char          fill1[0x200];
+               u_char          txdat_b1[B_FIFO_SIZE];
+               struct bzfifo   txbz_b1;
+               struct bzfifo   txbz_b2;
+               u_char          txdat_b2[B_FIFO_SIZE];
+               u_char          fill2[D_FIFO_SIZE];
+               u_char          rxdat_b1[B_FIFO_SIZE];
+               struct bzfifo   rxbz_b1;
+               struct bzfifo   rxbz_b2;
+               u_char rxdat_b2[B_FIFO_SIZE];
+       } b_chans;
+       u_char fill[32768];
+};
+
+#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io)+b))
+#define Read_hfc(a, b) (readb((a->hw.pci_io)+b))
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
new file mode 100644 (file)
index 0000000..2649ea5
--- /dev/null
@@ -0,0 +1,5320 @@
+/*
+ * hfcmulti.c  low level driver for hfc-4s/hfc-8s/hfc-e1 based cards
+ *
+ * Author      Andreas Eversberg (jolly@eversberg.eu)
+ * ported to mqueue mechanism:
+ *             Peter Sprenger (sprengermoving-bytes.de)
+ *
+ * inspired by existing hfc-pci driver:
+ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008  by Karsten Keil (kkeil@suse.de)
+ * Copyright 2008  by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Thanks to Cologne Chip AG for this great controller!
+ */
+
+/*
+ * module parameters:
+ * type:
+ *     By default (0), the card is automatically detected.
+ *     Or use the following combinations:
+ *     Bit 0-7   = 0x00001 = HFC-E1 (1 port)
+ * or  Bit 0-7   = 0x00004 = HFC-4S (4 ports)
+ * or  Bit 0-7   = 0x00008 = HFC-8S (8 ports)
+ *     Bit 8     = 0x00100 = uLaw (instead of aLaw)
+ *     Bit 9     = 0x00200 = Disable DTMF detect on all B-channels via hardware
+ *     Bit 10    = spare
+ *     Bit 11    = 0x00800 = Force PCM bus into slave mode. (otherwhise auto)
+ * or   Bit 12    = 0x01000 = Force PCM bus into master mode. (otherwhise auto)
+ *     Bit 13    = spare
+ *     Bit 14    = 0x04000 = Use external ram (128K)
+ *     Bit 15    = 0x08000 = Use external ram (512K)
+ *     Bit 16    = 0x10000 = Use 64 timeslots instead of 32
+ * or  Bit 17    = 0x20000 = Use 128 timeslots instead of anything else
+ *     Bit 18    = spare
+ *     Bit 19    = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog)
+ * (all other bits are reserved and shall be 0)
+ *     example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM
+ *              bus (PCM master)
+ *
+ * port: (optional or required for all ports on all installed cards)
+ *     HFC-4S/HFC-8S only bits:
+ *     Bit 0     = 0x001 = Use master clock for this S/T interface
+ *                         (ony once per chip).
+ *     Bit 1     = 0x002 = transmitter line setup (non capacitive mode)
+ *                         Don't use this unless you know what you are doing!
+ *     Bit 2     = 0x004 = Disable E-channel. (No E-channel processing)
+ *     example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock
+ *              received from port 1
+ *
+ *     HFC-E1 only bits:
+ *     Bit 0     = 0x0001 = interface: 0=copper, 1=optical
+ *     Bit 1     = 0x0002 = reserved (later for 32 B-channels transparent mode)
+ *     Bit 2     = 0x0004 = Report LOS
+ *     Bit 3     = 0x0008 = Report AIS
+ *     Bit 4     = 0x0010 = Report SLIP
+ *     Bit 5     = 0x0020 = Report RDI
+ *     Bit 8     = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame
+ *                          mode instead.
+ *     Bit 9     = 0x0200 = Force get clock from interface, even in NT mode.
+ * or  Bit 10    = 0x0400 = Force put clock to interface, even in TE mode.
+ *     Bit 11    = 0x0800 = Use direct RX clock for PCM sync rather than PLL.
+ *                          (E1 only)
+ *     Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0
+ *                          for default.
+ * (all other bits are reserved and shall be 0)
+ *
+ * debug:
+ *     NOTE: only one debug value must be given for all cards
+ *     enable debugging (see hfc_multi.h for debug options)
+ *
+ * poll:
+ *     NOTE: only one poll value must be given for all cards
+ *     Give the number of samples for each fifo process.
+ *     By default 128 is used. Decrease to reduce delay, increase to
+ *     reduce cpu load. If unsure, don't mess with it!
+ *     Valid is 8, 16, 32, 64, 128, 256.
+ *
+ * pcm:
+ *     NOTE: only one pcm value must be given for every card.
+ *     The PCM bus id tells the mISDNdsp module about the connected PCM bus.
+ *     By default (0), the PCM bus id is 100 for the card that is PCM master.
+ *     If multiple cards are PCM master (because they are not interconnected),
+ *     each card with PCM master will have increasing PCM id.
+ *     All PCM busses with the same ID are expected to be connected and have
+ *     common time slots slots.
+ *     Only one chip of the PCM bus must be master, the others slave.
+ *     -1 means no support of PCM bus not even.
+ *     Omit this value, if all cards are interconnected or none is connected.
+ *     If unsure, don't give this parameter.
+ *
+ * dslot:
+ *     NOTE: only one poll value must be given for every card.
+ *     Also this value must be given for non-E1 cards. If omitted, the E1
+ *     card has D-channel on time slot 16, which is default.
+ *     If 1..15 or 17..31, an alternate time slot is used for D-channel.
+ *     In this case, the application must be able to handle this.
+ *     If -1 is given, the D-channel is disabled and all 31 slots can be used
+ *     for B-channel. (only for specific applications)
+ *     If you don't know how to use it, you don't need it!
+ *
+ * iomode:
+ *     NOTE: only one mode value must be given for every card.
+ *     -> See hfc_multi.h for HFC_IO_MODE_* values
+ *     By default, the IO mode is pci memory IO (MEMIO).
+ *     Some cards requre specific IO mode, so it cannot be changed.
+ *     It may be usefull to set IO mode to register io (REGIO) to solve
+ *     PCI bridge problems.
+ *     If unsure, don't give this parameter.
+ *
+ * clockdelay_nt:
+ *     NOTE: only one clockdelay_nt value must be given once for all cards.
+ *     Give the value of the clock control register (A_ST_CLK_DLY)
+ *     of the S/T interfaces in NT mode.
+ *     This register is needed for the TBR3 certification, so don't change it.
+ *
+ * clockdelay_te:
+ *     NOTE: only one clockdelay_te value must be given once
+ *     Give the value of the clock control register (A_ST_CLK_DLY)
+ *     of the S/T interfaces in TE mode.
+ *     This register is needed for the TBR3 certification, so don't change it.
+ */
+
+/*
+ * debug register access (never use this, it will flood your system log)
+ * #define HFC_REGISTER_DEBUG
+ */
+
+static const char *hfcmulti_revision = "2.00";
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+
+/*
+#define IRQCOUNT_DEBUG
+#define IRQ_DEBUG
+*/
+
+#include "hfc_multi.h"
+#ifdef ECHOPREP
+#include "gaintab.h"
+#endif
+
+#define        MAX_CARDS       8
+#define        MAX_PORTS       (8 * MAX_CARDS)
+
+static LIST_HEAD(HFClist);
+static spinlock_t HFClock; /* global hfc list lock */
+
+static void ph_state_change(struct dchannel *);
+static void (*hfc_interrupt)(void);
+static void (*register_interrupt)(void);
+static int (*unregister_interrupt)(void);
+static int interrupt_registered;
+
+static struct hfc_multi *syncmaster;
+int plxsd_master; /* if we have a master card (yet) */
+static spinlock_t plx_lock; /* may not acquire other lock inside */
+EXPORT_SYMBOL(plx_lock);
+
+#define        TYP_E1          1
+#define        TYP_4S          4
+#define TYP_8S         8
+
+static int poll_timer = 6;     /* default = 128 samples = 16ms */
+/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */
+static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30  };
+#define        CLKDEL_TE       0x0f    /* CLKDEL in TE mode */
+#define        CLKDEL_NT       0x6c    /* CLKDEL in NT mode
+                                  (0x60 MUST be included!) */
+static u_char silence =        0xff;   /* silence by LAW */
+
+#define        DIP_4S  0x1             /* DIP Switches for Beronet 1S/2S/4S cards */
+#define        DIP_8S  0x2             /* DIP Switches for Beronet 8S+ cards */
+#define        DIP_E1  0x3             /* DIP Switches for Beronet E1 cards */
+
+/*
+ * module stuff
+ */
+
+static uint    type[MAX_CARDS];
+static uint    pcm[MAX_CARDS];
+static uint    dslot[MAX_CARDS];
+static uint    iomode[MAX_CARDS];
+static uint    port[MAX_PORTS];
+static uint    debug;
+static uint    poll;
+static uint    timer;
+static uint    clockdelay_te = CLKDEL_TE;
+static uint    clockdelay_nt = CLKDEL_NT;
+
+static int     HFC_cnt, Port_cnt, PCM_cnt = 99;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(timer, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR);
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+
+#ifdef HFC_REGISTER_DEBUG
+#define HFC_outb(hc, reg, val) \
+       (hc->HFC_outb(hc, reg, val, __func__, __LINE__))
+#define HFC_outb_nodebug(hc, reg, val) \
+       (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__))
+#define HFC_inb(hc, reg) \
+       (hc->HFC_inb(hc, reg, __func__, __LINE__))
+#define HFC_inb_nodebug(hc, reg) \
+       (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_inw(hc, reg) \
+       (hc->HFC_inw(hc, reg, __func__, __LINE__))
+#define HFC_inw_nodebug(hc, reg) \
+       (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_wait(hc) \
+       (hc->HFC_wait(hc, __func__, __LINE__))
+#define HFC_wait_nodebug(hc) \
+       (hc->HFC_wait_nodebug(hc, __func__, __LINE__))
+#else
+#define HFC_outb(hc, reg, val)         (hc->HFC_outb(hc, reg, val))
+#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val))
+#define HFC_inb(hc, reg)               (hc->HFC_inb(hc, reg))
+#define HFC_inb_nodebug(hc, reg)       (hc->HFC_inb_nodebug(hc, reg))
+#define HFC_inw(hc, reg)               (hc->HFC_inw(hc, reg))
+#define HFC_inw_nodebug(hc, reg)       (hc->HFC_inw_nodebug(hc, reg))
+#define HFC_wait(hc)                   (hc->HFC_wait(hc))
+#define HFC_wait_nodebug(hc)           (hc->HFC_wait_nodebug(hc))
+#endif
+
+/* HFC_IO_MODE_PCIMEM */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val,
+               const char *function, int line)
+#else
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+       writeb(val, (hc->pci_membase)+reg);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       return readb((hc->pci_membase)+reg);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       return readw((hc->pci_membase)+reg);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_pcimem(struct hfc_multi *hc)
+#endif
+{
+       while (readb((hc->pci_membase)+R_STATUS) & V_BUSY);
+}
+
+/* HFC_IO_MODE_REGIO */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val,
+       const char *function, int line)
+#else
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+       outb(reg, (hc->pci_iobase)+4);
+       outb(val, hc->pci_iobase);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       outb(reg, (hc->pci_iobase)+4);
+       return inb(hc->pci_iobase);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       outb(reg, (hc->pci_iobase)+4);
+       return inw(hc->pci_iobase);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_regio(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_regio(struct hfc_multi *hc)
+#endif
+{
+       outb(R_STATUS, (hc->pci_iobase)+4);
+       while (inb(hc->pci_iobase) & V_BUSY);
+}
+
+#ifdef HFC_REGISTER_DEBUG
+static void
+HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val,
+               const char *function, int line)
+{
+       char regname[256] = "", bits[9] = "xxxxxxxx";
+       int i;
+
+       i = -1;
+       while (hfc_register_names[++i].name) {
+               if (hfc_register_names[i].reg == reg)
+                       strcat(regname, hfc_register_names[i].name);
+       }
+       if (regname[0] == '\0')
+               strcpy(regname, "register");
+
+       bits[7] = '0'+(!!(val&1));
+       bits[6] = '0'+(!!(val&2));
+       bits[5] = '0'+(!!(val&4));
+       bits[4] = '0'+(!!(val&8));
+       bits[3] = '0'+(!!(val&16));
+       bits[2] = '0'+(!!(val&32));
+       bits[1] = '0'+(!!(val&64));
+       bits[0] = '0'+(!!(val&128));
+       printk(KERN_DEBUG
+           "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n",
+           hc->id, reg, regname, val, bits, function, line);
+       HFC_outb_nodebug(hc, reg, val);
+}
+static u_char
+HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+       char regname[256] = "", bits[9] = "xxxxxxxx";
+       u_char val = HFC_inb_nodebug(hc, reg);
+       int i;
+
+       i = 0;
+       while (hfc_register_names[i++].name)
+               ;
+       while (hfc_register_names[++i].name) {
+               if (hfc_register_names[i].reg == reg)
+                       strcat(regname, hfc_register_names[i].name);
+       }
+       if (regname[0] == '\0')
+               strcpy(regname, "register");
+
+       bits[7] = '0'+(!!(val&1));
+       bits[6] = '0'+(!!(val&2));
+       bits[5] = '0'+(!!(val&4));
+       bits[4] = '0'+(!!(val&8));
+       bits[3] = '0'+(!!(val&16));
+       bits[2] = '0'+(!!(val&32));
+       bits[1] = '0'+(!!(val&64));
+       bits[0] = '0'+(!!(val&128));
+       printk(KERN_DEBUG
+           "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n",
+           hc->id, reg, regname, val, bits, function, line);
+       return val;
+}
+static u_short
+HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+       char regname[256] = "";
+       u_short val = HFC_inw_nodebug(hc, reg);
+       int i;
+
+       i = 0;
+       while (hfc_register_names[i++].name)
+               ;
+       while (hfc_register_names[++i].name) {
+               if (hfc_register_names[i].reg == reg)
+                       strcat(regname, hfc_register_names[i].name);
+       }
+       if (regname[0] == '\0')
+               strcpy(regname, "register");
+
+       printk(KERN_DEBUG
+           "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n",
+           hc->id, reg, regname, val, function, line);
+       return val;
+}
+static void
+HFC_wait_debug(struct hfc_multi *hc, const char *function, int line)
+{
+       printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n",
+           hc->id, function, line);
+       HFC_wait_nodebug(hc);
+}
+#endif
+
+/* write fifo data (REGIO) */
+void
+write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+       outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+       while (len>>2) {
+               outl(*(u32 *)data, hc->pci_iobase);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               outw(*(u16 *)data, hc->pci_iobase);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               outb(*data, hc->pci_iobase);
+               data++;
+               len--;
+       }
+}
+/* write fifo data (PCIMEM) */
+void
+write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+       while (len>>2) {
+               writel(*(u32 *)data, (hc->pci_membase)+A_FIFO_DATA0);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               writew(*(u16 *)data, (hc->pci_membase)+A_FIFO_DATA0);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               writeb(*data, (hc->pci_membase)+A_FIFO_DATA0);
+               data++;
+               len--;
+       }
+}
+/* read fifo data (REGIO) */
+void
+read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+       outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+       while (len>>2) {
+               *(u32 *)data = inl(hc->pci_iobase);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               *(u16 *)data = inw(hc->pci_iobase);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               *data = inb(hc->pci_iobase);
+               data++;
+               len--;
+       }
+}
+
+/* read fifo data (PCIMEM) */
+void
+read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+       while (len>>2) {
+               *(u32 *)data =
+                       readl((hc->pci_membase)+A_FIFO_DATA0);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               *(u16 *)data =
+                       readw((hc->pci_membase)+A_FIFO_DATA0);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               *data = readb((hc->pci_membase)+A_FIFO_DATA0);
+               data++;
+               len--;
+       }
+}
+
+
+static void
+enable_hwirq(struct hfc_multi *hc)
+{
+       hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN;
+       HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+static void
+disable_hwirq(struct hfc_multi *hc)
+{
+       hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN);
+       HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+#define        NUM_EC 2
+#define        MAX_TDM_CHAN 32
+
+
+inline void
+enablepcibridge(struct hfc_multi *c)
+{
+       HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */
+}
+
+inline void
+disablepcibridge(struct hfc_multi *c)
+{
+       HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */
+}
+
+inline unsigned char
+readpcibridge(struct hfc_multi *hc, unsigned char address)
+{
+       unsigned short cipv;
+       unsigned char data;
+
+       if (!hc->pci_iobase)
+               return 0;
+
+       /* slow down a PCI read access by 1 PCI clock cycle */
+       HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/
+
+       if (address == 0)
+               cipv = 0x4000;
+       else
+               cipv = 0x5800;
+
+       /* select local bridge port address by writing to CIP port */
+       /* data = HFC_inb(c, cipv); * was _io before */
+       outw(cipv, hc->pci_iobase + 4);
+       data = inb(hc->pci_iobase);
+
+       /* restore R_CTRL for normal PCI read cycle speed */
+       HFC_outb(hc, R_CTRL, 0x0); /* was _io before */
+
+       return data;
+}
+
+inline void
+writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data)
+{
+       unsigned short cipv;
+       unsigned int datav;
+
+       if (!hc->pci_iobase)
+               return;
+
+       if (address == 0)
+               cipv = 0x4000;
+       else
+               cipv = 0x5800;
+
+       /* select local bridge port address by writing to CIP port */
+       outw(cipv, hc->pci_iobase + 4);
+       /* define a 32 bit dword with 4 identical bytes for write sequence */
+       datav = data | ((__u32) data << 8) | ((__u32) data << 16) |
+           ((__u32) data << 24);
+
+       /*
+        * write this 32 bit dword to the bridge data port
+        * this will initiate a write sequence of up to 4 writes to the same
+        * address on the local bus interface the number of write accesses
+        * is undefined but >=1 and depends on the next PCI transaction
+        * during write sequence on the local bus
+        */
+       outl(datav, hc->pci_iobase);
+}
+
+inline void
+cpld_set_reg(struct hfc_multi *hc, unsigned char reg)
+{
+       /* Do data pin read low byte */
+       HFC_outb(hc, R_GPIO_OUT1, reg);
+}
+
+inline void
+cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val)
+{
+       cpld_set_reg(hc, reg);
+
+       enablepcibridge(hc);
+       writepcibridge(hc, 1, val);
+       disablepcibridge(hc);
+
+       return;
+}
+
+inline unsigned char
+cpld_read_reg(struct hfc_multi *hc, unsigned char reg)
+{
+       unsigned char bytein;
+
+       cpld_set_reg(hc, reg);
+
+       /* Do data pin read low byte */
+       HFC_outb(hc, R_GPIO_OUT1, reg);
+
+       enablepcibridge(hc);
+       bytein = readpcibridge(hc, 1);
+       disablepcibridge(hc);
+
+       return bytein;
+}
+
+inline void
+vpm_write_address(struct hfc_multi *hc, unsigned short addr)
+{
+       cpld_write_reg(hc, 0, 0xff & addr);
+       cpld_write_reg(hc, 1, 0x01 & (addr >> 8));
+}
+
+inline unsigned short
+vpm_read_address(struct hfc_multi *c)
+{
+       unsigned short addr;
+       unsigned short highbit;
+
+       addr = cpld_read_reg(c, 0);
+       highbit = cpld_read_reg(c, 1);
+
+       addr = addr | (highbit << 8);
+
+       return addr & 0x1ff;
+}
+
+inline unsigned char
+vpm_in(struct hfc_multi *c, int which, unsigned short addr)
+{
+       unsigned char res;
+
+       vpm_write_address(c, addr);
+
+       if (!which)
+               cpld_set_reg(c, 2);
+       else
+               cpld_set_reg(c, 3);
+
+       enablepcibridge(c);
+       res = readpcibridge(c, 1);
+       disablepcibridge(c);
+
+       cpld_set_reg(c, 0);
+
+       return res;
+}
+
+inline void
+vpm_out(struct hfc_multi *c, int which, unsigned short addr,
+    unsigned char data)
+{
+       vpm_write_address(c, addr);
+
+       enablepcibridge(c);
+
+       if (!which)
+               cpld_set_reg(c, 2);
+       else
+               cpld_set_reg(c, 3);
+
+       writepcibridge(c, 1, data);
+
+       cpld_set_reg(c, 0);
+
+       disablepcibridge(c);
+
+       {
+       unsigned char regin;
+       regin = vpm_in(c, which, addr);
+       if (regin != data)
+               printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back "
+                       "0x%x\n", data, addr, regin);
+       }
+
+}
+
+
+void
+vpm_init(struct hfc_multi *wc)
+{
+       unsigned char reg;
+       unsigned int mask;
+       unsigned int i, x, y;
+       unsigned int ver;
+
+       for (x = 0; x < NUM_EC; x++) {
+               /* Setup GPIO's */
+               if (!x) {
+                       ver = vpm_in(wc, x, 0x1a0);
+                       printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver);
+               }
+
+               for (y = 0; y < 4; y++) {
+                       vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
+                       vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
+                       vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
+               }
+
+               /* Setup TDM path - sets fsync and tdm_clk as inputs */
+               reg = vpm_in(wc, x, 0x1a3); /* misc_con */
+               vpm_out(wc, x, 0x1a3, reg & ~2);
+
+               /* Setup Echo length (256 taps) */
+               vpm_out(wc, x, 0x022, 1);
+               vpm_out(wc, x, 0x023, 0xff);
+
+               /* Setup timeslots */
+               vpm_out(wc, x, 0x02f, 0x00);
+               mask = 0x02020202 << (x * 4);
+
+               /* Setup the tdm channel masks for all chips */
+               for (i = 0; i < 4; i++)
+                       vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff);
+
+               /* Setup convergence rate */
+               printk(KERN_DEBUG "VPM: A-law mode\n");
+               reg = 0x00 | 0x10 | 0x01;
+               vpm_out(wc, x, 0x20, reg);
+               printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg);
+               /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */
+
+               vpm_out(wc, x, 0x24, 0x02);
+               reg = vpm_in(wc, x, 0x24);
+               printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg);
+
+               /* Initialize echo cans */
+               for (i = 0; i < MAX_TDM_CHAN; i++) {
+                       if (mask & (0x00000001 << i))
+                               vpm_out(wc, x, i, 0x00);
+               }
+
+               /*
+                * ARM arch at least disallows a udelay of
+                * more than 2ms... it gives a fake "__bad_udelay"
+                * reference at link-time.
+                * long delays in kernel code are pretty sucky anyway
+                * for now work around it using 5 x 2ms instead of 1 x 10ms
+                */
+
+               udelay(2000);
+               udelay(2000);
+               udelay(2000);
+               udelay(2000);
+               udelay(2000);
+
+               /* Put in bypass mode */
+               for (i = 0; i < MAX_TDM_CHAN; i++) {
+                       if (mask & (0x00000001 << i))
+                               vpm_out(wc, x, i, 0x01);
+               }
+
+               /* Enable bypass */
+               for (i = 0; i < MAX_TDM_CHAN; i++) {
+                       if (mask & (0x00000001 << i))
+                               vpm_out(wc, x, 0x78 + i, 0x01);
+               }
+
+       }
+}
+
+void
+vpm_check(struct hfc_multi *hctmp)
+{
+       unsigned char gpi2;
+
+       gpi2 = HFC_inb(hctmp, R_GPI_IN2);
+
+       if ((gpi2 & 0x3) != 0x3)
+               printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
+}
+
+
+/*
+ * Interface to enable/disable the HW Echocan
+ *
+ * these functions are called within a spin_lock_irqsave on
+ * the channel instance lock, so we are not disturbed by irqs
+ *
+ * we can later easily change the interface to make  other
+ * things configurable, for now we configure the taps
+ *
+ */
+
+void
+vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
+{
+       unsigned int timeslot;
+       unsigned int unit;
+       struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+       int txadj = -4;
+       struct sk_buff *skb;
+#endif
+       if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+               return;
+
+       if (!bch)
+               return;
+
+#ifdef TXADJ
+       skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+               sizeof(int), &txadj, GFP_ATOMIC);
+       if (skb)
+               recv_Bchannel_skb(bch, skb);
+#endif
+
+       timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+       unit = ch % 4;
+
+       printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n",
+           taps, timeslot);
+
+       vpm_out(hc, unit, timeslot, 0x7e);
+}
+
+void
+vpm_echocan_off(struct hfc_multi *hc, int ch)
+{
+       unsigned int timeslot;
+       unsigned int unit;
+       struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+       int txadj = 0;
+       struct sk_buff *skb;
+#endif
+
+       if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+               return;
+
+       if (!bch)
+               return;
+
+#ifdef TXADJ
+       skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+               sizeof(int), &txadj, GFP_ATOMIC);
+       if (skb)
+               recv_Bchannel_skb(bch, skb);
+#endif
+
+       timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+       unit = ch % 4;
+
+       printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n",
+           timeslot);
+       /* FILLME */
+       vpm_out(hc, unit, timeslot, 0x01);
+}
+
+
+/*
+ * Speech Design resync feature
+ * NOTE: This is called sometimes outside interrupt handler.
+ * We must lock irqsave, so no other interrupt (other card) will occurr!
+ * Also multiple interrupts may nest, so must lock each access (lists, card)!
+ */
+static inline void
+hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
+{
+       struct hfc_multi *hc, *next, *pcmmaster = 0;
+       u_int *plx_acc_32, pv;
+       u_long flags;
+
+       spin_lock_irqsave(&HFClock, flags);
+       spin_lock(&plx_lock); /* must be locked inside other locks */
+
+       if (debug & DEBUG_HFCMULTI_PLXSD)
+               printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n",
+                       __func__, syncmaster);
+
+       /* select new master */
+       if (newmaster) {
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "using provided controller\n");
+       } else {
+               list_for_each_entry_safe(hc, next, &HFClist, list) {
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                               if (hc->syncronized) {
+                                       newmaster = hc;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* Disable sync of all cards */
+       list_for_each_entry_safe(hc, next, &HFClist, list) {
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+                       pv = readl(plx_acc_32);
+                       pv &= ~PLX_SYNC_O_EN;
+                       writel(pv, plx_acc_32);
+                       if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+                               pcmmaster = hc;
+                               if (hc->type == 1) {
+                                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                                               printk(KERN_DEBUG
+                                                       "Schedule SYNC_I\n");
+                                       hc->e1_resync |= 1; /* get SYNC_I */
+                               }
+                       }
+               }
+       }
+
+       if (newmaster) {
+               hc = newmaster;
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "id=%d (0x%p) = syncronized with "
+                               "interface.\n", hc->id, hc);
+               /* Enable new sync master */
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               pv = readl(plx_acc_32);
+               pv |= PLX_SYNC_O_EN;
+               writel(pv, plx_acc_32);
+               /* switch to jatt PLL, if not disabled by RX_SYNC */
+               if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "Schedule jatt PLL\n");
+                       hc->e1_resync |= 2; /* switch to jatt */
+               }
+       } else {
+               if (pcmmaster) {
+                       hc = pcmmaster;
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG
+                                       "id=%d (0x%p) = PCM master syncronized "
+                                       "with QUARTZ\n", hc->id, hc);
+                       if (hc->type == 1) {
+                               /* Use the crystal clock for the PCM
+                                  master card */
+                               if (debug & DEBUG_HFCMULTI_PLXSD)
+                                       printk(KERN_DEBUG
+                                           "Schedule QUARTZ for HFC-E1\n");
+                               hc->e1_resync |= 4; /* switch quartz */
+                       } else {
+                               if (debug & DEBUG_HFCMULTI_PLXSD)
+                                       printk(KERN_DEBUG
+                                           "QUARTZ is automatically "
+                                           "enabled by HFC-%dS\n", hc->type);
+                       }
+                       plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+                       pv = readl(plx_acc_32);
+                       pv |= PLX_SYNC_O_EN;
+                       writel(pv, plx_acc_32);
+               } else
+                       if (!rm)
+                               printk(KERN_ERR "%s no pcm master, this MUST "
+                                       "not happen!\n", __func__);
+       }
+       syncmaster = newmaster;
+
+       spin_unlock(&plx_lock);
+       spin_unlock_irqrestore(&HFClock, flags);
+}
+
+/* This must be called AND hc must be locked irqsave!!! */
+inline void
+plxsd_checksync(struct hfc_multi *hc, int rm)
+{
+       if (hc->syncronized) {
+               if (syncmaster == NULL) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_WARNING "%s: GOT sync on card %d"
+                                       " (id=%d)\n", __func__, hc->id + 1,
+                                       hc->id);
+                       hfcmulti_resync(hc, hc, rm);
+               }
+       } else {
+               if (syncmaster == hc) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_WARNING "%s: LOST sync on card %d"
+                                       " (id=%d)\n", __func__, hc->id + 1,
+                                       hc->id);
+                       hfcmulti_resync(hc, NULL, rm);
+               }
+       }
+}
+
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcmulti(struct hfc_multi *hc)
+{
+       u_int   *plx_acc_32, pv;
+       u_long  plx_flags;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+
+       /* soft reset also masks all interrupts */
+       hc->hw.r_cirm |= V_SRES;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(1000);
+       hc->hw.r_cirm &= ~V_SRES;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(1000); /* instead of 'wait' that may cause locking */
+
+       /* release Speech Design card, if PLX was initialized */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) {
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "%s: release PLXSD card %d\n",
+                           __func__, hc->id + 1);
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               writel(PLX_GPIOC_INIT, plx_acc_32);
+               pv = readl(plx_acc_32);
+               /* Termination off */
+               pv &= ~PLX_TERM_ON;
+               /* Disconnect the PCM */
+               pv |= PLX_SLAVE_EN_N;
+               pv &= ~PLX_MASTER_EN;
+               pv &= ~PLX_SYNC_O_EN;
+               /* Put the DSP in Reset */
+               pv &= ~PLX_DSP_RES_N;
+               writel(pv, plx_acc_32);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n",
+                               __func__, pv);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       /* disable memory mapped ports / io ports */
+       test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */
+       pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
+       if (hc->pci_membase)
+               iounmap((void *)hc->pci_membase);
+       if (hc->plx_membase)
+               iounmap((void *)hc->plx_membase);
+       if (hc->pci_iobase)
+               release_region(hc->pci_iobase, 8);
+
+       if (hc->pci_dev) {
+               pci_disable_device(hc->pci_dev);
+               pci_set_drvdata(hc->pci_dev, NULL);
+       }
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done\n", __func__);
+}
+
+/*
+ * function called to reset the HFC chip. A complete software reset of chip
+ * and fifos is done. All configuration of the chip is done.
+ */
+
+static int
+init_chip(struct hfc_multi *hc)
+{
+       u_long                  flags, val, val2 = 0, rev;
+       int                     i, err = 0;
+       u_char                  r_conf_en, rval;
+       u_int                   *plx_acc_32, pv;
+       u_long                  plx_flags, hfc_flags;
+       int                     plx_count;
+       struct hfc_multi        *pos, *next, *plx_last_hc;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       /* reset all registers */
+       memset(&hc->hw, 0, sizeof(struct hfcm_hw));
+
+       /* revision check */
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+       val = HFC_inb(hc, R_CHIP_ID)>>4;
+       if (val != 0x8 && val != 0xc && val != 0xe) {
+               printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val);
+               err = -EIO;
+               goto out;
+       }
+       rev = HFC_inb(hc, R_CHIP_RV);
+       printk(KERN_INFO
+           "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n",
+           val, rev, (rev == 0) ? " (old FIFO handling)" : "");
+       if (rev == 0) {
+               test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip);
+               printk(KERN_WARNING
+                   "HFC_multi: NOTE: Your chip is revision 0, "
+                   "ask Cologne Chip for update. Newer chips "
+                   "have a better FIFO handling. Old chips "
+                   "still work but may have slightly lower "
+                   "HDLC transmit performance.\n");
+       }
+       if (rev > 1) {
+               printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't "
+                   "consider chip revision = %ld. The chip / "
+                   "bridge may not work.\n", rev);
+       }
+
+       /* set s-ram size */
+       hc->Flen = 0x10;
+       hc->Zmin = 0x80;
+       hc->Zlen = 384;
+       hc->DTMFbase = 0x1000;
+       if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n",
+                           __func__);
+               hc->hw.r_ctrl |= V_EXT_RAM;
+               hc->hw.r_ram_sz = 1;
+               hc->Flen = 0x20;
+               hc->Zmin = 0xc0;
+               hc->Zlen = 1856;
+               hc->DTMFbase = 0x2000;
+       }
+       if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n",
+                           __func__);
+               hc->hw.r_ctrl |= V_EXT_RAM;
+               hc->hw.r_ram_sz = 2;
+               hc->Flen = 0x20;
+               hc->Zmin = 0xc0;
+               hc->Zlen = 8000;
+               hc->DTMFbase = 0x2000;
+       }
+       hc->max_trans = poll << 1;
+       if (hc->max_trans > hc->Zlen)
+               hc->max_trans = hc->Zlen;
+
+       /* Speech Design PLX bridge */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "%s: initializing PLXSD card %d\n",
+                           __func__, hc->id + 1);
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               writel(PLX_GPIOC_INIT, plx_acc_32);
+               pv = readl(plx_acc_32);
+               /* The first and the last cards are terminating the PCM bus */
+               pv |= PLX_TERM_ON; /* hc is currently the last */
+               /* Disconnect the PCM */
+               pv |= PLX_SLAVE_EN_N;
+               pv &= ~PLX_MASTER_EN;
+               pv &= ~PLX_SYNC_O_EN;
+               /* Put the DSP in Reset */
+               pv &= ~PLX_DSP_RES_N;
+               writel(pv, plx_acc_32);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n",
+                               __func__, pv);
+               /*
+                * If we are the 3rd PLXSD card or higher, we must turn
+                * termination of last PLXSD card off.
+                */
+               spin_lock_irqsave(&HFClock, hfc_flags);
+               plx_count = 0;
+               plx_last_hc = NULL;
+               list_for_each_entry_safe(pos, next, &HFClist, list) {
+                       if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) {
+                               plx_count++;
+                               if (pos != hc)
+                                       plx_last_hc = pos;
+                       }
+               }
+               if (plx_count >= 3) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "%s: card %d is between, so "
+                                       "we disable termination\n",
+                                   __func__, plx_last_hc->id + 1);
+                       spin_lock_irqsave(&plx_lock, plx_flags);
+                       plx_acc_32 = (u_int *)(plx_last_hc->plx_membase
+                                       + PLX_GPIOC);
+                       pv = readl(plx_acc_32);
+                       pv &= ~PLX_TERM_ON;
+                       writel(pv, plx_acc_32);
+                       spin_unlock_irqrestore(&plx_lock, plx_flags);
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                           printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n",
+                                       __func__, pv);
+               }
+               spin_unlock_irqrestore(&HFClock, hfc_flags);
+               hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */
+       }
+
+       /* we only want the real Z2 read-pointer for revision > 0 */
+       if (!test_bit(HFC_CHIP_REVISION0, &hc->chip))
+               hc->hw.r_ram_sz |= V_FZ_MD;
+
+       /* select pcm mode */
+       if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: setting PCM into slave mode\n",
+                           __func__);
+       } else
+       if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: setting PCM into master mode\n",
+                           __func__);
+               hc->hw.r_pcm_md0 |= V_PCM_MD;
+       } else {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: performing PCM auto detect\n",
+                           __func__);
+       }
+
+       /* soft reset */
+       HFC_outb(hc, R_CTRL, hc->hw.r_ctrl);
+       HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+       HFC_outb(hc, R_FIFO_MD, 0);
+       hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(100);
+       hc->hw.r_cirm = 0;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(100);
+       HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+
+       /* Speech Design PLX bridge pcm and sync mode */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               pv = readl(plx_acc_32);
+               /* Connect PCM */
+               if (hc->hw.r_pcm_md0 & V_PCM_MD) {
+                       pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+                       pv |= PLX_SYNC_O_EN;
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n",
+                                       __func__, pv);
+               } else {
+                       pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N);
+                       pv &= ~PLX_SYNC_O_EN;
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n",
+                                       __func__, pv);
+               }
+               writel(pv, plx_acc_32);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       /* PCM setup */
+       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90);
+       if (hc->slots == 32)
+               HFC_outb(hc, R_PCM_MD1, 0x00);
+       if (hc->slots == 64)
+               HFC_outb(hc, R_PCM_MD1, 0x10);
+       if (hc->slots == 128)
+               HFC_outb(hc, R_PCM_MD1, 0x20);
+       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0);
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+               HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */
+       else
+               HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */
+       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+       for (i = 0; i < 256; i++) {
+               HFC_outb_nodebug(hc, R_SLOT, i);
+               HFC_outb_nodebug(hc, A_SL_CFG, 0);
+               HFC_outb_nodebug(hc, A_CONF, 0);
+               hc->slot_owner[i] = -1;
+       }
+
+       /* set clock speed */
+       if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: setting double clock\n", __func__);
+               HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+       }
+
+       /* B410P GPIO */
+       if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+               printk(KERN_NOTICE "Setting GPIOs\n");
+               HFC_outb(hc, R_GPIO_SEL, 0x30);
+               HFC_outb(hc, R_GPIO_EN1, 0x3);
+               udelay(1000);
+               printk(KERN_NOTICE "calling vpm_init\n");
+               vpm_init(hc);
+       }
+
+       /* check if R_F0_CNT counts (8 kHz frame count) */
+       val = HFC_inb(hc, R_F0_CNTL);
+       val += HFC_inb(hc, R_F0_CNTH) << 8;
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG
+                   "HFC_multi F0_CNT %ld after reset\n", val);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */
+       spin_lock_irqsave(&hc->lock, flags);
+       val2 = HFC_inb(hc, R_F0_CNTL);
+       val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG
+                       "HFC_multi F0_CNT %ld after 10 ms (1st try)\n",
+                   val2);
+       if (val2 >= val+8) { /* 1 ms */
+               /* it counts, so we keep the pcm mode */
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+                       printk(KERN_INFO "controller is PCM bus MASTER\n");
+               else
+               if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip))
+                       printk(KERN_INFO "controller is PCM bus SLAVE\n");
+               else {
+                       test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+                       printk(KERN_INFO "controller is PCM bus SLAVE "
+                               "(auto detected)\n");
+               }
+       } else {
+               /* does not count */
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+controller_fail:
+                       printk(KERN_ERR "HFC_multi ERROR, getting no 125us "
+                           "pulse. Seems that controller fails.\n");
+                       err = -EIO;
+                       goto out;
+               }
+               if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+                       printk(KERN_INFO "controller is PCM bus SLAVE "
+                               "(ignoring missing PCM clock)\n");
+               } else {
+                       /* only one pcm master */
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+                               && plxsd_master) {
+                               printk(KERN_ERR "HFC_multi ERROR, no clock "
+                                   "on another Speech Design card found. "
+                                   "Please be sure to connect PCM cable.\n");
+                               err = -EIO;
+                               goto out;
+                       }
+                       /* retry with master clock */
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                               spin_lock_irqsave(&plx_lock, plx_flags);
+                               plx_acc_32 = (u_int *)(hc->plx_membase +
+                                       PLX_GPIOC);
+                               pv = readl(plx_acc_32);
+                               pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+                               pv |= PLX_SYNC_O_EN;
+                               writel(pv, plx_acc_32);
+                               spin_unlock_irqrestore(&plx_lock, plx_flags);
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                   printk(KERN_WARNING "%s: master: PLX_GPIO"
+                                       "=%x\n", __func__, pv);
+                       }
+                       hc->hw.r_pcm_md0 |= V_PCM_MD;
+                       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */
+                       spin_lock_irqsave(&hc->lock, flags);
+                       val2 = HFC_inb(hc, R_F0_CNTL);
+                       val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG "HFC_multi F0_CNT %ld after "
+                                       "10 ms (2nd try)\n", val2);
+                       if (val2 >= val+8) { /* 1 ms */
+                               test_and_set_bit(HFC_CHIP_PCM_MASTER,
+                                       &hc->chip);
+                               printk(KERN_INFO "controller is PCM bus MASTER "
+                                       "(auto detected)\n");
+                       } else
+                               goto controller_fail;
+               }
+       }
+
+       /* Release the DSP Reset */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+                       plxsd_master = 1;
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               pv = readl(plx_acc_32);
+               pv |=  PLX_DSP_RES_N;
+               writel(pv, plx_acc_32);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n",
+                               __func__, pv);
+       }
+
+       /* pcm id */
+       if (hc->pcm)
+               printk(KERN_INFO "controller has given PCM BUS ID %d\n",
+                       hc->pcm);
+       else {
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)
+                || test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       PCM_cnt++; /* SD has proprietary bridging */
+               }
+               hc->pcm = PCM_cnt;
+               printk(KERN_INFO "controller has PCM BUS ID %d "
+                       "(auto selected)\n", hc->pcm);
+       }
+
+       /* set up timer */
+       HFC_outb(hc, R_TI_WD, poll_timer);
+       hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
+
+       /*
+        * set up 125us interrupt, only if function pointer is available
+        * and module parameter timer is set
+        */
+       if (timer && hfc_interrupt && register_interrupt) {
+               /* only one chip should use this interrupt */
+               timer = 0;
+               interrupt_registered = 1;
+               hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK;
+               /* deactivate other interrupts in ztdummy */
+               register_interrupt();
+       }
+
+       /* set E1 state machine IRQ */
+       if (hc->type == 1)
+               hc->hw.r_irqmsk_misc |= V_STA_IRQMSK;
+
+       /* set DTMF detection */
+       if (test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: enabling DTMF detection "
+                           "for all B-channel\n", __func__);
+               hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP;
+               if (test_bit(HFC_CHIP_ULAW, &hc->chip))
+                       hc->hw.r_dtmf |= V_ULAW_SEL;
+               HFC_outb(hc, R_DTMF_N, 102 - 1);
+               hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK;
+       }
+
+       /* conference engine */
+       if (test_bit(HFC_CHIP_ULAW, &hc->chip))
+               r_conf_en = V_CONF_EN | V_ULAW;
+       else
+               r_conf_en = V_CONF_EN;
+       HFC_outb(hc, R_CONF_EN, r_conf_en);
+
+       /* setting leds */
+       switch (hc->leds) {
+       case 1: /* HFC-E1 OEM */
+               if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+                       HFC_outb(hc, R_GPIO_SEL, 0x32);
+               else
+                       HFC_outb(hc, R_GPIO_SEL, 0x30);
+
+               HFC_outb(hc, R_GPIO_EN1, 0x0f);
+               HFC_outb(hc, R_GPIO_OUT1, 0x00);
+
+               HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+               break;
+
+       case 2: /* HFC-4S OEM */
+       case 3:
+               HFC_outb(hc, R_GPIO_SEL, 0xf0);
+               HFC_outb(hc, R_GPIO_EN1, 0xff);
+               HFC_outb(hc, R_GPIO_OUT1, 0x00);
+               break;
+       }
+
+       /* set master clock */
+       if (hc->masterclk >= 0) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: setting ST master clock "
+                           "to port %d (0..%d)\n",
+                           __func__, hc->masterclk, hc->ports-1);
+               hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC;
+               HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync);
+       }
+
+       /* setting misc irq */
+       HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc);
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n",
+                   hc->hw.r_irqmsk_misc);
+
+       /* RAM access test */
+       HFC_outb(hc, R_RAM_ADDR0, 0);
+       HFC_outb(hc, R_RAM_ADDR1, 0);
+       HFC_outb(hc, R_RAM_ADDR2, 0);
+       for (i = 0; i < 256; i++) {
+               HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+               HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff));
+       }
+       for (i = 0; i < 256; i++) {
+               HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+               HFC_inb_nodebug(hc, R_RAM_DATA);
+               rval = HFC_inb_nodebug(hc, R_INT_DATA);
+               if (rval != ((i * 3) & 0xff)) {
+                       printk(KERN_DEBUG
+                           "addr:%x val:%x should:%x\n", i, rval,
+                           (i * 3) & 0xff);
+                       err++;
+               }
+       }
+       if (err) {
+               printk(KERN_DEBUG "aborting - %d RAM access errors\n", err);
+               err = -EIO;
+               goto out;
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done\n", __func__);
+out:
+       spin_unlock_irqrestore(&hc->lock, flags);
+       return err;
+}
+
+
+/*
+ * control the watchdog
+ */
+static void
+hfcmulti_watchdog(struct hfc_multi *hc)
+{
+       hc->wdcount++;
+
+       if (hc->wdcount > 10) {
+               hc->wdcount = 0;
+               hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ?
+                   V_GPIO_OUT3 : V_GPIO_OUT2;
+
+       /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */
+               HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+               HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte);
+       }
+}
+
+
+
+/*
+ * output leds
+ */
+static void
+hfcmulti_leds(struct hfc_multi *hc)
+{
+       unsigned long lled;
+       unsigned long leddw;
+       int i, state, active, leds;
+       struct dchannel *dch;
+       int led[4];
+
+       hc->ledcount += poll;
+       if (hc->ledcount > 4096) {
+               hc->ledcount -= 4096;
+               hc->ledstate = 0xAFFEAFFE;
+       }
+
+       switch (hc->leds) {
+       case 1: /* HFC-E1 OEM */
+               /* 2 red blinking: NT mode deactivate
+                * 2 red steady:   TE mode deactivate
+                * left green:     L1 active
+                * left red:       frame sync, but no L1
+                * right green:    L2 active
+                */
+               if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */
+                       if (hc->chan[hc->dslot].dch->dev.D.protocol
+                               != ISDN_P_NT_E1) {
+                               led[0] = 1;
+                               led[1] = 1;
+                       } else if (hc->ledcount>>11) {
+                               led[0] = 1;
+                               led[1] = 1;
+                       } else {
+                               led[0] = 0;
+                               led[1] = 0;
+                       }
+                       led[2] = 0;
+                       led[3] = 0;
+               } else { /* with frame sync */
+                       /* TODO make it work */
+                       led[0] = 0;
+                       led[1] = 0;
+                       led[2] = 0;
+                       led[3] = 1;
+               }
+               leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF;
+                       /* leds are inverted */
+               if (leds != (int)hc->ledstate) {
+                       HFC_outb_nodebug(hc, R_GPIO_OUT1, leds);
+                       hc->ledstate = leds;
+               }
+               break;
+
+       case 2: /* HFC-4S OEM */
+               /* red blinking = PH_DEACTIVATE NT Mode
+                * red steady   = PH_DEACTIVATE TE Mode
+                * green steady = PH_ACTIVATE
+                */
+               for (i = 0; i < 4; i++) {
+                       state = 0;
+                       active = -1;
+                       dch = hc->chan[(i << 2) | 2].dch;
+                       if (dch) {
+                               state = dch->state;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                       }
+                       if (state) {
+                               if (state == active) {
+                                       led[i] = 1; /* led green */
+                               } else
+                                       if (dch->dev.D.protocol == ISDN_P_TE_S0)
+                                               /* TE mode: led red */
+                                               led[i] = 2;
+                                       else
+                                               if (hc->ledcount>>11)
+                                                       /* led red */
+                                                       led[i] = 2;
+                                               else
+                                                       /* led off */
+                                                       led[i] = 0;
+                       } else
+                               led[i] = 0; /* led off */
+               }
+               if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+                       leds = 0;
+                       for (i = 0; i < 4; i++) {
+                               if (led[i] == 1) {
+                                       /*green*/
+                                       leds |= (0x2 << (i * 2));
+                               } else if (led[i] == 2) {
+                                       /*red*/
+                                       leds |= (0x1 << (i * 2));
+                               }
+                       }
+                       if (leds != (int)hc->ledstate) {
+                               vpm_out(hc, 0, 0x1a8 + 3, leds);
+                               hc->ledstate = leds;
+                       }
+               } else {
+                       leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) |
+                           ((led[0] > 0) << 2) | ((led[2] > 0) << 3) |
+                           ((led[3] & 1) << 4) | ((led[1] & 1) << 5) |
+                           ((led[0] & 1) << 6) | ((led[2] & 1) << 7);
+                       if (leds != (int)hc->ledstate) {
+                               HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F);
+                               HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4);
+                               hc->ledstate = leds;
+                       }
+               }
+               break;
+
+       case 3: /* HFC 1S/2S Beronet */
+               /* red blinking = PH_DEACTIVATE NT Mode
+                * red steady   = PH_DEACTIVATE TE Mode
+                * green steady = PH_ACTIVATE
+                */
+               for (i = 0; i < 2; i++) {
+                       state = 0;
+                       active = -1;
+                       dch = hc->chan[(i << 2) | 2].dch;
+                       if (dch) {
+                               state = dch->state;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                       }
+                       if (state) {
+                               if (state == active) {
+                                       led[i] = 1; /* led green */
+                               } else
+                                       if (dch->dev.D.protocol == ISDN_P_TE_S0)
+                                               /* TE mode: led red */
+                                               led[i] = 2;
+                                       else
+                                               if (hc->ledcount >> 11)
+                                                       /* led red */
+                                                       led[i] = 2;
+                                               else
+                                                       /* led off */
+                                                       led[i] = 0;
+                       } else
+                               led[i] = 0; /* led off */
+               }
+
+
+               leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2)
+                       | ((led[1]&1)<<3);
+               if (leds != (int)hc->ledstate) {
+                       HFC_outb_nodebug(hc, R_GPIO_EN1,
+                           ((led[0] > 0) << 2) | ((led[1] > 0) << 3));
+                       HFC_outb_nodebug(hc, R_GPIO_OUT1,
+                           ((led[0] & 1) << 2) | ((led[1] & 1) << 3));
+                       hc->ledstate = leds;
+               }
+               break;
+       case 8: /* HFC 8S+ Beronet */
+               lled = 0;
+
+               for (i = 0; i < 8; i++) {
+                       state = 0;
+                       active = -1;
+                       dch = hc->chan[(i << 2) | 2].dch;
+                       if (dch) {
+                               state = dch->state;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                       }
+                       if (state) {
+                               if (state == active) {
+                                       lled |= 0 << i;
+                               } else
+                                       if (hc->ledcount >> 11)
+                                               lled |= 0 << i;
+                                       else
+                                               lled |= 1 << i;
+                       } else
+                               lled |= 1 << i;
+               }
+               leddw = lled << 24 | lled << 16 | lled << 8 | lled;
+               if (leddw != hc->ledstate) {
+                       /* HFC_outb(hc, R_BRG_PCM_CFG, 1);
+                       HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */
+                       /* was _io before */
+                       HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+                       outw(0x4000, hc->pci_iobase + 4);
+                       outl(leddw, hc->pci_iobase);
+                       HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+                       hc->ledstate = leddw;
+               }
+               break;
+       }
+}
+/*
+ * read dtmf coefficients
+ */
+
+static void
+hfcmulti_dtmf(struct hfc_multi *hc)
+{
+       s32             *coeff;
+       u_int           mantissa;
+       int             co, ch;
+       struct bchannel *bch = NULL;
+       u8              exponent;
+       int             dtmf = 0;
+       int             addr;
+       u16             w_float;
+       struct sk_buff  *skb;
+       struct mISDNhead *hh;
+
+       if (debug & DEBUG_HFCMULTI_DTMF)
+               printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__);
+       for (ch = 0; ch <= 31; ch++) {
+               /* only process enabled B-channels */
+               bch = hc->chan[ch].bch;
+               if (!bch)
+                       continue;
+               if (!hc->created[hc->chan[ch].port])
+                       continue;
+               if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                       continue;
+               if (debug & DEBUG_HFCMULTI_DTMF)
+                       printk(KERN_DEBUG "%s: dtmf channel %d:",
+                               __func__, ch);
+               coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]);
+               dtmf = 1;
+               for (co = 0; co < 8; co++) {
+                       /* read W(n-1) coefficient */
+                       addr = hc->DTMFbase + ((co<<7) | (ch<<2));
+                       HFC_outb_nodebug(hc, R_RAM_ADDR0, addr);
+                       HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8);
+                       HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16)
+                               | V_ADDR_INC);
+                       w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+                       w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+                       if (debug & DEBUG_HFCMULTI_DTMF)
+                               printk(" %04x", w_float);
+
+                       /* decode float (see chip doc) */
+                       mantissa = w_float & 0x0fff;
+                       if (w_float & 0x8000)
+                               mantissa |= 0xfffff000;
+                       exponent = (w_float>>12) & 0x7;
+                       if (exponent) {
+                               mantissa ^= 0x1000;
+                               mantissa <<= (exponent-1);
+                       }
+
+                       /* store coefficient */
+                       coeff[co<<1] = mantissa;
+
+                       /* read W(n) coefficient */
+                       w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+                       w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+                       if (debug & DEBUG_HFCMULTI_DTMF)
+                               printk(" %04x", w_float);
+
+                       /* decode float (see chip doc) */
+                       mantissa = w_float & 0x0fff;
+                       if (w_float & 0x8000)
+                               mantissa |= 0xfffff000;
+                       exponent = (w_float>>12) & 0x7;
+                       if (exponent) {
+                               mantissa ^= 0x1000;
+                               mantissa <<= (exponent-1);
+                       }
+
+                       /* store coefficient */
+                       coeff[(co<<1)|1] = mantissa;
+               }
+               if (debug & DEBUG_HFCMULTI_DTMF)
+                       printk("%s: DTMF ready %08x %08x %08x %08x "
+                           "%08x %08x %08x %08x\n", __func__,
+                           coeff[0], coeff[1], coeff[2], coeff[3],
+                           coeff[4], coeff[5], coeff[6], coeff[7]);
+               hc->chan[ch].coeff_count++;
+               if (hc->chan[ch].coeff_count == 8) {
+                       hc->chan[ch].coeff_count = 0;
+                       skb = mI_alloc_skb(512, GFP_ATOMIC);
+                       if (!skb) {
+                               printk(KERN_WARNING "%s: No memory for skb\n",
+                                   __func__);
+                               continue;
+                       }
+                       hh = mISDN_HEAD_P(skb);
+                       hh->prim = PH_CONTROL_IND;
+                       hh->id = DTMF_HFC_COEF;
+                       memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512);
+                       recv_Bchannel_skb(bch, skb);
+               }
+       }
+
+       /* restart DTMF processing */
+       hc->dtmf = dtmf;
+       if (dtmf)
+               HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
+}
+
+
+/*
+ * fill fifo as much as possible
+ */
+
+static void
+hfcmulti_tx(struct hfc_multi *hc, int ch)
+{
+       int i, ii, temp, len = 0;
+       int Zspace, z1, z2; /* must be int for calculation */
+       int Fspace, f1, f2;
+       u_char *d;
+       int *txpending, slot_tx;
+       struct  bchannel *bch;
+       struct  dchannel *dch;
+       struct  sk_buff **sp = NULL;
+       int *idxp;
+
+       bch = hc->chan[ch].bch;
+       dch = hc->chan[ch].dch;
+       if ((!dch) && (!bch))
+               return;
+
+       txpending = &hc->chan[ch].txpending;
+       slot_tx = hc->chan[ch].slot_tx;
+       if (dch) {
+               if (!test_bit(FLG_ACTIVE, &dch->Flags))
+                       return;
+               sp = &dch->tx_skb;
+               idxp = &dch->tx_idx;
+       } else {
+               if (!test_bit(FLG_ACTIVE, &bch->Flags))
+                       return;
+               sp = &bch->tx_skb;
+               idxp = &bch->tx_idx;
+       }
+       if (*sp)
+               len = (*sp)->len;
+
+       if ((!len) && *txpending != 1)
+               return; /* no data */
+
+       if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+           (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+           (hc->chan[ch].slot_rx < 0) &&
+           (hc->chan[ch].slot_tx < 0))
+               HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1));
+       else
+               HFC_outb_nodebug(hc, R_FIFO, ch << 1);
+       HFC_wait_nodebug(hc);
+
+       if (*txpending == 2) {
+               /* reset fifo */
+               HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait_nodebug(hc);
+               HFC_outb(hc, A_SUBCH_CFG, 0);
+               *txpending = 1;
+       }
+next_frame:
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               f1 = HFC_inb_nodebug(hc, A_F1);
+               f2 = HFC_inb_nodebug(hc, A_F2);
+               while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) {
+                       if (debug & DEBUG_HFCMULTI_FIFO)
+                               printk(KERN_DEBUG
+                                   "%s(card %d): reread f2 because %d!=%d\n",
+                                   __func__, hc->id + 1, temp, f2);
+                       f2 = temp; /* repeat until F2 is equal */
+               }
+               Fspace = f2 - f1 - 1;
+               if (Fspace < 0)
+                       Fspace += hc->Flen;
+               /*
+                * Old FIFO handling doesn't give us the current Z2 read
+                * pointer, so we cannot send the next frame before the fifo
+                * is empty. It makes no difference except for a slightly
+                * lower performance.
+                */
+               if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) {
+                       if (f1 != f2)
+                               Fspace = 0;
+                       else
+                               Fspace = 1;
+               }
+               /* one frame only for ST D-channels, to allow resending */
+               if (hc->type != 1 && dch) {
+                       if (f1 != f2)
+                               Fspace = 0;
+               }
+               /* F-counter full condition */
+               if (Fspace == 0)
+                       return;
+       }
+       z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+       z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+       while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) {
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG "%s(card %d): reread z2 because "
+                               "%d!=%d\n", __func__, hc->id + 1, temp, z2);
+               z2 = temp; /* repeat unti Z2 is equal */
+       }
+       Zspace = z2 - z1;
+       if (Zspace <= 0)
+               Zspace += hc->Zlen;
+       Zspace -= 4; /* keep not too full, so pointers will not overrun */
+       /* fill transparent data only to maxinum transparent load (minus 4) */
+       if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+               Zspace = Zspace - hc->Zlen + hc->max_trans;
+       if (Zspace <= 0) /* no space of 4 bytes */
+               return;
+
+       /* if no data */
+       if (!len) {
+               if (z1 == z2) { /* empty */
+                       /* if done with FIFO audio data during PCM connection */
+                       if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) &&
+                           *txpending && slot_tx >= 0) {
+                               if (debug & DEBUG_HFCMULTI_MODE)
+                                       printk(KERN_DEBUG
+                                           "%s: reconnecting PCM due to no "
+                                           "more FIFO data: channel %d "
+                                           "slot_tx %d\n",
+                                           __func__, ch, slot_tx);
+                               /* connect slot */
+                               HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+                                   V_HDLC_TRP | V_IFF);
+                               HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+                               HFC_wait_nodebug(hc);
+                               HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+                                   V_HDLC_TRP | V_IFF);
+                               HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+                               HFC_wait_nodebug(hc);
+                       }
+                       *txpending = 0;
+               }
+               return; /* no data */
+       }
+
+       /* if audio data and connected slot */
+       if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending)
+               && slot_tx >= 0) {
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: disconnecting PCM due to "
+                           "FIFO data: channel %d slot_tx %d\n",
+                           __func__, ch, slot_tx);
+               /* disconnect slot */
+               HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+               HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+               HFC_wait_nodebug(hc);
+               HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+               HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+               HFC_wait_nodebug(hc);
+       }
+       *txpending = 1;
+
+       /* show activity */
+       hc->activity[hc->chan[ch].port] = 1;
+
+       /* fill fifo to what we have left */
+       ii = len;
+       if (dch || test_bit(FLG_HDLC, &bch->Flags))
+               temp = 1;
+       else
+               temp = 0;
+       i = *idxp;
+       d = (*sp)->data + i;
+       if (ii - i > Zspace)
+               ii = Zspace + i;
+       if (debug & DEBUG_HFCMULTI_FIFO)
+               printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space "
+                   "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
+                       __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i,
+                       temp ? "HDLC":"TRANS");
+
+
+       /* Have to prep the audio data */
+       hc->write_fifo(hc, d, ii - i);
+       *idxp = ii;
+
+       /* if not all data has been written */
+       if (ii != len) {
+               /* NOTE: fifo is started by the calling function */
+               return;
+       }
+
+       /* if all data has been written, terminate frame */
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               /* increment f-counter */
+               HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+               HFC_wait_nodebug(hc);
+       }
+
+       /* send confirm, since get_net_bframe will not do it with trans */
+       if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+               confirm_Bsend(bch);
+
+       /* check for next frame */
+       dev_kfree_skb(*sp);
+       if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */
+               len = (*sp)->len;
+               goto next_frame;
+       }
+       if (dch && get_next_dframe(dch)) {
+               len = (*sp)->len;
+               goto next_frame;
+       }
+
+       /*
+        * now we have no more data, so in case of transparent,
+        * we set the last byte in fifo to 'silence' in case we will get
+        * no more data at all. this prevents sending an undefined value.
+        */
+       if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+               HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+}
+
+
+/* NOTE: only called if E1 card is in active state */
+static void
+hfcmulti_rx(struct hfc_multi *hc, int ch)
+{
+       int temp;
+       int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */
+       int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
+       int again = 0;
+       struct  bchannel *bch;
+       struct  dchannel *dch;
+       struct sk_buff  *skb, **sp = NULL;
+       int     maxlen;
+
+       bch = hc->chan[ch].bch;
+       dch = hc->chan[ch].dch;
+       if ((!dch) && (!bch))
+               return;
+       if (dch) {
+               if (!test_bit(FLG_ACTIVE, &dch->Flags))
+                       return;
+               sp = &dch->rx_skb;
+               maxlen = dch->maxlen;
+       } else {
+               if (!test_bit(FLG_ACTIVE, &bch->Flags))
+                       return;
+               sp = &bch->rx_skb;
+               maxlen = bch->maxlen;
+       }
+next_frame:
+       /* on first AND before getting next valid frame, R_FIFO must be written
+          to. */
+       if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+           (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+           (hc->chan[ch].slot_rx < 0) &&
+           (hc->chan[ch].slot_tx < 0))
+               HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1);
+       else
+               HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1);
+       HFC_wait_nodebug(hc);
+
+       /* ignore if rx is off BUT change fifo (above) to start pending TX */
+       if (hc->chan[ch].rx_off)
+               return;
+
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               f1 = HFC_inb_nodebug(hc, A_F1);
+               while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) {
+                       if (debug & DEBUG_HFCMULTI_FIFO)
+                               printk(KERN_DEBUG
+                                   "%s(card %d): reread f1 because %d!=%d\n",
+                                   __func__, hc->id + 1, temp, f1);
+                       f1 = temp; /* repeat until F1 is equal */
+               }
+               f2 = HFC_inb_nodebug(hc, A_F2);
+       }
+       z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+       while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) {
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG "%s(card %d): reread z2 because "
+                               "%d!=%d\n", __func__, hc->id + 1, temp, z2);
+               z1 = temp; /* repeat until Z1 is equal */
+       }
+       z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+       Zsize = z1 - z2;
+       if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2)
+               /* complete hdlc frame */
+               Zsize++;
+       if (Zsize < 0)
+               Zsize += hc->Zlen;
+       /* if buffer is empty */
+       if (Zsize <= 0)
+               return;
+
+       if (*sp == NULL) {
+               *sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC);
+               if (*sp == NULL) {
+                       printk(KERN_DEBUG "%s: No mem for rx_skb\n",
+                           __func__);
+                       return;
+               }
+       }
+       /* show activity */
+       hc->activity[hc->chan[ch].port] = 1;
+
+       /* empty fifo with what we have */
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d "
+                           "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) "
+                           "got=%d (again %d)\n", __func__, hc->id + 1, ch,
+                           Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE",
+                           f1, f2, Zsize + (*sp)->len, again);
+               /* HDLC */
+               if ((Zsize + (*sp)->len) > (maxlen + 3)) {
+                       if (debug & DEBUG_HFCMULTI_FIFO)
+                               printk(KERN_DEBUG
+                                   "%s(card %d): hdlc-frame too large.\n",
+                                   __func__, hc->id + 1);
+                       skb_trim(*sp, 0);
+                       HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait_nodebug(hc);
+                       return;
+               }
+
+               hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+
+               if (f1 != f2) {
+                       /* increment Z2,F2-counter */
+                       HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+                       HFC_wait_nodebug(hc);
+                       /* check size */
+                       if ((*sp)->len < 4) {
+                               if (debug & DEBUG_HFCMULTI_FIFO)
+                                       printk(KERN_DEBUG
+                                           "%s(card %d): Frame below minimum "
+                                           "size\n", __func__, hc->id + 1);
+                               skb_trim(*sp, 0);
+                               goto next_frame;
+                       }
+                       /* there is at least one complete frame, check crc */
+                       if ((*sp)->data[(*sp)->len - 1]) {
+                               if (debug & DEBUG_HFCMULTI_CRC)
+                                       printk(KERN_DEBUG
+                                           "%s: CRC-error\n", __func__);
+                               skb_trim(*sp, 0);
+                               goto next_frame;
+                       }
+                       skb_trim(*sp, (*sp)->len - 3);
+                       if ((*sp)->len < MISDN_COPY_SIZE) {
+                               skb = *sp;
+                               *sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+                               if (*sp) {
+                                       memcpy(skb_put(*sp, skb->len),
+                                           skb->data, skb->len);
+                                       skb_trim(skb, 0);
+                               } else {
+                                       printk(KERN_DEBUG "%s: No mem\n",
+                                           __func__);
+                                       *sp = skb;
+                                       skb = NULL;
+                               }
+                       } else {
+                               skb = NULL;
+                       }
+                       if (debug & DEBUG_HFCMULTI_FIFO) {
+                               printk(KERN_DEBUG "%s(card %d):",
+                                       __func__, hc->id + 1);
+                               temp = 0;
+                               while (temp < (*sp)->len)
+                                       printk(" %02x", (*sp)->data[temp++]);
+                               printk("\n");
+                       }
+                       if (dch)
+                               recv_Dchannel(dch);
+                       else
+                               recv_Bchannel(bch);
+                       *sp = skb;
+                       again++;
+                       goto next_frame;
+               }
+               /* there is an incomplete frame */
+       } else {
+               /* transparent */
+               if (Zsize > skb_tailroom(*sp))
+                       Zsize = skb_tailroom(*sp);
+               hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+               if (((*sp)->len) < MISDN_COPY_SIZE) {
+                       skb = *sp;
+                       *sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+                       if (*sp) {
+                               memcpy(skb_put(*sp, skb->len),
+                                   skb->data, skb->len);
+                               skb_trim(skb, 0);
+                       } else {
+                               printk(KERN_DEBUG "%s: No mem\n", __func__);
+                               *sp = skb;
+                               skb = NULL;
+                       }
+               } else {
+                       skb = NULL;
+               }
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG
+                           "%s(card %d): fifo(%d) reading %d bytes "
+                           "(z1=%04x, z2=%04x) TRANS\n",
+                               __func__, hc->id + 1, ch, Zsize, z1, z2);
+               /* only bch is transparent */
+               recv_Bchannel(bch);
+               *sp = skb;
+       }
+}
+
+
+/*
+ * Interrupt handler
+ */
+static void
+signal_state_up(struct dchannel *dch, int info, char *msg)
+{
+       struct sk_buff  *skb;
+       int             id, data = info;
+
+       if (debug & DEBUG_HFCMULTI_STATE)
+               printk(KERN_DEBUG "%s: %s\n", __func__, msg);
+
+       id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */
+
+       skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data,
+               GFP_ATOMIC);
+       if (!skb)
+               return;
+       recv_Dchannel_skb(dch, skb);
+}
+
+static inline void
+handle_timer_irq(struct hfc_multi *hc)
+{
+       int             ch, temp;
+       struct dchannel *dch;
+       u_long          flags;
+
+       /* process queued resync jobs */
+       if (hc->e1_resync) {
+               /* lock, so e1_resync gets not changed */
+               spin_lock_irqsave(&HFClock, flags);
+               if (hc->e1_resync & 1) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "Enable SYNC_I\n");
+                       HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC);
+                       /* disable JATT, if RX_SYNC is set */
+                       if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+                               HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+               }
+               if (hc->e1_resync & 2) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "Enable jatt PLL\n");
+                       HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+               }
+               if (hc->e1_resync & 4) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG
+                                   "Enable QUARTZ for HFC-E1\n");
+                       /* set jatt to quartz */
+                       HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC
+                               | V_JATT_OFF);
+                       /* switch to JATT, in case it is not already */
+                       HFC_outb(hc, R_SYNC_OUT, 0);
+               }
+               hc->e1_resync = 0;
+               spin_unlock_irqrestore(&HFClock, flags);
+       }
+
+       if (hc->type != 1 || hc->e1_state == 1)
+               for (ch = 0; ch <= 31; ch++) {
+                       if (hc->created[hc->chan[ch].port]) {
+                               hfcmulti_tx(hc, ch);
+                               /* fifo is started when switching to rx-fifo */
+                               hfcmulti_rx(hc, ch);
+                               if (hc->chan[ch].dch &&
+                                   hc->chan[ch].nt_timer > -1) {
+                                       dch = hc->chan[ch].dch;
+                                       if (!(--hc->chan[ch].nt_timer)) {
+                                               schedule_event(dch,
+                                                   FLG_PHCHANGE);
+                                               if (debug &
+                                                   DEBUG_HFCMULTI_STATE)
+                                                       printk(KERN_DEBUG
+                                                           "%s: nt_timer at "
+                                                           "state %x\n",
+                                                           __func__,
+                                                           dch->state);
+                                       }
+                               }
+                       }
+               }
+       if (hc->type == 1 && hc->created[0]) {
+               dch = hc->chan[hc->dslot].dch;
+               if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+                       /* LOS */
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS;
+                       if (!temp && hc->chan[hc->dslot].los)
+                               signal_state_up(dch, L1_SIGNAL_LOS_ON,
+                                   "LOS detected");
+                       if (temp && !hc->chan[hc->dslot].los)
+                               signal_state_up(dch, L1_SIGNAL_LOS_OFF,
+                                   "LOS gone");
+                       hc->chan[hc->dslot].los = temp;
+               }
+               if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) {
+                       /* AIS */
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS;
+                       if (!temp && hc->chan[hc->dslot].ais)
+                               signal_state_up(dch, L1_SIGNAL_AIS_ON,
+                                   "AIS detected");
+                       if (temp && !hc->chan[hc->dslot].ais)
+                               signal_state_up(dch, L1_SIGNAL_AIS_OFF,
+                                   "AIS gone");
+                       hc->chan[hc->dslot].ais = temp;
+               }
+               if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) {
+                       /* SLIP */
+                       temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX;
+                       if (!temp && hc->chan[hc->dslot].slip_rx)
+                               signal_state_up(dch, L1_SIGNAL_SLIP_RX,
+                                   " bit SLIP detected RX");
+                       hc->chan[hc->dslot].slip_rx = temp;
+                       temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX;
+                       if (!temp && hc->chan[hc->dslot].slip_tx)
+                               signal_state_up(dch, L1_SIGNAL_SLIP_TX,
+                                   " bit SLIP detected TX");
+                       hc->chan[hc->dslot].slip_tx = temp;
+               }
+               if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) {
+                       /* RDI */
+                       temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A;
+                       if (!temp && hc->chan[hc->dslot].rdi)
+                               signal_state_up(dch, L1_SIGNAL_RDI_ON,
+                                   "RDI detected");
+                       if (temp && !hc->chan[hc->dslot].rdi)
+                               signal_state_up(dch, L1_SIGNAL_RDI_OFF,
+                                   "RDI gone");
+                       hc->chan[hc->dslot].rdi = temp;
+               }
+               temp = HFC_inb_nodebug(hc, R_JATT_DIR);
+               switch (hc->chan[hc->dslot].sync) {
+               case 0:
+                       if ((temp & 0x60) == 0x60) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 now "
+                                           "in clock sync\n",
+                                           __func__, hc->id);
+                               HFC_outb(hc, R_RX_OFF,
+                                   hc->chan[hc->dslot].jitter | V_RX_INIT);
+                               HFC_outb(hc, R_TX_OFF,
+                                   hc->chan[hc->dslot].jitter | V_RX_INIT);
+                               hc->chan[hc->dslot].sync = 1;
+                               goto check_framesync;
+                       }
+                       break;
+               case 1:
+                       if ((temp & 0x60) != 0x60) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 "
+                                           "lost clock sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 0;
+                               break;
+                       }
+check_framesync:
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+                       if (temp == 0x27) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 "
+                                           "now in frame sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 2;
+                       }
+                       break;
+               case 2:
+                       if ((temp & 0x60) != 0x60) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 lost "
+                                           "clock & frame sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 0;
+                               break;
+                       }
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+                       if (temp != 0x27) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 "
+                                           "lost frame sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 1;
+                       }
+                       break;
+               }
+       }
+
+       if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+               hfcmulti_watchdog(hc);
+
+       if (hc->leds)
+               hfcmulti_leds(hc);
+}
+
+static void
+ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech)
+{
+       struct dchannel *dch;
+       int             ch;
+       int             active;
+       u_char          st_status, temp;
+
+       /* state machine */
+       for (ch = 0; ch <= 31; ch++) {
+               if (hc->chan[ch].dch) {
+                       dch = hc->chan[ch].dch;
+                       if (r_irq_statech & 1) {
+                               HFC_outb_nodebug(hc, R_ST_SEL,
+                                       hc->chan[ch].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               /* undocumented: status changes during read */
+                               st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE);
+                               while (st_status != (temp =
+                                       HFC_inb_nodebug(hc, A_ST_RD_STATE))) {
+                                       if (debug & DEBUG_HFCMULTI_STATE)
+                                               printk(KERN_DEBUG "%s: reread "
+                                                   "STATE because %d!=%d\n",
+                                                   __func__, temp,
+                                                   st_status);
+                                       st_status = temp; /* repeat */
+                               }
+
+                               /* Speech Design TE-sync indication */
+                               if (test_bit(HFC_CHIP_PLXSD, &hc->chip) &&
+                                       dch->dev.D.protocol == ISDN_P_TE_S0) {
+                                       if (st_status & V_FR_SYNC_ST)
+                                               hc->syncronized |=
+                                                   (1 << hc->chan[ch].port);
+                                       else
+                                               hc->syncronized &=
+                                                  ~(1 << hc->chan[ch].port);
+                               }
+                               dch->state = st_status & 0x0f;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                               if (dch->state == active) {
+                                       HFC_outb_nodebug(hc, R_FIFO,
+                                               (ch << 1) | 1);
+                                       HFC_wait_nodebug(hc);
+                                       HFC_outb_nodebug(hc,
+                                               R_INC_RES_FIFO, V_RES_F);
+                                       HFC_wait_nodebug(hc);
+                                       dch->tx_idx = 0;
+                               }
+                               schedule_event(dch, FLG_PHCHANGE);
+                               if (debug & DEBUG_HFCMULTI_STATE)
+                                       printk(KERN_DEBUG
+                                           "%s: S/T newstate %x port %d\n",
+                                           __func__, dch->state,
+                                           hc->chan[ch].port);
+                       }
+                       r_irq_statech >>= 1;
+               }
+       }
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+               plxsd_checksync(hc, 0);
+}
+
+static void
+fifo_irq(struct hfc_multi *hc, int block)
+{
+       int     ch, j;
+       struct dchannel *dch;
+       struct bchannel *bch;
+       u_char r_irq_fifo_bl;
+
+       r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block);
+       j = 0;
+       while (j < 8) {
+               ch = (block << 2) + (j >> 1);
+               dch = hc->chan[ch].dch;
+               bch = hc->chan[ch].bch;
+               if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) {
+                       j += 2;
+                       continue;
+               }
+               if (dch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &dch->Flags)) {
+                       hfcmulti_tx(hc, ch);
+                       /* start fifo */
+                       HFC_outb_nodebug(hc, R_FIFO, 0);
+                       HFC_wait_nodebug(hc);
+               }
+               if (bch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       hfcmulti_tx(hc, ch);
+                       /* start fifo */
+                       HFC_outb_nodebug(hc, R_FIFO, 0);
+                       HFC_wait_nodebug(hc);
+               }
+               j++;
+               if (dch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &dch->Flags)) {
+                       hfcmulti_rx(hc, ch);
+               }
+               if (bch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       hfcmulti_rx(hc, ch);
+               }
+               j++;
+       }
+}
+
+#ifdef IRQ_DEBUG
+int irqsem;
+#endif
+static irqreturn_t
+hfcmulti_interrupt(int intno, void *dev_id)
+{
+#ifdef IRQCOUNT_DEBUG
+       static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0,
+           iq5 = 0, iq6 = 0, iqcnt = 0;
+#endif
+       static int              count;
+       struct hfc_multi        *hc = dev_id;
+       struct dchannel         *dch;
+       u_char                  r_irq_statech, status, r_irq_misc, r_irq_oview;
+       int                     i;
+       u_short                 *plx_acc, wval;
+       u_char                  e1_syncsta, temp;
+       u_long                  flags;
+
+       if (!hc) {
+               printk(KERN_ERR "HFC-multi: Spurious interrupt!\n");
+               return IRQ_NONE;
+       }
+
+       spin_lock(&hc->lock);
+
+#ifdef IRQ_DEBUG
+       if (irqsem)
+               printk(KERN_ERR "irq for card %d during irq from "
+               "card %d, this is no bug.\n", hc->id + 1, irqsem);
+       irqsem = hc->id + 1;
+#endif
+
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, flags);
+               plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR);
+               wval = readw(plx_acc);
+               spin_unlock_irqrestore(&plx_lock, flags);
+               if (!(wval & PLX_INTCSR_LINTI1_STATUS))
+                       goto irq_notforus;
+       }
+
+       status = HFC_inb_nodebug(hc, R_STATUS);
+       r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH);
+#ifdef IRQCOUNT_DEBUG
+       if (r_irq_statech)
+               iq1++;
+       if (status & V_DTMF_STA)
+               iq2++;
+       if (status & V_LOST_STA)
+               iq3++;
+       if (status & V_EXT_IRQSTA)
+               iq4++;
+       if (status & V_MISC_IRQSTA)
+               iq5++;
+       if (status & V_FR_IRQSTA)
+               iq6++;
+       if (iqcnt++ > 5000) {
+               printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n",
+                   iq1, iq2, iq3, iq4, iq5, iq6);
+               iqcnt = 0;
+       }
+#endif
+       if (!r_irq_statech &&
+           !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA |
+           V_MISC_IRQSTA | V_FR_IRQSTA))) {
+               /* irq is not for us */
+               goto irq_notforus;
+       }
+       hc->irqcnt++;
+       if (r_irq_statech) {
+               if (hc->type != 1)
+                       ph_state_irq(hc, r_irq_statech);
+       }
+       if (status & V_EXT_IRQSTA)
+               ; /* external IRQ */
+       if (status & V_LOST_STA) {
+               /* LOST IRQ */
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */
+       }
+       if (status & V_MISC_IRQSTA) {
+               /* misc IRQ */
+               r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC);
+               if (r_irq_misc & V_STA_IRQ) {
+                       if (hc->type == 1) {
+                               /* state machine */
+                               dch = hc->chan[hc->dslot].dch;
+                               e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA);
+                               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+                                && hc->e1_getclock) {
+                                       if (e1_syncsta & V_FR_SYNC_E1)
+                                               hc->syncronized = 1;
+                                       else
+                                               hc->syncronized = 0;
+                               }
+                               /* undocumented: status changes during read */
+                               dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA);
+                               while (dch->state != (temp =
+                                       HFC_inb_nodebug(hc, R_E1_RD_STA))) {
+                                       if (debug & DEBUG_HFCMULTI_STATE)
+                                               printk(KERN_DEBUG "%s: reread "
+                                                   "STATE because %d!=%d\n",
+                                                   __func__, temp,
+                                                   dch->state);
+                                       dch->state = temp; /* repeat */
+                               }
+                               dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA)
+                                       & 0x7;
+                               schedule_event(dch, FLG_PHCHANGE);
+                               if (debug & DEBUG_HFCMULTI_STATE)
+                                       printk(KERN_DEBUG
+                                           "%s: E1 (id=%d) newstate %x\n",
+                                           __func__, hc->id, dch->state);
+                               if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+                                       plxsd_checksync(hc, 0);
+                       }
+               }
+               if (r_irq_misc & V_TI_IRQ)
+                       handle_timer_irq(hc);
+
+               if (r_irq_misc & V_DTMF_IRQ) {
+                       /* -> DTMF IRQ */
+                       hfcmulti_dtmf(hc);
+               }
+               /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable  */
+               if (r_irq_misc & V_IRQ_PROC) {
+                       /* IRQ every 125us */
+                       count++;
+                       /* generate 1kHz signal */
+                       if (count == 8) {
+                               if (hfc_interrupt)
+                                       hfc_interrupt();
+                               count = 0;
+                       }
+               }
+
+       }
+       if (status & V_FR_IRQSTA) {
+               /* FIFO IRQ */
+               r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW);
+               for (i = 0; i < 8; i++) {
+                       if (r_irq_oview & (1 << i))
+                               fifo_irq(hc, i);
+               }
+       }
+
+#ifdef IRQ_DEBUG
+       irqsem = 0;
+#endif
+       spin_unlock(&hc->lock);
+       return IRQ_HANDLED;
+
+irq_notforus:
+#ifdef IRQ_DEBUG
+       irqsem = 0;
+#endif
+       spin_unlock(&hc->lock);
+       return IRQ_NONE;
+}
+
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+
+static void
+hfcmulti_dbusy_timer(struct hfc_multi *hc)
+{
+}
+
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ *
+ * configure B-channel with the given protocol
+ * ch eqals to the HFC-channel (0-31)
+ * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31
+ * for S/T, 1-31 for E1)
+ * the hdlc interrupts will be set/unset
+ */
+static int
+mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx,
+    int bank_tx, int slot_rx, int bank_rx)
+{
+       int flow_tx = 0, flow_rx = 0, routing = 0;
+       int oslot_tx, oslot_rx;
+       int conf;
+
+       if (ch < 0 || ch > 31)
+               return EINVAL;
+       oslot_tx = hc->chan[ch].slot_tx;
+       oslot_rx = hc->chan[ch].slot_rx;
+       conf = hc->chan[ch].conf;
+
+       if (debug & DEBUG_HFCMULTI_MODE)
+               printk(KERN_DEBUG
+                   "%s: card %d channel %d protocol %x slot old=%d new=%d "
+                   "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n",
+                   __func__, hc->id, ch, protocol, oslot_tx, slot_tx,
+                   bank_tx, oslot_rx, slot_rx, bank_rx);
+
+       if (oslot_tx >= 0 && slot_tx != oslot_tx) {
+               /* remove from slot */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: remove from slot %d (TX)\n",
+                           __func__, oslot_tx);
+               if (hc->slot_owner[oslot_tx<<1] == ch) {
+                       HFC_outb(hc, R_SLOT, oslot_tx << 1);
+                       HFC_outb(hc, A_SL_CFG, 0);
+                       HFC_outb(hc, A_CONF, 0);
+                       hc->slot_owner[oslot_tx<<1] = -1;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_MODE)
+                               printk(KERN_DEBUG
+                                   "%s: we are not owner of this tx slot "
+                                   "anymore, channel %d is.\n",
+                                   __func__, hc->slot_owner[oslot_tx<<1]);
+               }
+       }
+
+       if (oslot_rx >= 0 && slot_rx != oslot_rx) {
+               /* remove from slot */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG
+                           "%s: remove from slot %d (RX)\n",
+                           __func__, oslot_rx);
+               if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) {
+                       HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR);
+                       HFC_outb(hc, A_SL_CFG, 0);
+                       hc->slot_owner[(oslot_rx << 1) | 1] = -1;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_MODE)
+                               printk(KERN_DEBUG
+                                   "%s: we are not owner of this rx slot "
+                                   "anymore, channel %d is.\n",
+                                   __func__,
+                                   hc->slot_owner[(oslot_rx << 1) | 1]);
+               }
+       }
+
+       if (slot_tx < 0) {
+               flow_tx = 0x80; /* FIFO->ST */
+               /* disable pcm slot */
+               hc->chan[ch].slot_tx = -1;
+               hc->chan[ch].bank_tx = 0;
+       } else {
+               /* set pcm slot */
+               if (hc->chan[ch].txpending)
+                       flow_tx = 0x80; /* FIFO->ST */
+               else
+                       flow_tx = 0xc0; /* PCM->ST */
+               /* put on slot */
+               routing = bank_tx ? 0xc0 : 0x80;
+               if (conf >= 0 || bank_tx > 1)
+                       routing = 0x40; /* loop */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: put channel %d to slot %d bank"
+                           " %d flow %02x routing %02x conf %d (TX)\n",
+                           __func__, ch, slot_tx, bank_tx,
+                           flow_tx, routing, conf);
+               HFC_outb(hc, R_SLOT, slot_tx << 1);
+               HFC_outb(hc, A_SL_CFG, (ch<<1) | routing);
+               HFC_outb(hc, A_CONF, (conf < 0) ? 0 : (conf | V_CONF_SL));
+               hc->slot_owner[slot_tx << 1] = ch;
+               hc->chan[ch].slot_tx = slot_tx;
+               hc->chan[ch].bank_tx = bank_tx;
+       }
+       if (slot_rx < 0) {
+               /* disable pcm slot */
+               flow_rx = 0x80; /* ST->FIFO */
+               hc->chan[ch].slot_rx = -1;
+               hc->chan[ch].bank_rx = 0;
+       } else {
+               /* set pcm slot */
+               if (hc->chan[ch].txpending)
+                       flow_rx = 0x80; /* ST->FIFO */
+               else
+                       flow_rx = 0xc0; /* ST->(FIFO,PCM) */
+               /* put on slot */
+               routing = bank_rx?0x80:0xc0; /* reversed */
+               if (conf >= 0 || bank_rx > 1)
+                       routing = 0x40; /* loop */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: put channel %d to slot %d bank"
+                           " %d flow %02x routing %02x conf %d (RX)\n",
+                           __func__, ch, slot_rx, bank_rx,
+                           flow_rx, routing, conf);
+               HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR);
+               HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing);
+               hc->slot_owner[(slot_rx<<1)|1] = ch;
+               hc->chan[ch].slot_rx = slot_rx;
+               hc->chan[ch].bank_rx = bank_rx;
+       }
+
+       switch (protocol) {
+       case (ISDN_P_NONE):
+               /* disable TX fifo */
+               HFC_outb(hc, R_FIFO, ch << 1);
+               HFC_wait(hc);
+               HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF);
+               HFC_outb(hc, A_SUBCH_CFG, 0);
+               HFC_outb(hc, A_IRQ_MSK, 0);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               /* disable RX fifo */
+               HFC_outb(hc, R_FIFO, (ch<<1)|1);
+               HFC_wait(hc);
+               HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00);
+               HFC_outb(hc, A_SUBCH_CFG, 0);
+               HFC_outb(hc, A_IRQ_MSK, 0);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               if (hc->chan[ch].bch && hc->type != 1) {
+                       hc->hw.a_st_ctrl0[hc->chan[ch].port] &=
+                           ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN;
+                       HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_CTRL0,
+                           hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+               }
+               if (hc->chan[ch].bch) {
+                       test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT,
+                           &hc->chan[ch].bch->Flags);
+               }
+               break;
+       case (ISDN_P_B_RAW): /* B-channel */
+
+               if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+                   (hc->chan[ch].slot_rx < 0) &&
+                   (hc->chan[ch].slot_tx < 0)) {
+
+                       printk(KERN_DEBUG
+                           "Setting B-channel %d to echo cancelable "
+                           "state on PCM slot %d\n", ch,
+                           ((ch / 4) * 8) + ((ch % 4) * 4) + 1);
+                       printk(KERN_DEBUG
+                           "Enabling pass through for channel\n");
+                       vpm_out(hc, ch, ((ch / 4) * 8) +
+                           ((ch % 4) * 4) + 1, 0x01);
+                       /* rx path */
+                       /* S/T -> PCM */
+                       HFC_outb(hc, R_FIFO, (ch << 1));
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+                           ((ch % 4) * 4) + 1) << 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1));
+
+                       /* PCM -> FIFO */
+                       HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+                       HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+                           ((ch % 4) * 4) + 1) << 1) | 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1);
+
+                       /* tx path */
+                       /* PCM -> S/T */
+                       HFC_outb(hc, R_FIFO, (ch << 1) | 1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+                           ((ch % 4) * 4)) << 1) | 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1);
+
+                       /* FIFO -> PCM */
+                       HFC_outb(hc, R_FIFO, 0x20 | (ch << 1));
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+                       /* tx silence */
+                       HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+                       HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+                           ((ch % 4) * 4)) << 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1));
+               } else {
+                       /* enable TX fifo */
+                       HFC_outb(hc, R_FIFO, ch << 1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 |
+                           V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+                       /* tx silence */
+                       HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+                       /* enable RX fifo */
+                       HFC_outb(hc, R_FIFO, (ch<<1)|1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+               }
+               if (hc->type != 1) {
+                       hc->hw.a_st_ctrl0[hc->chan[ch].port] |=
+                           ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN;
+                       HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_CTRL0,
+                           hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+               }
+               if (hc->chan[ch].bch)
+                       test_and_set_bit(FLG_TRANSPARENT,
+                           &hc->chan[ch].bch->Flags);
+               break;
+       case (ISDN_P_B_HDLC): /* B-channel */
+       case (ISDN_P_TE_S0): /* D-channel */
+       case (ISDN_P_NT_S0):
+       case (ISDN_P_TE_E1):
+       case (ISDN_P_NT_E1):
+               /* enable TX fifo */
+               HFC_outb(hc, R_FIFO, ch<<1);
+               HFC_wait(hc);
+               if (hc->type == 1 || hc->chan[ch].bch) {
+                       /* E1 or B-channel */
+                       HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+               } else {
+                       /* D-Channel without HDLC fill flags */
+                       HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 2);
+               }
+               HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               /* enable RX fifo */
+               HFC_outb(hc, R_FIFO, (ch<<1)|1);
+               HFC_wait(hc);
+               HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04);
+               if (hc->type == 1 || hc->chan[ch].bch)
+                       HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */
+               else
+                       HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */
+               HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               if (hc->chan[ch].bch) {
+                       test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+                       if (hc->type != 1) {
+                               hc->hw.a_st_ctrl0[hc->chan[ch].port] |=
+                                 ((ch&0x3) == 0) ? V_B1_EN : V_B2_EN;
+                               HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               HFC_outb(hc, A_ST_CTRL0,
+                                 hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+                       }
+               }
+               break;
+       default:
+               printk(KERN_DEBUG "%s: protocol not known %x\n",
+                   __func__, protocol);
+               hc->chan[ch].protocol = ISDN_P_NONE;
+               return -ENOPROTOOPT;
+       }
+       hc->chan[ch].protocol = protocol;
+       return 0;
+}
+
+
+/*
+ * connect/disconnect PCM
+ */
+
+static void
+hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx,
+    int slot_rx, int bank_rx)
+{
+       if (slot_rx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) {
+               /* disable PCM */
+               mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0);
+               return;
+       }
+
+       /* enable pcm */
+       mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx,
+               slot_rx, bank_rx);
+}
+
+/*
+ * set/disable conference
+ */
+
+static void
+hfcmulti_conf(struct hfc_multi *hc, int ch, int num)
+{
+       if (num >= 0 && num <= 7)
+               hc->chan[ch].conf = num;
+       else
+               hc->chan[ch].conf = -1;
+       mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx,
+           hc->chan[ch].bank_tx, hc->chan[ch].slot_rx,
+           hc->chan[ch].bank_rx);
+}
+
+
+/*
+ * set/disable sample loop
+ */
+
+/* NOTE: this function is experimental and therefore disabled */
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfcm_l1callback(struct dchannel *dch, u_int cmd)
+{
+       struct hfc_multi        *hc = dch->hw;
+       u_long  flags;
+
+       switch (cmd) {
+       case INFO3_P8:
+       case INFO3_P10:
+               break;
+       case HW_RESET_REQ:
+               /* start activation */
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->type == 1) {
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: HW_RESET_REQ no BRI\n",
+                                   __func__);
+               } else {
+                       HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */
+                       udelay(6); /* wait at least 5,21us */
+                       HFC_outb(hc, A_ST_WR_STATE, 3);
+                       HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3));
+                               /* activate */
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case HW_DEACT_REQ:
+               /* start deactivation */
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->type == 1) {
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: HW_DEACT_REQ no BRI\n",
+                                   __func__);
+               } else {
+                       HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2);
+                               /* deactivate */
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                               hc->syncronized &=
+                                  ~(1 << hc->chan[dch->slot].port);
+                               plxsd_checksync(hc, 0);
+                       }
+               }
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case HW_POWERUP_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->type == 1) {
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: HW_POWERUP_REQ no BRI\n",
+                                   __func__);
+               } else {
+                       HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */
+                       udelay(6); /* wait at least 5,21us */
+                       HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Layer2 -> Layer 1 Transfer
+ */
+
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_multi        *hc = dch->hw;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (skb->len < 1)
+                       break;
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcmulti_tx(hc, dch->slot);
+                       ret = 0;
+                       /* start fifo */
+                       HFC_outb(hc, R_FIFO, 0);
+                       HFC_wait(hc);
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+                       spin_lock_irqsave(&hc->lock, flags);
+                       ret = 0;
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: PH_ACTIVATE port %d (0..%d)\n",
+                                   __func__, hc->chan[dch->slot].port,
+                                   hc->ports-1);
+                       /* start activation */
+                       if (hc->type == 1) {
+                               ph_state_change(dch);
+                               if (debug & DEBUG_HFCMULTI_STATE)
+                                       printk(KERN_DEBUG
+                                           "%s: E1 report state %x \n",
+                                           __func__, dch->state);
+                       } else {
+                               HFC_outb(hc, R_ST_SEL,
+                                   hc->chan[dch->slot].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1);
+                                   /* G1 */
+                               udelay(6); /* wait at least 5,21us */
+                               HFC_outb(hc, A_ST_WR_STATE, 1);
+                               HFC_outb(hc, A_ST_WR_STATE, 1 |
+                                   (V_ST_ACT*3)); /* activate */
+                               dch->state = 1;
+                       }
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               } else
+                       ret = l1_event(dch->l1, hh->prim);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+                       spin_lock_irqsave(&hc->lock, flags);
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: PH_DEACTIVATE port %d (0..%d)\n",
+                                   __func__, hc->chan[dch->slot].port,
+                                   hc->ports-1);
+                       /* start deactivation */
+                       if (hc->type == 1) {
+                               if (debug & DEBUG_HFCMULTI_MSG)
+                                       printk(KERN_DEBUG
+                                           "%s: PH_DEACTIVATE no BRI\n",
+                                           __func__);
+                       } else {
+                               HFC_outb(hc, R_ST_SEL,
+                                   hc->chan[dch->slot].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2);
+                                   /* deactivate */
+                               dch->state = 1;
+                       }
+                       skb_queue_purge(&dch->squeue);
+                       if (dch->tx_skb) {
+                               dev_kfree_skb(dch->tx_skb);
+                               dch->tx_skb = NULL;
+                       }
+                       dch->tx_idx = 0;
+                       if (dch->rx_skb) {
+                               dev_kfree_skb(dch->rx_skb);
+                               dch->rx_skb = NULL;
+                       }
+                       test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+                       if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                               del_timer(&dch->timer);
+#ifdef FIXME
+                       if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+                               dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+                       ret = 0;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               } else
+                       ret = l1_event(dch->l1, hh->prim);
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+       struct hfc_multi        *hc = bch->hw;
+       u_long                  flags;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+               dev_kfree_skb(bch->next_skb);
+               bch->next_skb = NULL;
+       }
+       if (bch->tx_skb) {
+               dev_kfree_skb(bch->tx_skb);
+               bch->tx_skb = NULL;
+       }
+       bch->tx_idx = 0;
+       if (bch->rx_skb) {
+               dev_kfree_skb(bch->rx_skb);
+               bch->rx_skb = NULL;
+       }
+       hc->chan[bch->slot].coeff_count = 0;
+       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+       hc->chan[bch->slot].rx_off = 0;
+       hc->chan[bch->slot].conf = -1;
+       mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
+       spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_multi        *hc = bch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (!skb->len)
+                       break;
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcmulti_tx(hc, bch->slot);
+                       ret = 0;
+                       /* start fifo */
+                       HFC_outb_nodebug(hc, R_FIFO, 0);
+                       HFC_wait_nodebug(hc);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+                       } else
+                               spin_unlock_irqrestore(&hc->lock, flags);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n",
+                               __func__, bch->slot);
+               spin_lock_irqsave(&hc->lock, flags);
+               /* activate B-channel if not already activated */
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
+                       hc->chan[bch->slot].txpending = 0;
+                       ret = mode_hfcmulti(hc, bch->slot,
+                               ch->protocol,
+                               hc->chan[bch->slot].slot_tx,
+                               hc->chan[bch->slot].bank_tx,
+                               hc->chan[bch->slot].slot_rx,
+                               hc->chan[bch->slot].bank_rx);
+                       if (!ret) {
+                               if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf
+                                       && test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+                                       /* start decoder */
+                                       hc->dtmf = 1;
+                                       if (debug & DEBUG_HFCMULTI_DTMF)
+                                               printk(KERN_DEBUG
+                                                   "%s: start dtmf decoder\n",
+                                                       __func__);
+                                       HFC_outb(hc, R_DTMF, hc->hw.r_dtmf |
+                                           V_RST_DTMF);
+                               }
+                       }
+               } else
+                       ret = 0;
+               spin_unlock_irqrestore(&hc->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+                               GFP_KERNEL);
+               break;
+       case PH_CONTROL_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               switch (hh->id) {
+               case HFC_SPL_LOOP_ON: /* set sample loop */
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG
+                           "%s: HFC_SPL_LOOP_ON (len = %d)\n",
+                           __func__, skb->len);
+                       ret = 0;
+                       break;
+               case HFC_SPL_LOOP_OFF: /* set silence */
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n",
+                                   __func__);
+                       ret = 0;
+                       break;
+               default:
+                       printk(KERN_ERR
+                            "%s: unknown PH_CONTROL_REQ info %x\n",
+                            __func__, hh->id);
+                       ret = -EINVAL;
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case PH_DEACTIVATE_REQ:
+               deactivate_bchannel(bch); /* locked there */
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+                       GFP_KERNEL);
+               ret = 0;
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+/*
+ * bchannel control function
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int                     ret = 0;
+       struct dsp_features     *features =
+               (struct dsp_features *)(*((u_long *)&cq->p1));
+       struct hfc_multi        *hc = bch->hw;
+       int                     slot_tx;
+       int                     bank_tx;
+       int                     slot_rx;
+       int                     bank_rx;
+       int                     num;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP
+                       | MISDN_CTRL_RX_OFF;
+               break;
+       case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */
+               hc->chan[bch->slot].rx_off = !!cq->p1;
+               if (!hc->chan[bch->slot].rx_off) {
+                       /* reset fifo on rx on */
+                       HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1);
+                       HFC_wait_nodebug(hc);
+                       HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait_nodebug(hc);
+               }
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n",
+                           __func__, bch->nr, hc->chan[bch->slot].rx_off);
+               break;
+       case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+                           __func__);
+               /* create confirm */
+               features->hfc_id = hc->id;
+               if (test_bit(HFC_CHIP_DTMF, &hc->chip))
+                       features->hfc_dtmf = 1;
+               features->hfc_loops = 0;
+               if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+                       features->hfc_echocanhw = 1;
+               } else {
+                       features->pcm_id = hc->pcm;
+                       features->pcm_slots = hc->slots;
+                       features->pcm_banks = 2;
+               }
+               break;
+       case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */
+               slot_tx = cq->p1 & 0xff;
+               bank_tx = cq->p1 >> 8;
+               slot_rx = cq->p2 & 0xff;
+               bank_rx = cq->p2 >> 8;
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG
+                           "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+                           "slot %d bank %d (RX)\n",
+                           __func__, slot_tx, bank_tx,
+                           slot_rx, bank_rx);
+               if (slot_tx < hc->slots && bank_tx <= 2 &&
+                   slot_rx < hc->slots && bank_rx <= 2)
+                       hfcmulti_pcm(hc, bch->slot,
+                           slot_tx, bank_tx, slot_rx, bank_rx);
+               else {
+                       printk(KERN_WARNING
+                           "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+                           "slot %d bank %d (RX) out of range\n",
+                           __func__, slot_tx, bank_tx,
+                           slot_rx, bank_rx);
+                       ret = -EINVAL;
+               }
+               break;
+       case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_PCM_DISC\n",
+                           __func__);
+               hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0);
+               break;
+       case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */
+               num = cq->p1 & 0xff;
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n",
+                           __func__, num);
+               if (num <= 7)
+                       hfcmulti_conf(hc, bch->slot, num);
+               else {
+                       printk(KERN_WARNING
+                           "%s: HW_CONF_JOIN conf %d out of range\n",
+                           __func__, num);
+                       ret = -EINVAL;
+               }
+               break;
+       case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__);
+               hfcmulti_conf(hc, bch->slot, -1);
+               break;
+       case MISDN_CTRL_HFC_ECHOCAN_ON:
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__);
+               if (test_bit(HFC_CHIP_B410P, &hc->chip))
+                       vpm_echocan_on(hc, bch->slot, cq->p1);
+               else
+                       ret = -EINVAL;
+               break;
+
+       case MISDN_CTRL_HFC_ECHOCAN_OFF:
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n",
+                               __func__);
+               if (test_bit(HFC_CHIP_B410P, &hc->chip))
+                       vpm_echocan_off(hc, bch->slot);
+               else
+                       ret = -EINVAL;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_multi        *hc = bch->hw;
+       int                     err = -EINVAL;
+       u_long  flags;
+
+       if (bch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags))
+                       deactivate_bchannel(bch); /* locked there */
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               err = 0;
+               break;
+       case CONTROL_CHANNEL:
+               spin_lock_irqsave(&hc->lock, flags);
+               err = channel_bctrl(bch, arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown prim(%x)\n",
+                       __func__, cmd);
+       }
+       return err;
+}
+
+/*
+ * handle D-channel events
+ *
+ * handle state change event
+ */
+static void
+ph_state_change(struct dchannel *dch)
+{
+       struct hfc_multi *hc = dch->hw;
+       int ch, i;
+
+       if (!dch) {
+               printk(KERN_WARNING "%s: ERROR given dch is NULL\n",
+                   __func__);
+               return;
+       }
+       ch = dch->slot;
+
+       if (hc->type == 1) {
+               if (dch->dev.D.protocol == ISDN_P_TE_E1) {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG
+                                   "%s: E1 TE (id=%d) newstate %x\n",
+                                   __func__, hc->id, dch->state);
+               } else {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG
+                                   "%s: E1 NT (id=%d) newstate %x\n",
+                                   __func__, hc->id, dch->state);
+               }
+               switch (dch->state) {
+               case (1):
+                       if (hc->e1_state != 1) {
+                           for (i = 1; i <= 31; i++) {
+                               /* reset fifos on e1 activation */
+                               HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1);
+                               HFC_wait_nodebug(hc);
+                               HFC_outb_nodebug(hc,
+                                       R_INC_RES_FIFO, V_RES_F);
+                               HFC_wait_nodebug(hc);
+                           }
+                       }
+                       test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+                       _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+                           MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                       break;
+
+               default:
+                       if (hc->e1_state != 1)
+                               return;
+                       test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+                       _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+                           MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+               }
+               hc->e1_state = dch->state;
+       } else {
+               if (dch->dev.D.protocol == ISDN_P_TE_S0) {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG
+                                   "%s: S/T TE newstate %x\n",
+                                   __func__, dch->state);
+                       switch (dch->state) {
+                       case (0):
+                               l1_event(dch->l1, HW_RESET_IND);
+                               break;
+                       case (3):
+                               l1_event(dch->l1, HW_DEACT_IND);
+                               break;
+                       case (5):
+                       case (8):
+                               l1_event(dch->l1, ANYSIGNAL);
+                               break;
+                       case (6):
+                               l1_event(dch->l1, INFO2);
+                               break;
+                       case (7):
+                               l1_event(dch->l1, INFO4_P8);
+                               break;
+                       }
+               } else {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG "%s: S/T NT newstate %x\n",
+                                   __func__, dch->state);
+                       switch (dch->state) {
+                       case (2):
+                               if (hc->chan[ch].nt_timer == 0) {
+                                       hc->chan[ch].nt_timer = -1;
+                                       HFC_outb(hc, R_ST_SEL,
+                                           hc->chan[ch].port);
+                                       /* undocumented: delay after R_ST_SEL */
+                                       udelay(1);
+                                       HFC_outb(hc, A_ST_WR_STATE, 4 |
+                                           V_ST_LD_STA); /* G4 */
+                                       udelay(6); /* wait at least 5,21us */
+                                       HFC_outb(hc, A_ST_WR_STATE, 4);
+                                       dch->state = 4;
+                               } else {
+                                       /* one extra count for the next event */
+                                       hc->chan[ch].nt_timer =
+                                           nt_t1_count[poll_timer] + 1;
+                                       HFC_outb(hc, R_ST_SEL,
+                                           hc->chan[ch].port);
+                                       /* undocumented: delay after R_ST_SEL */
+                                       udelay(1);
+                                       /* allow G2 -> G3 transition */
+                                       HFC_outb(hc, A_ST_WR_STATE, 2 |
+                                           V_SET_G2_G3);
+                               }
+                               break;
+                       case (1):
+                               hc->chan[ch].nt_timer = -1;
+                               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+                               _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+                                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                               break;
+                       case (4):
+                               hc->chan[ch].nt_timer = -1;
+                               break;
+                       case (3):
+                               hc->chan[ch].nt_timer = -1;
+                               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+                               _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+                                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                               break;
+                       }
+               }
+       }
+}
+
+/*
+ * called for card mode init message
+ */
+
+static void
+hfcmulti_initmode(struct dchannel *dch)
+{
+       struct hfc_multi *hc = dch->hw;
+       u_char          a_st_wr_state, r_e1_wr_sta;
+       int             i, pt;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+
+       if (hc->type == 1) {
+               hc->chan[hc->dslot].slot_tx = -1;
+               hc->chan[hc->dslot].slot_rx = -1;
+               hc->chan[hc->dslot].conf = -1;
+               if (hc->dslot) {
+                       mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol,
+                               -1, 0, -1, 0);
+                       dch->timer.function = (void *) hfcmulti_dbusy_timer;
+                       dch->timer.data = (long) dch;
+                       init_timer(&dch->timer);
+               }
+               for (i = 1; i <= 31; i++) {
+                       if (i == hc->dslot)
+                               continue;
+                       hc->chan[i].slot_tx = -1;
+                       hc->chan[i].slot_rx = -1;
+                       hc->chan[i].conf = -1;
+                       mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0);
+               }
+               /* E1 */
+               if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+                       HFC_outb(hc, R_LOS0, 255); /* 2 ms */
+                       HFC_outb(hc, R_LOS1, 255); /* 512 ms */
+               }
+               if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) {
+                       HFC_outb(hc, R_RX0, 0);
+                       hc->hw.r_tx0 = 0 | V_OUT_EN;
+               } else {
+                       HFC_outb(hc, R_RX0, 1);
+                       hc->hw.r_tx0 = 1 | V_OUT_EN;
+               }
+               hc->hw.r_tx1 = V_ATX | V_NTRI;
+               HFC_outb(hc, R_TX0, hc->hw.r_tx0);
+               HFC_outb(hc, R_TX1, hc->hw.r_tx1);
+               HFC_outb(hc, R_TX_FR0, 0x00);
+               HFC_outb(hc, R_TX_FR1, 0xf8);
+
+               if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+                       HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E);
+
+               HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0);
+
+               if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+                       HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC);
+
+               if (dch->dev.D.protocol == ISDN_P_NT_E1) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG "%s: E1 port is NT-mode\n",
+                                   __func__);
+                       r_e1_wr_sta = 0; /* G0 */
+                       hc->e1_getclock = 0;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG "%s: E1 port is TE-mode\n",
+                                   __func__);
+                       r_e1_wr_sta = 0; /* F0 */
+                       hc->e1_getclock = 1;
+               }
+               if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+                       HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+               else
+                       HFC_outb(hc, R_SYNC_OUT, 0);
+               if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip))
+                       hc->e1_getclock = 1;
+               if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip))
+                       hc->e1_getclock = 0;
+               if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+                       /* SLAVE (clock master) */
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: E1 port is clock master "
+                                   "(clock from PCM)\n", __func__);
+                       HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC);
+               } else {
+                       if (hc->e1_getclock) {
+                               /* MASTER (clock slave) */
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                       printk(KERN_DEBUG
+                                           "%s: E1 port is clock slave "
+                                           "(clock to PCM)\n", __func__);
+                               HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+                       } else {
+                               /* MASTER (clock master) */
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                       printk(KERN_DEBUG "%s: E1 port is "
+                                           "clock master "
+                                           "(clock from QUARTZ)\n",
+                                           __func__);
+                               HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC |
+                                   V_PCM_SYNC | V_JATT_OFF);
+                               HFC_outb(hc, R_SYNC_OUT, 0);
+                       }
+               }
+               HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */
+               HFC_outb(hc, R_PWM_MD, V_PWM0_MD);
+               HFC_outb(hc, R_PWM0, 0x50);
+               HFC_outb(hc, R_PWM1, 0xff);
+               /* state machine setup */
+               HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA);
+               udelay(6); /* wait at least 5,21us */
+               HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta);
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized = 0;
+                       plxsd_checksync(hc, 0);
+               }
+       } else {
+               i = dch->slot;
+               hc->chan[i].slot_tx = -1;
+               hc->chan[i].slot_rx = -1;
+               hc->chan[i].conf = -1;
+               mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0);
+               dch->timer.function = (void *)hfcmulti_dbusy_timer;
+               dch->timer.data = (long) dch;
+               init_timer(&dch->timer);
+               hc->chan[i - 2].slot_tx = -1;
+               hc->chan[i - 2].slot_rx = -1;
+               hc->chan[i - 2].conf = -1;
+               mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0);
+               hc->chan[i - 1].slot_tx = -1;
+               hc->chan[i - 1].slot_rx = -1;
+               hc->chan[i - 1].conf = -1;
+               mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0);
+               /* ST */
+               pt = hc->chan[i].port;
+               /* select interface */
+               HFC_outb(hc, R_ST_SEL, pt);
+               /* undocumented: delay after R_ST_SEL */
+               udelay(1);
+               if (dch->dev.D.protocol == ISDN_P_NT_S0) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: ST port %d is NT-mode\n",
+                                   __func__, pt);
+                       /* clock delay */
+                       HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt);
+                       a_st_wr_state = 1; /* G1 */
+                       hc->hw.a_st_ctrl0[pt] = V_ST_MD;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: ST port %d is TE-mode\n",
+                                   __func__, pt);
+                       /* clock delay */
+                       HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te);
+                       a_st_wr_state = 2; /* F2 */
+                       hc->hw.a_st_ctrl0[pt] = 0;
+               }
+               if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg))
+                       hc->hw.a_st_ctrl0[pt] |= V_TX_LI;
+               /* line setup */
+               HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[pt]);
+               /* disable E-channel */
+               if ((dch->dev.D.protocol == ISDN_P_NT_S0) ||
+                   test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg))
+                       HFC_outb(hc, A_ST_CTRL1, V_E_IGNO);
+               else
+                       HFC_outb(hc, A_ST_CTRL1, 0);
+               /* enable B-channel receive */
+               HFC_outb(hc, A_ST_CTRL2,  V_B1_RX_EN | V_B2_RX_EN);
+               /* state machine setup */
+               HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA);
+               udelay(6); /* wait at least 5,21us */
+               HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state);
+               hc->hw.r_sci_msk |= 1 << pt;
+               /* state machine interrupts */
+               HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk);
+               /* unset sync on port */
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized &=
+                          ~(1 << hc->chan[dch->slot].port);
+                       plxsd_checksync(hc, 0);
+               }
+       }
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk("%s: done\n", __func__);
+}
+
+
+static int
+open_dchannel(struct hfc_multi *hc, struct dchannel *dch,
+    struct channel_req *rq)
+{
+       int     err = 0;
+       u_long  flags;
+
+       if (debug & DEBUG_HW_OPEN)
+               printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+                   dch->dev.id, __builtin_return_address(0));
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+           (dch->dev.D.protocol != rq->protocol)) {
+           if (debug & DEBUG_HFCMULTI_MODE)
+               printk(KERN_WARNING "%s: change protocol %x to %x\n",
+                   __func__, dch->dev.D.protocol, rq->protocol);
+       }
+       if ((dch->dev.D.protocol == ISDN_P_TE_S0)
+        && (rq->protocol != ISDN_P_TE_S0))
+               l1_event(dch->l1, CLOSE_CHANNEL);
+       if (dch->dev.D.protocol != rq->protocol) {
+               if (rq->protocol == ISDN_P_TE_S0) {
+                       err = create_l1(dch, hfcm_l1callback);
+                       if (err)
+                               return err;
+               }
+               dch->dev.D.protocol = rq->protocol;
+               spin_lock_irqsave(&hc->lock, flags);
+               hfcmulti_initmode(dch);
+               spin_unlock_irqrestore(&hc->lock, flags);
+       }
+
+       if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) ||
+           ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) ||
+           ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) ||
+           ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) {
+               _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       }
+       rq->ch = &dch->dev.D;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+open_bchannel(struct hfc_multi *hc, struct dchannel *dch,
+    struct channel_req *rq)
+{
+       struct bchannel *bch;
+       int             ch;
+
+       if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0]))
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if (hc->type == 1)
+               ch = rq->adr.channel;
+       else
+               ch = (rq->adr.channel - 1) + (dch->slot - 2);
+       bch = hc->chan[ch].bch;
+       if (!bch) {
+               printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+                   __func__, ch);
+               return -EINVAL;
+       }
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       bch->ch.protocol = rq->protocol;
+       hc->chan[ch].rx_off = 0;
+       rq->ch = &bch->ch;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_multi        *hc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+       u_long                  flags;
+
+       if (dch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               switch (rq->protocol) {
+               case ISDN_P_TE_S0:
+               case ISDN_P_NT_S0:
+                       if (hc->type == 1) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq); /* locked there */
+                       break;
+               case ISDN_P_TE_E1:
+               case ISDN_P_NT_E1:
+                       if (hc->type != 1) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq); /* locked there */
+                       break;
+               default:
+                       spin_lock_irqsave(&hc->lock, flags);
+                       err = open_bchannel(hc, dch, rq);
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+                           __func__, dch->dev.id,
+                           __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               spin_lock_irqsave(&hc->lock, flags);
+               err = channel_dctrl(dch, arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               err = -EINVAL;
+       }
+       return err;
+}
+
+/*
+ * initialize the card
+ */
+
+/*
+ * start timer irq, wait some time and check if we have interrupts.
+ * if not, reset chip and try again.
+ */
+static int
+init_card(struct hfc_multi *hc)
+{
+       int     err = -EIO;
+       u_long  flags;
+       u_short *plx_acc;
+       u_long  plx_flags;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+
+       spin_lock_irqsave(&hc->lock, flags);
+       /* set interrupts but leave global interrupt disabled */
+       hc->hw.r_irq_ctrl = V_FIFO_IRQ;
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+
+       if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED,
+           "HFC-multi", hc)) {
+               printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n",
+                   hc->pci_dev->irq);
+               return -EIO;
+       }
+       hc->irq = hc->pci_dev->irq;
+
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+               writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE),
+                       plx_acc); /* enable PCI & LINT1 irq */
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+                   __func__, hc->irq, hc->irqcnt);
+       err = init_chip(hc);
+       if (err)
+               goto error;
+       /*
+        * Finally enable IRQ output
+        * this is only allowed, if an IRQ routine is allready
+        * established for this HFC, so don't do that earlier
+        */
+       spin_lock_irqsave(&hc->lock, flags);
+       enable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       /* printk(KERN_DEBUG "no master irq set!!!\n"); */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
+       /* turn IRQ off until chip is completely initialized */
+       spin_lock_irqsave(&hc->lock, flags);
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+                   __func__, hc->irq, hc->irqcnt);
+       if (hc->irqcnt) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: done\n", __func__);
+
+               return 0;
+       }
+       if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+               printk(KERN_INFO "ignoring missing interrupts\n");
+               return 0;
+       }
+
+       printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n",
+               hc->irq);
+
+       err = -EIO;
+
+error:
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+               writew(0x00, plx_acc); /*disable IRQs*/
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq);
+       if (hc->irq) {
+               free_irq(hc->irq, hc);
+               hc->irq = 0;
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err);
+       return err;
+}
+
+/*
+ * find pci device and set it up
+ */
+
+static int
+setup_pci(struct hfc_multi *hc, struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       struct hm_map   *m = (struct hm_map *)ent->driver_data;
+
+       printk(KERN_INFO
+           "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n",
+           m->vendor_name, m->card_name, m->clock2 ? "double" : "normal");
+
+       hc->pci_dev = pdev;
+       if (m->clock2)
+               test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
+
+       if (ent->device == 0xB410) {
+               test_and_set_bit(HFC_CHIP_B410P, &hc->chip);
+               test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+               test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+               hc->slots = 32;
+       }
+
+       if (hc->pci_dev->irq <= 0) {
+               printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
+               return -EIO;
+       }
+       if (pci_enable_device(hc->pci_dev)) {
+               printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n");
+               return -EIO;
+       }
+       hc->leds = m->leds;
+       hc->ledstate = 0xAFFEAFFE;
+       hc->opticalsupport = m->opticalsupport;
+
+       /* set memory access methods */
+       if (m->io_mode) /* use mode from card config */
+               hc->io_mode = m->io_mode;
+       switch (hc->io_mode) {
+       case HFC_IO_MODE_PLXSD:
+               test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip);
+               hc->slots = 128; /* required */
+               /* fall through */
+       case HFC_IO_MODE_PCIMEM:
+               hc->HFC_outb = HFC_outb_pcimem;
+               hc->HFC_inb = HFC_inb_pcimem;
+               hc->HFC_inw = HFC_inw_pcimem;
+               hc->HFC_wait = HFC_wait_pcimem;
+               hc->read_fifo = read_fifo_pcimem;
+               hc->write_fifo = write_fifo_pcimem;
+               break;
+       case HFC_IO_MODE_REGIO:
+               hc->HFC_outb = HFC_outb_regio;
+               hc->HFC_inb = HFC_inb_regio;
+               hc->HFC_inw = HFC_inw_regio;
+               hc->HFC_wait = HFC_wait_regio;
+               hc->read_fifo = read_fifo_regio;
+               hc->write_fifo = write_fifo_regio;
+               break;
+       default:
+               printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+               pci_disable_device(hc->pci_dev);
+               return -EIO;
+       }
+       hc->HFC_outb_nodebug = hc->HFC_outb;
+       hc->HFC_inb_nodebug = hc->HFC_inb;
+       hc->HFC_inw_nodebug = hc->HFC_inw;
+       hc->HFC_wait_nodebug = hc->HFC_wait;
+#ifdef HFC_REGISTER_DEBUG
+       hc->HFC_outb = HFC_outb_debug;
+       hc->HFC_inb = HFC_inb_debug;
+       hc->HFC_inw = HFC_inw_debug;
+       hc->HFC_wait = HFC_wait_debug;
+#endif
+       hc->pci_iobase = 0;
+       hc->pci_membase = NULL;
+       hc->plx_membase = NULL;
+
+       switch (hc->io_mode) {
+       case HFC_IO_MODE_PLXSD:
+               hc->plx_origmembase =  hc->pci_dev->resource[0].start;
+               /* MEMBASE 1 is PLX PCI Bridge */
+
+               if (!hc->plx_origmembase) {
+                       printk(KERN_WARNING
+                         "HFC-multi: No IO-Memory for PCI PLX bridge found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               hc->plx_membase = ioremap(hc->plx_origmembase, 0x80);
+               if (!hc->plx_membase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: failed to remap plx address space. "
+                           "(internal error)\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+               printk(KERN_INFO
+                   "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n",
+                   (u_long)hc->plx_membase, hc->plx_origmembase);
+
+               hc->pci_origmembase =  hc->pci_dev->resource[2].start;
+                   /* MEMBASE 1 is PLX PCI Bridge */
+               if (!hc->pci_origmembase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: No IO-Memory for PCI card found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               hc->pci_membase = ioremap(hc->pci_origmembase, 0x400);
+               if (!hc->pci_membase) {
+                       printk(KERN_WARNING "HFC-multi: failed to remap io "
+                           "address space. (internal error)\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               printk(KERN_INFO
+                   "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d "
+                   "leds-type %d\n",
+                   hc->id, (u_long)hc->pci_membase, hc->pci_origmembase,
+                   hc->pci_dev->irq, HZ, hc->leds);
+               pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+               break;
+       case HFC_IO_MODE_PCIMEM:
+               hc->pci_origmembase = hc->pci_dev->resource[1].start;
+               if (!hc->pci_origmembase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: No IO-Memory for PCI card found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               hc->pci_membase = ioremap(hc->pci_origmembase, 256);
+               if (!hc->pci_membase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: failed to remap io address space. "
+                           "(internal error)\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+               printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d "
+                   "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase,
+                   hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds);
+               pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+               break;
+       case HFC_IO_MODE_REGIO:
+               hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start;
+               if (!hc->pci_iobase) {
+                       printk(KERN_WARNING
+                               "HFC-multi: No IO for PCI card found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               if (!request_region(hc->pci_iobase, 8, "hfcmulti")) {
+                       printk(KERN_WARNING "HFC-multi: failed to request "
+                           "address space at 0x%08lx (internal error)\n",
+                           hc->pci_iobase);
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               printk(KERN_INFO
+                   "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n",
+                   m->vendor_name, m->card_name, (u_int) hc->pci_iobase,
+                   hc->pci_dev->irq, HZ, hc->leds);
+               pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO);
+               break;
+       default:
+               printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+               pci_disable_device(hc->pci_dev);
+               return -EIO;
+       }
+
+       pci_set_drvdata(hc->pci_dev, hc);
+
+       /* At this point the needed PCI config is done */
+       /* fifos are still not enabled */
+       return 0;
+}
+
+
+/*
+ * remove port
+ */
+
+static void
+release_port(struct hfc_multi *hc, struct dchannel *dch)
+{
+       int     pt, ci, i = 0;
+       u_long  flags;
+       struct bchannel *pb;
+
+       ci = dch->slot;
+       pt = hc->chan[ci].port;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered for port %d\n",
+                       __func__, pt + 1);
+
+       if (pt >= hc->ports) {
+               printk(KERN_WARNING "%s: ERROR port out of range (%d).\n",
+                    __func__, pt + 1);
+               return;
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: releasing port=%d\n",
+                   __func__, pt + 1);
+
+       if (dch->dev.D.protocol == ISDN_P_TE_S0)
+               l1_event(dch->l1, CLOSE_CHANNEL);
+
+       hc->chan[ci].dch = NULL;
+
+       if (hc->created[pt]) {
+               hc->created[pt] = 0;
+               mISDN_unregister_device(&dch->dev);
+       }
+
+       spin_lock_irqsave(&hc->lock, flags);
+
+       if (dch->timer.function) {
+               del_timer(&dch->timer);
+               dch->timer.function = NULL;
+       }
+
+       if (hc->type == 1) { /* E1 */
+               /* remove sync */
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized = 0;
+                       plxsd_checksync(hc, 1);
+               }
+               /* free channels */
+               for (i = 0; i <= 31; i++) {
+                       if (hc->chan[i].bch) {
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                       printk(KERN_DEBUG
+                                           "%s: free port %d channel %d\n",
+                                           __func__, hc->chan[i].port+1, i);
+                               pb = hc->chan[i].bch;
+                               hc->chan[i].bch = NULL;
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               mISDN_freebchannel(pb);
+                               kfree(pb);
+                               kfree(hc->chan[i].coeff);
+                               spin_lock_irqsave(&hc->lock, flags);
+                       }
+               }
+       } else {
+               /* remove sync */
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized &=
+                          ~(1 << hc->chan[ci].port);
+                       plxsd_checksync(hc, 1);
+               }
+               /* free channels */
+               if (hc->chan[ci - 2].bch) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: free port %d channel %d\n",
+                                   __func__, hc->chan[ci - 2].port+1,
+                                   ci - 2);
+                       pb = hc->chan[ci - 2].bch;
+                       hc->chan[ci - 2].bch = NULL;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       mISDN_freebchannel(pb);
+                       kfree(pb);
+                       kfree(hc->chan[ci - 2].coeff);
+                       spin_lock_irqsave(&hc->lock, flags);
+               }
+               if (hc->chan[ci - 1].bch) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: free port %d channel %d\n",
+                                   __func__, hc->chan[ci - 1].port+1,
+                                   ci - 1);
+                       pb = hc->chan[ci - 1].bch;
+                       hc->chan[ci - 1].bch = NULL;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       mISDN_freebchannel(pb);
+                       kfree(pb);
+                       kfree(hc->chan[ci - 1].coeff);
+                       spin_lock_irqsave(&hc->lock, flags);
+               }
+       }
+
+       spin_unlock_irqrestore(&hc->lock, flags);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt);
+       mISDN_freedchannel(dch);
+       kfree(dch);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done!\n", __func__);
+}
+
+static void
+release_card(struct hfc_multi *hc)
+{
+       u_long  flags;
+       int     ch;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: release card (%d) entered\n",
+                   __func__, hc->id);
+
+       spin_lock_irqsave(&hc->lock, flags);
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+
+       udelay(1000);
+
+       /* dimm leds */
+       if (hc->leds)
+               hfcmulti_leds(hc);
+
+       /* disable D-channels & B-channels */
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: disable all channels (d and b)\n",
+                   __func__);
+       for (ch = 0; ch <= 31; ch++) {
+               if (hc->chan[ch].dch)
+                       release_port(hc, hc->chan[ch].dch);
+       }
+
+       /* release hardware & irq */
+       if (hc->irq) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: free irq %d\n",
+                           __func__, hc->irq);
+               free_irq(hc->irq, hc);
+               hc->irq = 0;
+
+       }
+       release_io_hfcmulti(hc);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: remove instance from list\n",
+                    __func__);
+       list_del(&hc->list);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: delete instance\n", __func__);
+       if (hc == syncmaster)
+               syncmaster = NULL;
+       kfree(hc);
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: card successfully removed\n",
+                   __func__);
+}
+
+static int
+init_e1_port(struct hfc_multi *hc, struct hm_map *m)
+{
+       struct dchannel *dch;
+       struct bchannel *bch;
+       int             ch, ret = 0;
+       char            name[MISDN_MAX_IDLEN];
+
+       dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+       if (!dch)
+               return -ENOMEM;
+       dch->debug = debug;
+       mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+       dch->hw = hc;
+       dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+       dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       dch->dev.D.send = handle_dmsg;
+       dch->dev.D.ctrl = hfcm_dctrl;
+       dch->dev.nrbchan = (hc->dslot)?30:31;
+       dch->slot = hc->dslot;
+       hc->chan[hc->dslot].dch = dch;
+       hc->chan[hc->dslot].port = 0;
+       hc->chan[hc->dslot].nt_timer = -1;
+       for (ch = 1; ch <= 31; ch++) {
+               if (ch == hc->dslot) /* skip dchannel */
+                       continue;
+               bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+               if (!bch) {
+                       printk(KERN_ERR "%s: no memory for bchannel\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL);
+               if (!hc->chan[ch].coeff) {
+                       printk(KERN_ERR "%s: no memory for coeffs\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               bch->nr = ch;
+               bch->slot = ch;
+               bch->debug = debug;
+               mISDN_initbchannel(bch, MAX_DATA_MEM);
+               bch->hw = hc;
+               bch->ch.send = handle_bmsg;
+               bch->ch.ctrl = hfcm_bctrl;
+               bch->ch.nr = ch;
+               list_add(&bch->ch.list, &dch->dev.bchannels);
+               hc->chan[ch].bch = bch;
+               hc->chan[ch].port = 0;
+               test_and_set_bit(bch->nr, &dch->dev.channelmap[0]);
+       }
+       /* set optical line type */
+       if (port[Port_cnt] & 0x001) {
+               if (!m->opticalsupport)  {
+                       printk(KERN_INFO
+                           "This board has no optical "
+                           "support\n");
+               } else {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: PORT set optical "
+                                   "interfacs: card(%d) "
+                                   "port(%d)\n",
+                                   __func__,
+                                   HFC_cnt + 1, 1);
+                       test_and_set_bit(HFC_CFG_OPTICAL,
+                           &hc->chan[hc->dslot].cfg);
+               }
+       }
+       /* set LOS report */
+       if (port[Port_cnt] & 0x004) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT set "
+                           "LOS report: card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_LOS,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set AIS report */
+       if (port[Port_cnt] & 0x008) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT set "
+                           "AIS report: card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_AIS,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set SLIP report */
+       if (port[Port_cnt] & 0x010) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PORT set SLIP report: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_SLIP,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set RDI report */
+       if (port[Port_cnt] & 0x020) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PORT set RDI report: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_RDI,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set CRC-4 Mode */
+       if (!(port[Port_cnt] & 0x100)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT turn on CRC4 report:"
+                               " card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_CRC4,
+                   &hc->chan[hc->dslot].cfg);
+       } else {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT turn off CRC4"
+                               " report: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+       }
+       /* set forced clock */
+       if (port[Port_cnt] & 0x0200) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT force getting clock from "
+                               "E1: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip);
+       } else
+       if (port[Port_cnt] & 0x0400) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT force putting clock to "
+                               "E1: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip);
+       }
+       /* set JATT PLL */
+       if (port[Port_cnt] & 0x0800) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT disable JATT PLL on "
+                               "E1: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip);
+       }
+       /* set elastic jitter buffer */
+       if (port[Port_cnt] & 0x3000) {
+               hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3;
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PORT set elastic "
+                           "buffer to %d: card(%d) port(%d)\n",
+                           __func__, hc->chan[hc->dslot].jitter,
+                           HFC_cnt + 1, 1);
+       } else
+               hc->chan[hc->dslot].jitter = 2; /* default */
+       snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1);
+       ret = mISDN_register_device(&dch->dev, name);
+       if (ret)
+               goto free_chan;
+       hc->created[0] = 1;
+       return ret;
+free_chan:
+       release_port(hc, dch);
+       return ret;
+}
+
+static int
+init_multi_port(struct hfc_multi *hc, int pt)
+{
+       struct dchannel *dch;
+       struct bchannel *bch;
+       int             ch, i, ret = 0;
+       char            name[MISDN_MAX_IDLEN];
+
+       dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+       if (!dch)
+               return -ENOMEM;
+       dch->debug = debug;
+       mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+       dch->hw = hc;
+       dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+       dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       dch->dev.D.send = handle_dmsg;
+       dch->dev.D.ctrl = hfcm_dctrl;
+       dch->dev.nrbchan = 2;
+       i = pt << 2;
+       dch->slot = i + 2;
+       hc->chan[i + 2].dch = dch;
+       hc->chan[i + 2].port = pt;
+       hc->chan[i + 2].nt_timer = -1;
+       for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+               bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+               if (!bch) {
+                       printk(KERN_ERR "%s: no memory for bchannel\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL);
+               if (!hc->chan[i + ch].coeff) {
+                       printk(KERN_ERR "%s: no memory for coeffs\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               bch->nr = ch + 1;
+               bch->slot = i + ch;
+               bch->debug = debug;
+               mISDN_initbchannel(bch, MAX_DATA_MEM);
+               bch->hw = hc;
+               bch->ch.send = handle_bmsg;
+               bch->ch.ctrl = hfcm_bctrl;
+               bch->ch.nr = ch + 1;
+               list_add(&bch->ch.list, &dch->dev.bchannels);
+               hc->chan[i + ch].bch = bch;
+               hc->chan[i + ch].port = pt;
+               test_and_set_bit(bch->nr, &dch->dev.channelmap[0]);
+       }
+       /* set master clock */
+       if (port[Port_cnt] & 0x001) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PROTOCOL set master clock: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, pt + 1);
+               if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+                       printk(KERN_ERR "Error: Master clock "
+                           "for port(%d) of card(%d) is only"
+                           " possible with TE-mode\n",
+                           pt + 1, HFC_cnt + 1);
+                       ret = -EINVAL;
+                       goto free_chan;
+               }
+               if (hc->masterclk >= 0) {
+                       printk(KERN_ERR "Error: Master clock "
+                           "for port(%d) of card(%d) already "
+                           "defined for port(%d)\n",
+                           pt + 1, HFC_cnt + 1, hc->masterclk+1);
+                       ret = -EINVAL;
+                       goto free_chan;
+               }
+               hc->masterclk = pt;
+       }
+       /* set transmitter line to non capacitive */
+       if (port[Port_cnt] & 0x002) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PROTOCOL set non capacitive "
+                           "transmitter: card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, pt + 1);
+               test_and_set_bit(HFC_CFG_NONCAP_TX,
+                   &hc->chan[i + 2].cfg);
+       }
+       /* disable E-channel */
+       if (port[Port_cnt] & 0x004) {
+       if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PROTOCOL disable E-channel: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, pt + 1);
+               test_and_set_bit(HFC_CFG_DIS_ECHANNEL,
+                   &hc->chan[i + 2].cfg);
+       }
+       snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d",
+               hc->type, HFC_cnt + 1, pt + 1);
+       ret = mISDN_register_device(&dch->dev, name);
+       if (ret)
+               goto free_chan;
+       hc->created[pt] = 1;
+       return ret;
+free_chan:
+       release_port(hc, dch);
+       return ret;
+}
+
+static int
+hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct hm_map   *m = (struct hm_map *)ent->driver_data;
+       int             ret_err = 0;
+       int             pt;
+       struct hfc_multi        *hc;
+       u_long          flags;
+       u_char          dips = 0, pmj = 0; /* dip settings, port mode Jumpers */
+
+       if (HFC_cnt >= MAX_CARDS) {
+               printk(KERN_ERR "too many cards (max=%d).\n",
+                       MAX_CARDS);
+               return -EINVAL;
+       }
+       if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) {
+               printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but "
+                   "type[%d] %d was supplied as module parameter\n",
+                   m->vendor_name, m->card_name, m->type, HFC_cnt,
+                   type[HFC_cnt] & 0xff);
+               printk(KERN_WARNING "HFC-MULTI: Load module without parameters "
+                       "first, to see cards and their types.");
+               return -EINVAL;
+       }
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n",
+                   __func__, m->vendor_name, m->card_name, m->type,
+                   type[HFC_cnt]);
+
+       /* allocate card+fifo structure */
+       hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL);
+       if (!hc) {
+               printk(KERN_ERR "No kmem for HFC-Multi card\n");
+               return -ENOMEM;
+       }
+       spin_lock_init(&hc->lock);
+       hc->mtyp = m;
+       hc->type =  m->type;
+       hc->ports = m->ports;
+       hc->id = HFC_cnt;
+       hc->pcm = pcm[HFC_cnt];
+       hc->io_mode = iomode[HFC_cnt];
+       if (dslot[HFC_cnt] < 0) {
+               hc->dslot = 0;
+               printk(KERN_INFO "HFC-E1 card has disabled D-channel, but "
+                       "31 B-channels\n");
+       } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) {
+               hc->dslot = dslot[HFC_cnt];
+               printk(KERN_INFO "HFC-E1 card has alternating D-channel on "
+                       "time slot %d\n", dslot[HFC_cnt]);
+       } else
+               hc->dslot = 16;
+
+       /* set chip specific features */
+       hc->masterclk = -1;
+       if (type[HFC_cnt] & 0x100) {
+               test_and_set_bit(HFC_CHIP_ULAW, &hc->chip);
+               silence = 0xff; /* ulaw silence */
+       } else
+               silence = 0x2a; /* alaw silence */
+       if (!(type[HFC_cnt] & 0x200))
+               test_and_set_bit(HFC_CHIP_DTMF, &hc->chip);
+
+       if (type[HFC_cnt] & 0x800)
+               test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+       if (type[HFC_cnt] & 0x1000) {
+               test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+               test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+       }
+       if (type[HFC_cnt] & 0x4000)
+               test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip);
+       if (type[HFC_cnt] & 0x8000)
+               test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip);
+       hc->slots = 32;
+       if (type[HFC_cnt] & 0x10000)
+               hc->slots = 64;
+       if (type[HFC_cnt] & 0x20000)
+               hc->slots = 128;
+       if (type[HFC_cnt] & 0x80000) {
+               test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip);
+               hc->wdcount = 0;
+               hc->wdbyte = V_GPIO_OUT2;
+               printk(KERN_NOTICE "Watchdog enabled\n");
+       }
+
+       /* setup pci, hc->slots may change due to PLXSD */
+       ret_err = setup_pci(hc, pdev, ent);
+       if (ret_err) {
+               if (hc == syncmaster)
+                       syncmaster = NULL;
+               kfree(hc);
+               return ret_err;
+       }
+
+       /* crate channels */
+       for (pt = 0; pt < hc->ports; pt++) {
+               if (Port_cnt >= MAX_PORTS) {
+                       printk(KERN_ERR "too many ports (max=%d).\n",
+                               MAX_PORTS);
+                       ret_err = -EINVAL;
+                       goto free_card;
+               }
+               if (hc->type == 1)
+                       ret_err = init_e1_port(hc, m);
+               else
+                       ret_err = init_multi_port(hc, pt);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: Registering D-channel, card(%d) port(%d)"
+                           "result %d\n",
+                           __func__, HFC_cnt + 1, pt, ret_err);
+
+               if (ret_err) {
+                       while (pt) { /* release already registered ports */
+                               pt--;
+                               release_port(hc, hc->chan[(pt << 2) + 2].dch);
+                       }
+                       goto free_card;
+               }
+               Port_cnt++;
+       }
+
+       /* disp switches */
+       switch (m->dip_type) {
+       case DIP_4S:
+               /*
+                * get DIP Setting for beroNet 1S/2S/4S cards
+                *  check if Port Jumper config matches
+                * module param 'protocol'
+                * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) +
+                * GPI 19/23 (R_GPI_IN2))
+                */
+               dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) |
+                       ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) |
+                       (~HFC_inb(hc, R_GPI_IN2) & 0x08);
+
+               /* Port mode (TE/NT) jumpers */
+               pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4)  & 0xf);
+
+               if (test_bit(HFC_CHIP_B410P, &hc->chip))
+                       pmj = ~pmj & 0xf;
+
+               printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n",
+                       m->vendor_name, m->card_name, dips, pmj);
+               break;
+       case DIP_8S:
+               /*
+                * get DIP Setting for beroNet 8S0+ cards
+                *
+                * enable PCI auxbridge function
+                */
+               HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+               /* prepare access to auxport */
+               outw(0x4000, hc->pci_iobase + 4);
+               /*
+                * some dummy reads are required to
+                * read valid DIP switch data
+                */
+               dips = inb(hc->pci_iobase);
+               dips = inb(hc->pci_iobase);
+               dips = inb(hc->pci_iobase);
+               dips = ~inb(hc->pci_iobase) & 0x3F;
+               outw(0x0, hc->pci_iobase + 4);
+               /* disable PCI auxbridge function */
+               HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+               printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+                   m->vendor_name, m->card_name, dips);
+               break;
+       case DIP_E1:
+               /*
+                * get DIP Setting for beroNet E1 cards
+                * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0)
+                */
+               dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4;
+               printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+                   m->vendor_name, m->card_name, dips);
+               break;
+       }
+
+       /* add to list */
+       spin_lock_irqsave(&HFClock, flags);
+       list_add_tail(&hc->list, &HFClist);
+       spin_unlock_irqrestore(&HFClock, flags);
+
+       /* initialize hardware */
+       ret_err = init_card(hc);
+       if (ret_err) {
+               printk(KERN_ERR "init card returns %d\n", ret_err);
+               release_card(hc);
+               return ret_err;
+       }
+
+       /* start IRQ and return */
+       spin_lock_irqsave(&hc->lock, flags);
+       enable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       return 0;
+
+free_card:
+       release_io_hfcmulti(hc);
+       if (hc == syncmaster)
+               syncmaster = NULL;
+       kfree(hc);
+       return ret_err;
+}
+
+static void __devexit hfc_remove_pci(struct pci_dev *pdev)
+{
+       struct hfc_multi        *card = pci_get_drvdata(pdev);
+       u_long                  flags;
+
+       if (debug)
+               printk(KERN_INFO "removing hfc_multi card vendor:%x "
+                   "device:%x subvendor:%x subdevice:%x\n",
+                   pdev->vendor, pdev->device,
+                   pdev->subsystem_vendor, pdev->subsystem_device);
+
+       if (card) {
+               spin_lock_irqsave(&HFClock, flags);
+               release_card(card);
+               spin_unlock_irqrestore(&HFClock, flags);
+       }  else {
+               if (debug)
+                       printk(KERN_WARNING "%s: drvdata allready removed\n",
+                           __func__);
+       }
+}
+
+#define        VENDOR_CCD      "Cologne Chip AG"
+#define        VENDOR_BN       "beroNet GmbH"
+#define        VENDOR_DIG      "Digium Inc."
+#define VENDOR_JH      "Junghanns.NET GmbH"
+#define VENDOR_PRIM    "PrimuX"
+
+static const struct hm_map hfcm_map[] = {
+/*0*/  {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0},
+/*1*/  {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S},
+/*2*/  {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0},
+/*3*/  {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0},
+/*4*/  {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0},
+/*5*/  {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0},
+/*6*/  {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, 0, 0},
+/*7*/  {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0},
+/*8*/  {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO},
+/*9*/  {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0},
+/*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0},
+/*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0},
+
+/*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0},
+/*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S,
+               HFC_IO_MODE_REGIO},
+/*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0},
+/*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0},
+
+/*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0},
+/*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+/*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+
+/*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0},
+/*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+
+/*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0},
+/*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0},
+/*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0},
+
+/*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0,
+               HFC_IO_MODE_PLXSD},
+/*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0,
+               HFC_IO_MODE_PLXSD},
+/*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0},
+/*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0},
+/*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0},
+};
+
+#undef H
+#define H(x)   ((unsigned long)&hfcm_map[x])
+static struct pci_device_id hfmultipci_ids[] __devinitdata = {
+
+       /* Cards with HFC-4S Chip */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */
+       { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S,
+               PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */
+
+       /* Cards with HFC-8S Chip */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)},
+           /* IOB8ST Recording */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST  */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST  */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */
+
+
+       /* Cards with HFC-E1 Chip */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */
+
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */
+
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0},
+       {0, }
+};
+#undef H
+
+MODULE_DEVICE_TABLE(pci, hfmultipci_ids);
+
+static int
+hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct hm_map   *m = (struct hm_map *)ent->driver_data;
+       int             ret;
+
+       if (m == NULL) {
+               if (ent->vendor == PCI_VENDOR_ID_CCD)
+                       if (ent->device == PCI_DEVICE_ID_CCD_HFC4S ||
+                           ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
+                           ent->device == PCI_DEVICE_ID_CCD_HFCE1)
+                               printk(KERN_ERR
+                                   "unknown HFC multiport controller "
+                                   "(vendor:%x device:%x subvendor:%x "
+                                   "subdevice:%x) Please contact the "
+                                   "driver maintainer for support.\n",
+                                   ent->vendor, ent->device,
+                                   ent->subvendor, ent->subdevice);
+               return -ENODEV;
+       }
+       ret = hfcmulti_init(pdev, ent);
+       if (ret)
+               return ret;
+       HFC_cnt++;
+       printk(KERN_INFO "%d devices registered\n", HFC_cnt);
+       return 0;
+}
+
+static struct pci_driver hfcmultipci_driver = {
+       .name           = "hfc_multi",
+       .probe          = hfcmulti_probe,
+       .remove         = __devexit_p(hfc_remove_pci),
+       .id_table       = hfmultipci_ids,
+};
+
+static void __exit
+HFCmulti_cleanup(void)
+{
+       struct hfc_multi *card, *next;
+
+       /* unload interrupt function symbol */
+       if (hfc_interrupt)
+               symbol_put(ztdummy_extern_interrupt);
+       if (register_interrupt)
+               symbol_put(ztdummy_register_interrupt);
+       if (unregister_interrupt) {
+               if (interrupt_registered) {
+                       interrupt_registered = 0;
+                       unregister_interrupt();
+               }
+               symbol_put(ztdummy_unregister_interrupt);
+       }
+
+       list_for_each_entry_safe(card, next, &HFClist, list)
+               release_card(card);
+       /* get rid of all devices of this driver */
+       pci_unregister_driver(&hfcmultipci_driver);
+}
+
+static int __init
+HFCmulti_init(void)
+{
+       int err;
+
+#ifdef IRQ_DEBUG
+       printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__);
+#endif
+
+       spin_lock_init(&HFClock);
+       spin_lock_init(&plx_lock);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: init entered\n", __func__);
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+       hfc_interrupt = symbol_get(ztdummy_extern_interrupt);
+       register_interrupt = symbol_get(ztdummy_register_interrupt);
+       unregister_interrupt = symbol_get(ztdummy_unregister_interrupt);
+       printk(KERN_INFO "mISDN: HFC-multi driver %s\n",
+           hfcmulti_revision);
+
+       switch (poll) {
+       case 0:
+               poll_timer = 6;
+               poll = 128;
+               break;
+               /*
+                * wenn dieses break nochmal verschwindet,
+                * gibt es heisse ohren :-)
+                * "without the break you will get hot ears ???"
+                */
+       case 8:
+               poll_timer = 2;
+               break;
+       case 16:
+               poll_timer = 3;
+               break;
+       case 32:
+               poll_timer = 4;
+               break;
+       case 64:
+               poll_timer = 5;
+               break;
+       case 128:
+               poll_timer = 6;
+               break;
+       case 256:
+               poll_timer = 7;
+               break;
+       default:
+               printk(KERN_ERR
+                   "%s: Wrong poll value (%d).\n", __func__, poll);
+               err = -EINVAL;
+               return err;
+
+       }
+
+       err = pci_register_driver(&hfcmultipci_driver);
+       if (err < 0) {
+               printk(KERN_ERR "error registering pci driver: %x\n", err);
+               if (hfc_interrupt)
+                       symbol_put(ztdummy_extern_interrupt);
+               if (register_interrupt)
+                       symbol_put(ztdummy_register_interrupt);
+               if (unregister_interrupt) {
+                       if (interrupt_registered) {
+                               interrupt_registered = 0;
+                               unregister_interrupt();
+                       }
+                       symbol_put(ztdummy_unregister_interrupt);
+               }
+               return err;
+       }
+       return 0;
+}
+
+
+module_init(HFCmulti_init);
+module_exit(HFCmulti_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
new file mode 100644 (file)
index 0000000..9179685
--- /dev/null
@@ -0,0 +1,2256 @@
+/*
+ *
+ * hfcpci.c     low level driver for CCD's hfc-pci based cards
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *            based on existing driver for CCD hfc ISA cards
+ *            type approval valid for HFC-S PCI A based card
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008  by Karsten Keil <kkeil@novell.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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+
+#include "hfc_pci.h"
+
+static const char *hfcpci_revision = "2.0";
+
+#define MAX_CARDS      8
+static int HFC_cnt;
+static uint debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, 0);
+
+static LIST_HEAD(HFClist);
+DEFINE_RWLOCK(HFClock);
+
+enum {
+       HFC_CCD_2BD0,
+       HFC_CCD_B000,
+       HFC_CCD_B006,
+       HFC_CCD_B007,
+       HFC_CCD_B008,
+       HFC_CCD_B009,
+       HFC_CCD_B00A,
+       HFC_CCD_B00B,
+       HFC_CCD_B00C,
+       HFC_CCD_B100,
+       HFC_CCD_B700,
+       HFC_CCD_B701,
+       HFC_ASUS_0675,
+       HFC_BERKOM_A1T,
+       HFC_BERKOM_TCONCEPT,
+       HFC_ANIGMA_MC145575,
+       HFC_ZOLTRIX_2BD0,
+       HFC_DIGI_DF_M_IOM2_E,
+       HFC_DIGI_DF_M_E,
+       HFC_DIGI_DF_M_IOM2_A,
+       HFC_DIGI_DF_M_A,
+       HFC_ABOCOM_2BD1,
+       HFC_SITECOM_DC105V2,
+};
+
+struct hfcPCI_hw {
+       unsigned char           cirm;
+       unsigned char           ctmt;
+       unsigned char           clkdel;
+       unsigned char           states;
+       unsigned char           conn;
+       unsigned char           mst_m;
+       unsigned char           int_m1;
+       unsigned char           int_m2;
+       unsigned char           sctrl;
+       unsigned char           sctrl_r;
+       unsigned char           sctrl_e;
+       unsigned char           trm;
+       unsigned char           fifo_en;
+       unsigned char           bswapped;
+       unsigned char           protocol;
+       int                     nt_timer;
+       unsigned char           *pci_io; /* start of PCI IO memory */
+       dma_addr_t              dmahandle;
+       void                    *fifos; /* FIFO memory */
+       int                     last_bfifo_cnt[2];
+           /* marker saving last b-fifo frame count */
+       struct timer_list       timer;
+};
+
+#define        HFC_CFG_MASTER          1
+#define HFC_CFG_SLAVE          2
+#define        HFC_CFG_PCM             3
+#define HFC_CFG_2HFC           4
+#define HFC_CFG_SLAVEHFC       5
+#define HFC_CFG_NEG_F0         6
+#define HFC_CFG_SW_DD_DU       7
+
+#define FLG_HFC_TIMER_T1       16
+#define FLG_HFC_TIMER_T3       17
+
+#define NT_T1_COUNT    1120    /* number of 3.125ms interrupts (3.5s) */
+#define NT_T3_COUNT    31      /* number of 3.125ms interrupts (97 ms) */
+#define CLKDEL_TE      0x0e    /* CLKDEL in TE mode */
+#define CLKDEL_NT      0x6c    /* CLKDEL in NT mode */
+
+
+struct hfc_pci {
+       struct list_head        list;
+       u_char                  subtype;
+       u_char                  chanlimit;
+       u_char                  initdone;
+       u_long                  cfg;
+       u_int                   irq;
+       u_int                   irqcnt;
+       struct pci_dev          *pdev;
+       struct hfcPCI_hw        hw;
+       spinlock_t              lock;   /* card lock */
+       struct dchannel         dch;
+       struct bchannel         bch[2];
+};
+
+/* Interface functions */
+static void
+enable_hwirq(struct hfc_pci *hc)
+{
+       hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE;
+       Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+static void
+disable_hwirq(struct hfc_pci *hc)
+{
+       hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE);
+       Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcpci(struct hfc_pci *hc)
+{
+       /* disable memory mapped ports + busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND, 0);
+       del_timer(&hc->hw.timer);
+       pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, hc->hw.dmahandle);
+       iounmap((void *)hc->hw.pci_io);
+}
+
+/*
+ * set mode (NT or TE)
+ */
+static void
+hfcpci_setmode(struct hfc_pci *hc)
+{
+       if (hc->hw.protocol == ISDN_P_NT_S0) {
+               hc->hw.clkdel = CLKDEL_NT;      /* ST-Bit delay for NT-Mode */
+               hc->hw.sctrl |= SCTRL_MODE_NT;  /* NT-MODE */
+               hc->hw.states = 1;              /* G1 */
+       } else {
+               hc->hw.clkdel = CLKDEL_TE;      /* ST-Bit delay for TE-Mode */
+               hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */
+               hc->hw.states = 2;              /* F2 */
+       }
+       Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
+       Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states);
+       udelay(10);
+       Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */
+       Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+}
+
+/*
+ * function called to reset the HFC PCI chip. A complete software reset of chip
+ * and fifos is done.
+ */
+static void
+reset_hfcpci(struct hfc_pci *hc)
+{
+       u_char  val;
+       int     cnt = 0;
+
+       printk(KERN_DEBUG "reset_hfcpci: entered\n");
+       val = Read_hfc(hc, HFCPCI_CHIP_ID);
+       printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val);
+       /* enable memory mapped ports, disable busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+       disable_hwirq(hc);
+       /* enable memory ports + busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND,
+           PCI_ENA_MEMIO + PCI_ENA_MASTER);
+       val = Read_hfc(hc, HFCPCI_STATUS);
+       printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val);
+       hc->hw.cirm = HFCPCI_RESET;     /* Reset On */
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       mdelay(10);                     /* Timeout 10ms */
+       hc->hw.cirm = 0;                /* Reset Off */
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+       val = Read_hfc(hc, HFCPCI_STATUS);
+       printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val);
+       while (cnt < 50000) { /* max 50000 us */
+               udelay(5);
+               cnt += 5;
+               val = Read_hfc(hc, HFCPCI_STATUS);
+               if (!(val & 2))
+                       break;
+       }
+       printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt);
+
+       hc->hw.fifo_en = 0x30;  /* only D fifos enabled */
+
+       hc->hw.bswapped = 0;    /* no exchange */
+       hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+       hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */
+       hc->hw.sctrl = 0x40;    /* set tx_lo mode, error in datasheet ! */
+       hc->hw.sctrl_r = 0;
+       hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE;     /* S/T Auto awake */
+       hc->hw.mst_m = 0;
+       if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+               hc->hw.mst_m |= HFCPCI_MASTER;  /* HFC Master Mode */
+       if (test_bit(HFC_CFG_NEG_F0, &hc->cfg))
+               hc->hw.mst_m |= HFCPCI_F0_NEGATIV;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+       Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
+       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+
+       hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+           HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+
+       /* Clear already pending ints */
+       if (Read_hfc(hc, HFCPCI_INT_S1));
+
+       /* set NT/TE mode */
+       hfcpci_setmode(hc);
+
+       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+       Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+
+       /*
+        * Init GCI/IOM2 in master mode
+        * Slots 0 and 1 are set for B-chan 1 and 2
+        * D- and monitor/CI channel are not enabled
+        * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC
+        * STIO2 is used as data input, B1+B2 from IOM->ST
+        * ST B-channel send disabled -> continous 1s
+        * The IOM slots are always enabled
+        */
+       if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+               /* set data flow directions: connect B1,B2: HFC to/from PCM */
+               hc->hw.conn = 0x09;
+       } else {
+               hc->hw.conn = 0x36;     /* set data flow directions */
+               if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
+                       Write_hfc(hc, HFCPCI_B1_SSL, 0xC0);
+                       Write_hfc(hc, HFCPCI_B2_SSL, 0xC1);
+                       Write_hfc(hc, HFCPCI_B1_RSL, 0xC0);
+                       Write_hfc(hc, HFCPCI_B2_RSL, 0xC1);
+               } else {
+                       Write_hfc(hc, HFCPCI_B1_SSL, 0x80);
+                       Write_hfc(hc, HFCPCI_B2_SSL, 0x81);
+                       Write_hfc(hc, HFCPCI_B1_RSL, 0x80);
+                       Write_hfc(hc, HFCPCI_B2_RSL, 0x81);
+               }
+       }
+       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+       val = Read_hfc(hc, HFCPCI_INT_S2);
+}
+
+/*
+ * Timer function called when kernel timer expires
+ */
+static void
+hfcpci_Timer(struct hfc_pci *hc)
+{
+       hc->hw.timer.expires = jiffies + 75;
+       /* WD RESET */
+/*
+ *     WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80);
+ *     add_timer(&hc->hw.timer);
+ */
+}
+
+
+/*
+ * select a b-channel entry matching and active
+ */
+static struct bchannel *
+Sel_BCS(struct hfc_pci *hc, int channel)
+{
+       if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) &&
+               (hc->bch[0].nr & channel))
+               return &hc->bch[0];
+       else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) &&
+               (hc->bch[1].nr & channel))
+               return &hc->bch[1];
+       else
+               return NULL;
+}
+
+/*
+ * clear the desired B-channel rx fifo
+ */
+static void
+hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo)
+{
+       u_char          fifo_state;
+       struct bzfifo   *bzr;
+
+       if (fifo) {
+               bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX;
+       } else {
+               bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX;
+       }
+       if (fifo_state)
+               hc->hw.fifo_en ^= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       hc->hw.last_bfifo_cnt[fifo] = 0;
+       bzr->f1 = MAX_B_FRAMES;
+       bzr->f2 = bzr->f1;      /* init F pointers to remain constant */
+       bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+       bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+           le16_to_cpu(bzr->za[MAX_B_FRAMES].z1));
+       if (fifo_state)
+               hc->hw.fifo_en |= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+}
+
+/*
+ * clear the desired B-channel tx fifo
+ */
+static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo)
+{
+       u_char          fifo_state;
+       struct bzfifo   *bzt;
+
+       if (fifo) {
+               bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX;
+       } else {
+               bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX;
+       }
+       if (fifo_state)
+               hc->hw.fifo_en ^= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) "
+                   "z1(%x) z2(%x) state(%x)\n",
+                   fifo, bzt->f1, bzt->f2,
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z2),
+                   fifo_state);
+       bzt->f2 = MAX_B_FRAMES;
+       bzt->f1 = bzt->f2;      /* init F pointers to remain constant */
+       bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+       bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+           le16_to_cpu(bzt->za[MAX_B_FRAMES].z1 - 1));
+       if (fifo_state)
+               hc->hw.fifo_en |= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n",
+                   fifo, bzt->f1, bzt->f2,
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z2));
+}
+
+/*
+ * read a complete B-frame out of the buffer
+ */
+static void
+hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz,
+    u_char *bdata, int count)
+{
+       u_char          *ptr, *ptr1, new_f2;
+       int             total, maxlen, new_z2;
+       struct zt       *zp;
+
+       if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+               printk(KERN_DEBUG "hfcpci_empty_fifo\n");
+       zp = &bz->za[bz->f2];   /* point to Z-Regs */
+       new_z2 = le16_to_cpu(zp->z2) + count;   /* new position in fifo */
+       if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+               new_z2 -= B_FIFO_SIZE;  /* buffer wrap */
+       new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
+       if ((count > MAX_DATA_SIZE + 3) || (count < 4) ||
+           (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) {
+               if (bch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet "
+                           "invalid length %d or crc\n", count);
+#ifdef ERROR_STATISTIC
+               bch->err_inv++;
+#endif
+               bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+               bz->f2 = new_f2;        /* next buffer */
+       } else {
+               bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC);
+               if (!bch->rx_skb) {
+                       printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+                       return;
+               }
+               total = count;
+               count -= 3;
+               ptr = skb_put(bch->rx_skb, count);
+
+               if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL)
+                       maxlen = count;         /* complete transfer */
+               else
+                       maxlen = B_FIFO_SIZE + B_SUB_VAL -
+                           le16_to_cpu(zp->z2);        /* maximum */
+
+               ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL);
+                   /* start of data */
+               memcpy(ptr, ptr1, maxlen);      /* copy data */
+               count -= maxlen;
+
+               if (count) {    /* rest remaining */
+                       ptr += maxlen;
+                       ptr1 = bdata;   /* start of buffer */
+                       memcpy(ptr, ptr1, count);       /* rest */
+               }
+               bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+               bz->f2 = new_f2;        /* next buffer */
+               recv_Bchannel(bch);
+       }
+}
+
+/*
+ * D-channel receive procedure
+ */
+static int
+receive_dmsg(struct hfc_pci *hc)
+{
+       struct dchannel *dch = &hc->dch;
+       int             maxlen;
+       int             rcnt, total;
+       int             count = 5;
+       u_char          *ptr, *ptr1;
+       struct dfifo    *df;
+       struct zt       *zp;
+
+       df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx;
+       while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) {
+               zp = &df->za[df->f2 & D_FREG_MASK];
+               rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+               if (rcnt < 0)
+                       rcnt += D_FIFO_SIZE;
+               rcnt++;
+               if (dch->debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n",
+                               df->f1, df->f2,
+                               le16_to_cpu(zp->z1),
+                               le16_to_cpu(zp->z2),
+                               rcnt);
+
+               if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
+                   (df->data[le16_to_cpu(zp->z1)])) {
+                       if (dch->debug & DEBUG_HW)
+                               printk(KERN_DEBUG
+                                   "empty_fifo hfcpci paket inv. len "
+                                   "%d or crc %d\n",
+                                   rcnt,
+                                   df->data[le16_to_cpu(zp->z1)]);
+#ifdef ERROR_STATISTIC
+                       cs->err_rx++;
+#endif
+                       df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) |
+                           (MAX_D_FRAMES + 1); /* next buffer */
+                       df->za[df->f2 & D_FREG_MASK].z2 =
+                           cpu_to_le16((zp->z2 + rcnt) & (D_FIFO_SIZE - 1));
+               } else {
+                       dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC);
+                       if (!dch->rx_skb) {
+                               printk(KERN_WARNING
+                                   "HFC-PCI: D receive out of memory\n");
+                               break;
+                       }
+                       total = rcnt;
+                       rcnt -= 3;
+                       ptr = skb_put(dch->rx_skb, rcnt);
+
+                       if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE)
+                               maxlen = rcnt;  /* complete transfer */
+                       else
+                               maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2);
+                                   /* maximum */
+
+                       ptr1 = df->data + le16_to_cpu(zp->z2);
+                           /* start of data */
+                       memcpy(ptr, ptr1, maxlen);      /* copy data */
+                       rcnt -= maxlen;
+
+                       if (rcnt) {     /* rest remaining */
+                               ptr += maxlen;
+                               ptr1 = df->data;        /* start of buffer */
+                               memcpy(ptr, ptr1, rcnt);        /* rest */
+                       }
+                       df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) |
+                           (MAX_D_FRAMES + 1); /* next buffer */
+                       df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((
+                           le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1));
+                       recv_Dchannel(dch);
+               }
+       }
+       return 1;
+}
+
+/*
+ * check for transparent receive data and read max one threshold size if avail
+ */
+int
+hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
+{
+       unsigned short  *z1r, *z2r;
+       int             new_z2, fcnt, maxlen;
+       u_char          *ptr, *ptr1;
+
+       z1r = &bz->za[MAX_B_FRAMES].z1;         /* pointer to z reg */
+       z2r = z1r + 1;
+
+       fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r);
+       if (!fcnt)
+               return 0;       /* no data avail */
+
+       if (fcnt <= 0)
+               fcnt += B_FIFO_SIZE;    /* bytes actually buffered */
+       if (fcnt > HFCPCI_BTRANS_THRESHOLD)
+               fcnt = HFCPCI_BTRANS_THRESHOLD;         /* limit size */
+
+       new_z2 = le16_to_cpu(*z2r) + fcnt;      /* new position in fifo */
+       if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+               new_z2 -= B_FIFO_SIZE;  /* buffer wrap */
+
+       bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC);
+       if (bch->rx_skb) {
+               ptr = skb_put(bch->rx_skb, fcnt);
+               if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
+                       maxlen = fcnt;  /* complete transfer */
+               else
+                       maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r);
+                           /* maximum */
+
+               ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL);
+                   /* start of data */
+               memcpy(ptr, ptr1, maxlen);      /* copy data */
+               fcnt -= maxlen;
+
+               if (fcnt) {     /* rest remaining */
+                       ptr += maxlen;
+                       ptr1 = bdata;   /* start of buffer */
+                       memcpy(ptr, ptr1, fcnt);        /* rest */
+               }
+               recv_Bchannel(bch);
+       } else
+               printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+
+       *z2r = cpu_to_le16(new_z2);             /* new position */
+       return 1;
+}
+
+/*
+ * B-channel main receive routine
+ */
+void
+main_rec_hfcpci(struct bchannel *bch)
+{
+       struct hfc_pci  *hc = bch->hw;
+       int             rcnt, real_fifo;
+       int             receive, count = 5;
+       struct bzfifo   *bz;
+       u_char          *bdata;
+       struct zt       *zp;
+
+
+       if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2;
+               real_fifo = 1;
+       } else {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1;
+               real_fifo = 0;
+       }
+Begin:
+       count--;
+       if (bz->f1 != bz->f2) {
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n",
+                           bch->nr, bz->f1, bz->f2);
+               zp = &bz->za[bz->f2];
+
+               rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+               if (rcnt < 0)
+                       rcnt += B_FIFO_SIZE;
+               rcnt++;
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n",
+                           bch->nr, le16_to_cpu(zp->z1),
+                           le16_to_cpu(zp->z2), rcnt);
+               hfcpci_empty_bfifo(bch, bz, bdata, rcnt);
+               rcnt = bz->f1 - bz->f2;
+               if (rcnt < 0)
+                       rcnt += MAX_B_FRAMES + 1;
+               if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) {
+                       rcnt = 0;
+                       hfcpci_clear_fifo_rx(hc, real_fifo);
+               }
+               hc->hw.last_bfifo_cnt[real_fifo] = rcnt;
+               if (rcnt > 1)
+                       receive = 1;
+               else
+                       receive = 0;
+       } else if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+               receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
+       else
+               receive = 0;
+       if (count && receive)
+               goto Begin;
+
+}
+
+/*
+ * D-channel send routine
+ */
+static void
+hfcpci_fill_dfifo(struct hfc_pci *hc)
+{
+       struct dchannel *dch = &hc->dch;
+       int             fcnt;
+       int             count, new_z1, maxlen;
+       struct dfifo    *df;
+       u_char          *src, *dst, new_f1;
+
+       if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO))
+               printk(KERN_DEBUG "%s\n", __func__);
+
+       if (!dch->tx_skb)
+               return;
+       count = dch->tx_skb->len - dch->tx_idx;
+       if (count <= 0)
+               return;
+       df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx;
+
+       if (dch->debug & DEBUG_HW_DFIFO)
+               printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__,
+                   df->f1, df->f2,
+                   le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1));
+       fcnt = df->f1 - df->f2; /* frame count actually buffered */
+       if (fcnt < 0)
+               fcnt += (MAX_D_FRAMES + 1);     /* if wrap around */
+       if (fcnt > (MAX_D_FRAMES - 1)) {
+               if (dch->debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci_fill_Dfifo more as 14 frames\n");
+#ifdef ERROR_STATISTIC
+               cs->err_tx++;
+#endif
+               return;
+       }
+       /* now determine free bytes in FIFO buffer */
+       maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) -
+           le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1;
+       if (maxlen <= 0)
+               maxlen += D_FIFO_SIZE;  /* count now contains available bytes */
+
+       if (dch->debug & DEBUG_HW_DCHANNEL)
+               printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n",
+                       count, maxlen);
+       if (count > maxlen) {
+               if (dch->debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n");
+               return;
+       }
+       new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) &
+           (D_FIFO_SIZE - 1);
+       new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1);
+       src = dch->tx_skb->data + dch->tx_idx;  /* source pointer */
+       dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+       maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+           /* end fifo */
+       if (maxlen > count)
+               maxlen = count; /* limit size */
+       memcpy(dst, src, maxlen);       /* first copy */
+
+       count -= maxlen;        /* remaining bytes */
+       if (count) {
+               dst = df->data; /* start of buffer */
+               src += maxlen;  /* new position */
+               memcpy(dst, src, count);
+       }
+       df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+           /* for next buffer */
+       df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+           /* new pos actual buffer */
+       df->f1 = new_f1;        /* next frame */
+       dch->tx_idx = dch->tx_skb->len;
+}
+
+/*
+ * B-channel send routine
+ */
+static void
+hfcpci_fill_fifo(struct bchannel *bch)
+{
+       struct hfc_pci  *hc = bch->hw;
+       int             maxlen, fcnt;
+       int             count, new_z1;
+       struct bzfifo   *bz;
+       u_char          *bdata;
+       u_char          new_f1, *src, *dst;
+       unsigned short  *z1t, *z2t;
+
+       if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+               printk(KERN_DEBUG "%s\n", __func__);
+       if ((!bch->tx_skb) || bch->tx_skb->len <= 0)
+               return;
+       count = bch->tx_skb->len - bch->tx_idx;
+       if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2;
+       } else {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1;
+       }
+
+       if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+               z1t = &bz->za[MAX_B_FRAMES].z1;
+               z2t = z1t + 1;
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) "
+                           "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count,
+                           le16_to_cpu(*z1t), le16_to_cpu(*z2t));
+               fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t);
+               if (fcnt <= 0)
+                       fcnt += B_FIFO_SIZE;
+                           /* fcnt contains available bytes in fifo */
+               fcnt = B_FIFO_SIZE - fcnt;
+                   /* remaining bytes to send (bytes in fifo) */
+next_t_frame:
+               count = bch->tx_skb->len - bch->tx_idx;
+               /* maximum fill shall be HFCPCI_BTRANS_MAX */
+               if (count > HFCPCI_BTRANS_MAX - fcnt)
+                       count = HFCPCI_BTRANS_MAX - fcnt;
+               if (count <= 0)
+                       return;
+               /* data is suitable for fifo */
+               new_z1 = le16_to_cpu(*z1t) + count;
+                   /* new buffer Position */
+               if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+                       new_z1 -= B_FIFO_SIZE;  /* buffer wrap */
+               src = bch->tx_skb->data + bch->tx_idx;
+                   /* source pointer */
+               dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL);
+               maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t);
+                   /* end of fifo */
+               if (bch->debug & DEBUG_HW_BFIFO)
+                       printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) "
+                           "maxl(%d) nz1(%x) dst(%p)\n",
+                           fcnt, maxlen, new_z1, dst);
+               fcnt += count;
+               bch->tx_idx += count;
+               if (maxlen > count)
+                       maxlen = count;         /* limit size */
+               memcpy(dst, src, maxlen);       /* first copy */
+               count -= maxlen;        /* remaining bytes */
+               if (count) {
+                       dst = bdata;    /* start of buffer */
+                       src += maxlen;  /* new position */
+                       memcpy(dst, src, count);
+               }
+               *z1t = cpu_to_le16(new_z1);     /* now send data */
+               if (bch->tx_idx < bch->tx_skb->len)
+                       return;
+               /* send confirm, on trans, free on hdlc. */
+               if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+                       confirm_Bsend(bch);
+               dev_kfree_skb(bch->tx_skb);
+               if (get_next_bframe(bch))
+                       goto next_t_frame;
+               return;
+       }
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n",
+                   __func__, bch->nr, bz->f1, bz->f2,
+                   bz->za[bz->f1].z1);
+       fcnt = bz->f1 - bz->f2; /* frame count actually buffered */
+       if (fcnt < 0)
+               fcnt += (MAX_B_FRAMES + 1);     /* if wrap around */
+       if (fcnt > (MAX_B_FRAMES - 1)) {
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci_fill_Bfifo more as 14 frames\n");
+               return;
+       }
+       /* now determine free bytes in FIFO buffer */
+       maxlen = le16_to_cpu(bz->za[bz->f2].z2) -
+           le16_to_cpu(bz->za[bz->f1].z1) - 1;
+       if (maxlen <= 0)
+               maxlen += B_FIFO_SIZE;  /* count now contains available bytes */
+
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n",
+                       bch->nr, count, maxlen);
+
+       if (maxlen < count) {
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n");
+               return;
+       }
+       new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count;
+           /* new buffer Position */
+       if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+               new_z1 -= B_FIFO_SIZE;  /* buffer wrap */
+
+       new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES);
+       src = bch->tx_skb->data + bch->tx_idx;  /* source pointer */
+       dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL);
+       maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1);
+           /* end fifo */
+       if (maxlen > count)
+               maxlen = count; /* limit size */
+       memcpy(dst, src, maxlen);       /* first copy */
+
+       count -= maxlen;        /* remaining bytes */
+       if (count) {
+               dst = bdata;    /* start of buffer */
+               src += maxlen;  /* new position */
+               memcpy(dst, src, count);
+       }
+       bz->za[new_f1].z1 = cpu_to_le16(new_z1);        /* for next buffer */
+       bz->f1 = new_f1;        /* next frame */
+       dev_kfree_skb(bch->tx_skb);
+       get_next_bframe(bch);
+}
+
+
+
+/*
+ * handle L1 state changes TE
+ */
+
+static void
+ph_state_te(struct dchannel *dch)
+{
+       if (dch->debug)
+               printk(KERN_DEBUG "%s: TE newstate %x\n",
+                       __func__, dch->state);
+       switch (dch->state) {
+       case 0:
+               l1_event(dch->l1, HW_RESET_IND);
+               break;
+       case 3:
+               l1_event(dch->l1, HW_DEACT_IND);
+               break;
+       case 5:
+       case 8:
+               l1_event(dch->l1, ANYSIGNAL);
+               break;
+       case 6:
+               l1_event(dch->l1, INFO2);
+               break;
+       case 7:
+               l1_event(dch->l1, INFO4_P8);
+               break;
+       }
+}
+
+/*
+ * handle L1 state changes NT
+ */
+
+static void
+handle_nt_timer3(struct dchannel *dch) {
+       struct hfc_pci  *hc = dch->hw;
+
+       test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+       hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       hc->hw.nt_timer = 0;
+       test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+       if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+               hc->hw.mst_m |= HFCPCI_MASTER;
+       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+       _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+           MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+}
+
+static void
+ph_state_nt(struct dchannel *dch)
+{
+       struct hfc_pci  *hc = dch->hw;
+
+       if (dch->debug)
+               printk(KERN_DEBUG "%s: NT newstate %x\n",
+                       __func__, dch->state);
+       switch (dch->state) {
+       case 2:
+               if (hc->hw.nt_timer < 0) {
+                       hc->hw.nt_timer = 0;
+                       test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+                       test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+                       hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+                       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+                       /* Clear already pending ints */
+                       if (Read_hfc(hc, HFCPCI_INT_S1));
+                       Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+                       udelay(10);
+                       Write_hfc(hc, HFCPCI_STATES, 4);
+                       dch->state = 4;
+               } else if (hc->hw.nt_timer == 0) {
+                       hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+                       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+                       hc->hw.nt_timer = NT_T1_COUNT;
+                       hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+                       hc->hw.ctmt |= HFCPCI_TIM3_125;
+                       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+                               HFCPCI_CLTIMER);
+                       test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+                       test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+                       /* allow G2 -> G3 transition */
+                       Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+               } else {
+                       Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+               }
+               break;
+       case 1:
+               hc->hw.nt_timer = 0;
+               test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+               test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+               hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+               Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               hc->hw.mst_m &= ~HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+               break;
+       case 4:
+               hc->hw.nt_timer = 0;
+               test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+               test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+               hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+               Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+               break;
+       case 3:
+               if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) {
+                       if (!test_and_clear_bit(FLG_L2_ACTIVATED,
+                           &dch->Flags)) {
+                               handle_nt_timer3(dch);
+                               break;
+                       }
+                       test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+                       hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+                       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+                       hc->hw.nt_timer = NT_T3_COUNT;
+                       hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+                       hc->hw.ctmt |= HFCPCI_TIM3_125;
+                       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+                               HFCPCI_CLTIMER);
+               }
+               break;
+       }
+}
+
+static void
+ph_state(struct dchannel *dch)
+{
+       struct hfc_pci  *hc = dch->hw;
+
+       if (hc->hw.protocol == ISDN_P_NT_S0) {
+               if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) &&
+                   hc->hw.nt_timer < 0)
+                       handle_nt_timer3(dch);
+               else
+                       ph_state_nt(dch);
+       } else
+               ph_state_te(dch);
+}
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfc_l1callback(struct dchannel *dch, u_int cmd)
+{
+       struct hfc_pci          *hc = dch->hw;
+
+       switch (cmd) {
+       case INFO3_P8:
+       case INFO3_P10:
+               if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+                       hc->hw.mst_m |= HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               break;
+       case HW_RESET_REQ:
+               Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3);
+               /* HFC ST 3 */
+               udelay(6);
+               Write_hfc(hc, HFCPCI_STATES, 3);        /* HFC ST 2 */
+               if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+                       hc->hw.mst_m |= HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+                  HFCPCI_DO_ACTION);
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case HW_DEACT_REQ:
+               hc->hw.mst_m &= ~HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               break;
+       case HW_POWERUP_REQ:
+               Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Interrupt handler
+ */
+static inline void
+tx_birq(struct bchannel *bch)
+{
+       if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+               hfcpci_fill_fifo(bch);
+       else {
+               if (bch->tx_skb)
+                       dev_kfree_skb(bch->tx_skb);
+               if (get_next_bframe(bch))
+                       hfcpci_fill_fifo(bch);
+       }
+}
+
+static inline void
+tx_dirq(struct dchannel *dch)
+{
+       if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len)
+               hfcpci_fill_dfifo(dch->hw);
+       else {
+               if (dch->tx_skb)
+                       dev_kfree_skb(dch->tx_skb);
+               if (get_next_dframe(dch))
+                       hfcpci_fill_dfifo(dch->hw);
+       }
+}
+
+static irqreturn_t
+hfcpci_int(int intno, void *dev_id)
+{
+       struct hfc_pci  *hc = dev_id;
+       u_char          exval;
+       struct bchannel *bch;
+       u_char          val, stat;
+
+       spin_lock(&hc->lock);
+       if (!(hc->hw.int_m2 & 0x08)) {
+               spin_unlock(&hc->lock);
+               return IRQ_NONE; /* not initialised */
+       }
+       stat = Read_hfc(hc, HFCPCI_STATUS);
+       if (HFCPCI_ANYINT & stat) {
+               val = Read_hfc(hc, HFCPCI_INT_S1);
+               if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG
+                           "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val);
+       } else {
+               /* shared */
+               spin_unlock(&hc->lock);
+               return IRQ_NONE;
+       }
+       hc->irqcnt++;
+
+       if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+               printk(KERN_DEBUG "HFC-PCI irq %x\n", val);
+       val &= hc->hw.int_m1;
+       if (val & 0x40) {       /* state machine irq */
+               exval = Read_hfc(hc, HFCPCI_STATES) & 0xf;
+               if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG "ph_state chg %d->%d\n",
+                               hc->dch.state, exval);
+               hc->dch.state = exval;
+               schedule_event(&hc->dch, FLG_PHCHANGE);
+               val &= ~0x40;
+       }
+       if (val & 0x80) {       /* timer irq */
+               if (hc->hw.protocol == ISDN_P_NT_S0) {
+                       if ((--hc->hw.nt_timer) < 0)
+                               schedule_event(&hc->dch, FLG_PHCHANGE);
+               }
+               val &= ~0x80;
+               Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
+       }
+       if (val & 0x08) {
+               bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+               if (bch)
+                       main_rec_hfcpci(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n");
+       }
+       if (val & 0x10) {
+               bch = Sel_BCS(hc, 2);
+               if (bch)
+                       main_rec_hfcpci(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n");
+       }
+       if (val & 0x01) {
+               bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+               if (bch)
+                       tx_birq(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n");
+       }
+       if (val & 0x02) {
+               bch = Sel_BCS(hc, 2);
+               if (bch)
+                       tx_birq(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n");
+       }
+       if (val & 0x20)
+               receive_dmsg(hc);
+       if (val & 0x04) {       /* dframe transmitted */
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags))
+                       del_timer(&hc->dch.timer);
+               tx_dirq(&hc->dch);
+       }
+       spin_unlock(&hc->lock);
+       return IRQ_HANDLED;
+}
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+static void
+hfcpci_dbusy_timer(struct hfc_pci *hc)
+{
+}
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ */
+static int
+mode_hfcpci(struct bchannel *bch, int bc, int protocol)
+{
+       struct hfc_pci  *hc = bch->hw;
+       int             fifo2;
+       u_char          rx_slot = 0, tx_slot = 0, pcm_mode;
+
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n",
+                   bch->state, protocol, bch->nr, bc);
+
+       fifo2 = bc;
+       pcm_mode = (bc>>24) & 0xff;
+       if (pcm_mode) { /* PCM SLOT USE */
+               if (!test_bit(HFC_CFG_PCM, &hc->cfg))
+                       printk(KERN_WARNING
+                           "%s: pcm channel id without HFC_CFG_PCM\n",
+                           __func__);
+               rx_slot = (bc>>8) & 0xff;
+               tx_slot = (bc>>16) & 0xff;
+               bc = bc & 0xff;
+       } else if (test_bit(HFC_CFG_PCM, &hc->cfg) &&
+           (protocol > ISDN_P_NONE))
+               printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n",
+                   __func__);
+       if (hc->chanlimit > 1) {
+               hc->hw.bswapped = 0;    /* B1 and B2 normal mode */
+               hc->hw.sctrl_e &= ~0x80;
+       } else {
+               if (bc & 2) {
+                       if (protocol != ISDN_P_NONE) {
+                               hc->hw.bswapped = 1; /* B1 and B2 exchanged */
+                               hc->hw.sctrl_e |= 0x80;
+                       } else {
+                               hc->hw.bswapped = 0; /* B1 and B2 normal mode */
+                               hc->hw.sctrl_e &= ~0x80;
+                       }
+                       fifo2 = 1;
+               } else {
+                       hc->hw.bswapped = 0;    /* B1 and B2 normal mode */
+                       hc->hw.sctrl_e &= ~0x80;
+               }
+       }
+       switch (protocol) {
+       case (-1): /* used for init */
+               bch->state = -1;
+               bch->nr = bc;
+       case (ISDN_P_NONE):
+               if (bch->state == ISDN_P_NONE)
+                       return 0;
+               if (bc & 2) {
+                       hc->hw.sctrl &= ~SCTRL_B2_ENA;
+                       hc->hw.sctrl_r &= ~SCTRL_B2_ENA;
+               } else {
+                       hc->hw.sctrl &= ~SCTRL_B1_ENA;
+                       hc->hw.sctrl_r &= ~SCTRL_B1_ENA;
+               }
+               if (fifo2 & 2) {
+                       hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2;
+                       hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS +
+                               HFCPCI_INTS_B2REC);
+               } else {
+                       hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1;
+                       hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS +
+                               HFCPCI_INTS_B1REC);
+               }
+#ifdef REVERSE_BITORDER
+               if (bch->nr & 2)
+                       hc->hw.cirm &= 0x7f;
+               else
+                       hc->hw.cirm &= 0xbf;
+#endif
+               bch->state = ISDN_P_NONE;
+               bch->nr = bc;
+               test_and_clear_bit(FLG_HDLC, &bch->Flags);
+               test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+               break;
+       case (ISDN_P_B_RAW):
+               bch->state = protocol;
+               bch->nr = bc;
+               hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
+               hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
+               if (bc & 2) {
+                       hc->hw.sctrl |= SCTRL_B2_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x80;
+#endif
+               } else {
+                       hc->hw.sctrl |= SCTRL_B1_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x40;
+#endif
+               }
+               if (fifo2 & 2) {
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+                           HFCPCI_INTS_B2REC);
+                       hc->hw.ctmt |= 2;
+                       hc->hw.conn &= ~0x18;
+               } else {
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+                           HFCPCI_INTS_B1REC);
+                       hc->hw.ctmt |= 1;
+                       hc->hw.conn &= ~0x03;
+               }
+               test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+               break;
+       case (ISDN_P_B_HDLC):
+               bch->state = protocol;
+               bch->nr = bc;
+               hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
+               hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
+               if (bc & 2) {
+                       hc->hw.sctrl |= SCTRL_B2_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+               } else {
+                       hc->hw.sctrl |= SCTRL_B1_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+               }
+               if (fifo2 & 2) {
+                       hc->hw.last_bfifo_cnt[1] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+                           HFCPCI_INTS_B2REC);
+                       hc->hw.ctmt &= ~2;
+                       hc->hw.conn &= ~0x18;
+               } else {
+                       hc->hw.last_bfifo_cnt[0] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+                           HFCPCI_INTS_B1REC);
+                       hc->hw.ctmt &= ~1;
+                       hc->hw.conn &= ~0x03;
+               }
+               test_and_set_bit(FLG_HDLC, &bch->Flags);
+               break;
+       default:
+               printk(KERN_DEBUG "prot not known %x\n", protocol);
+               return -ENOPROTOOPT;
+       }
+       if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+               if ((protocol == ISDN_P_NONE) ||
+                       (protocol == -1)) {     /* init case */
+                       rx_slot = 0;
+                       tx_slot = 0;
+               } else {
+                       if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
+                               rx_slot |= 0xC0;
+                               tx_slot |= 0xC0;
+                       } else {
+                               rx_slot |= 0x80;
+                               tx_slot |= 0x80;
+                       }
+               }
+               if (bc & 2) {
+                       hc->hw.conn &= 0xc7;
+                       hc->hw.conn |= 0x08;
+                       printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n",
+                               __func__, tx_slot);
+                       printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n",
+                               __func__, rx_slot);
+                       Write_hfc(hc, HFCPCI_B2_SSL, tx_slot);
+                       Write_hfc(hc, HFCPCI_B2_RSL, rx_slot);
+               } else {
+                       hc->hw.conn &= 0xf8;
+                       hc->hw.conn |= 0x01;
+                       printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n",
+                               __func__, tx_slot);
+                       printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n",
+                               __func__, rx_slot);
+                       Write_hfc(hc, HFCPCI_B1_SSL, tx_slot);
+                       Write_hfc(hc, HFCPCI_B1_RSL, rx_slot);
+               }
+       }
+       Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+       Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+#ifdef REVERSE_BITORDER
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+#endif
+       return 0;
+}
+
+static int
+set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan)
+{
+       struct hfc_pci  *hc = bch->hw;
+
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n",
+                   bch->state, protocol, bch->nr, chan);
+       if (bch->nr != chan) {
+               printk(KERN_DEBUG
+                   "HFCPCI rxtest wrong channel parameter %x/%x\n",
+                   bch->nr, chan);
+               return -EINVAL;
+       }
+       switch (protocol) {
+       case (ISDN_P_B_RAW):
+               bch->state = protocol;
+               hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0);
+               if (chan & 2) {
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+                       hc->hw.ctmt |= 2;
+                       hc->hw.conn &= ~0x18;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x80;
+#endif
+               } else {
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+                       hc->hw.ctmt |= 1;
+                       hc->hw.conn &= ~0x03;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x40;
+#endif
+               }
+               break;
+       case (ISDN_P_B_HDLC):
+               bch->state = protocol;
+               hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0);
+               if (chan & 2) {
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+                       hc->hw.last_bfifo_cnt[1] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+                       hc->hw.ctmt &= ~2;
+                       hc->hw.conn &= ~0x18;
+               } else {
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+                       hc->hw.last_bfifo_cnt[0] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+                       hc->hw.ctmt &= ~1;
+                       hc->hw.conn &= ~0x03;
+               }
+               break;
+       default:
+               printk(KERN_DEBUG "prot not known %x\n", protocol);
+               return -ENOPROTOOPT;
+       }
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+#ifdef REVERSE_BITORDER
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+#endif
+       return 0;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+       struct hfc_pci  *hc = bch->hw;
+       u_long          flags;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+               dev_kfree_skb(bch->next_skb);
+               bch->next_skb = NULL;
+       }
+       if (bch->tx_skb) {
+               dev_kfree_skb(bch->tx_skb);
+               bch->tx_skb = NULL;
+       }
+       bch->tx_idx = 0;
+       if (bch->rx_skb) {
+               dev_kfree_skb(bch->rx_skb);
+               bch->rx_skb = NULL;
+       }
+       mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+       spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+/*
+ * Layer 1 B-channel hardware access
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int                     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+static int
+hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_pci  *hc = bch->hw;
+       int             ret = -EINVAL;
+       u_long          flags;
+
+       if (bch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg);
+       switch (cmd) {
+       case HW_TESTRX_RAW:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case HW_TESTRX_HDLC:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case HW_TESTRX_OFF:
+               spin_lock_irqsave(&hc->lock, flags);
+               mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               ret = 0;
+               break;
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags))
+                       deactivate_bchannel(bch);
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown prim(%x)\n",
+                       __func__, cmd);
+       }
+       return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Dchannel data
+ */
+static int
+hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_pci          *hc = dch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcpci_fill_dfifo(dch->hw);
+                       ret = 0;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->hw.protocol == ISDN_P_NT_S0) {
+                       ret = 0;
+                       if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+                               hc->hw.mst_m |= HFCPCI_MASTER;
+                       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+                       if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+                                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                               break;
+                       }
+                       test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags);
+                       Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+                           HFCPCI_DO_ACTION | 1);
+               } else
+                       ret = l1_event(dch->l1, hh->prim);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->hw.protocol == ISDN_P_NT_S0) {
+                       /* prepare deactivation */
+                       Write_hfc(hc, HFCPCI_STATES, 0x40);
+                       skb_queue_purge(&dch->squeue);
+                       if (dch->tx_skb) {
+                               dev_kfree_skb(dch->tx_skb);
+                               dch->tx_skb = NULL;
+                       }
+                       dch->tx_idx = 0;
+                       if (dch->rx_skb) {
+                               dev_kfree_skb(dch->rx_skb);
+                               dch->rx_skb = NULL;
+                       }
+                       test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+                       if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                               del_timer(&dch->timer);
+#ifdef FIXME
+                       if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+                               dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+                       hc->hw.mst_m &= ~HFCPCI_MASTER;
+                       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+                       ret = 0;
+               } else {
+                       ret = l1_event(dch->l1, hh->prim);
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Bchannel data
+ */
+static int
+hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_pci          *hc = bch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcpci_fill_fifo(bch);
+                       ret = 0;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = mode_hfcpci(bch, bch->nr, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(&hc->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               deactivate_bchannel(bch);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+/*
+ * called for card init message
+ */
+
+void
+inithfcpci(struct hfc_pci *hc)
+{
+       printk(KERN_DEBUG "inithfcpci: entered\n");
+       hc->dch.timer.function = (void *) hfcpci_dbusy_timer;
+       hc->dch.timer.data = (long) &hc->dch;
+       init_timer(&hc->dch.timer);
+       hc->chanlimit = 2;
+       mode_hfcpci(&hc->bch[0], 1, -1);
+       mode_hfcpci(&hc->bch[1], 2, -1);
+}
+
+
+static int
+init_card(struct hfc_pci *hc)
+{
+       int     cnt = 3;
+       u_long  flags;
+
+       printk(KERN_DEBUG "init_card: entered\n");
+
+
+       spin_lock_irqsave(&hc->lock, flags);
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) {
+               printk(KERN_WARNING
+                   "mISDN: couldn't get interrupt %d\n", hc->irq);
+               return -EIO;
+       }
+       spin_lock_irqsave(&hc->lock, flags);
+       reset_hfcpci(hc);
+       while (cnt) {
+               inithfcpci(hc);
+               /*
+                * Finally enable IRQ output
+                * this is only allowed, if an IRQ routine is allready
+                * established for this HFC, so don't do that earlier
+                */
+               enable_hwirq(hc);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               /* Timeout 80ms */
+               current->state = TASK_UNINTERRUPTIBLE;
+               schedule_timeout((80*HZ)/1000);
+               printk(KERN_INFO "HFC PCI: IRQ %d count %d\n",
+                       hc->irq, hc->irqcnt);
+               /* now switch timer interrupt off */
+               spin_lock_irqsave(&hc->lock, flags);
+               hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+               Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+               /* reinit mode reg */
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               if (!hc->irqcnt) {
+                       printk(KERN_WARNING
+                           "HFC PCI: IRQ(%d) getting no interrupts "
+                           "during init %d\n", hc->irq, 4 - cnt);
+                       if (cnt == 1) {
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               return -EIO;
+                       } else {
+                               reset_hfcpci(hc);
+                               cnt--;
+                       }
+               } else {
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       hc->initdone = 1;
+                       return 0;
+               }
+       }
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       free_irq(hc->irq, hc);
+       return -EIO;
+}
+
+static int
+channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+       u_char  slot;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT |
+                   MISDN_CTRL_DISCONNECT;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* channel 0 disabled loop */
+               if (cq->channel < 0 || cq->channel > 2) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (cq->channel & 1) {
+                       if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                               slot = 0xC0;
+                       else
+                               slot = 0x80;
+                       printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
+                           __func__, slot);
+                       Write_hfc(hc, HFCPCI_B1_SSL, slot);
+                       Write_hfc(hc, HFCPCI_B1_RSL, slot);
+                       hc->hw.conn = (hc->hw.conn & ~7) | 6;
+                       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               }
+               if (cq->channel & 2) {
+                       if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                               slot = 0xC1;
+                       else
+                               slot = 0x81;
+                       printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
+                           __func__, slot);
+                       Write_hfc(hc, HFCPCI_B2_SSL, slot);
+                       Write_hfc(hc, HFCPCI_B2_RSL, slot);
+                       hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30;
+                       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               }
+               if (cq->channel & 3)
+                       hc->hw.trm |= 0x80;     /* enable IOM-loop */
+               else {
+                       hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+                       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+                       hc->hw.trm &= 0x7f;     /* disable IOM-loop */
+               }
+               Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+               break;
+       case MISDN_CTRL_CONNECT:
+               if (cq->channel == cq->p1) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (cq->channel < 1 || cq->channel > 2 ||
+                   cq->p1 < 1 || cq->p1 > 2) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                       slot = 0xC0;
+               else
+                       slot = 0x80;
+               printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
+                   __func__, slot);
+               Write_hfc(hc, HFCPCI_B1_SSL, slot);
+               Write_hfc(hc, HFCPCI_B2_RSL, slot);
+               if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                       slot = 0xC1;
+               else
+                       slot = 0x81;
+               printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
+                   __func__, slot);
+               Write_hfc(hc, HFCPCI_B2_SSL, slot);
+               Write_hfc(hc, HFCPCI_B1_RSL, slot);
+               hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36;
+               Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               hc->hw.trm |= 0x80;
+               Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+               break;
+       case MISDN_CTRL_DISCONNECT:
+               hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+               Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               hc->hw.trm &= 0x7f;     /* disable IOM-loop */
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch,
+    struct channel_req *rq)
+{
+       int err = 0;
+
+       if (debug & DEBUG_HW_OPEN)
+               printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+                   hc->dch.dev.id, __builtin_return_address(0));
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if (!hc->initdone) {
+               if (rq->protocol == ISDN_P_TE_S0) {
+                       err = create_l1(&hc->dch, hfc_l1callback);
+                       if (err)
+                               return err;
+               }
+               hc->hw.protocol = rq->protocol;
+               ch->protocol = rq->protocol;
+               err = init_card(hc);
+               if (err)
+                       return err;
+       } else {
+               if (rq->protocol != ch->protocol) {
+                       if (hc->hw.protocol == ISDN_P_TE_S0)
+                               l1_event(hc->dch.l1, CLOSE_CHANNEL);
+                       hc->hw.protocol = rq->protocol;
+                       ch->protocol = rq->protocol;
+                       hfcpci_setmode(hc);
+               }
+       }
+
+       if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) ||
+           ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) {
+               _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       }
+       rq->ch = ch;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+open_bchannel(struct hfc_pci *hc, struct channel_req *rq)
+{
+       struct bchannel         *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &hc->bch[rq->adr.channel - 1];
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch; /* TODO: E-channel */
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_pci          *hc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       if (dch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->adr.channel == 0)
+                       err = open_dchannel(hc, ch, rq);
+               else
+                       err = open_bchannel(hc, rq);
+               break;
+       case CLOSE_CHANNEL:
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+                           __func__, hc->dch.dev.id,
+                           __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(hc, arg);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+static int
+setup_hw(struct hfc_pci *hc)
+{
+       void    *buffer;
+
+       printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision);
+       hc->hw.cirm = 0;
+       hc->dch.state = 0;
+       pci_set_master(hc->pdev);
+       if (!hc->irq) {
+               printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+               return 1;
+       }
+       hc->hw.pci_io = (char *)(ulong)hc->pdev->resource[1].start;
+
+       if (!hc->hw.pci_io) {
+               printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+               return 1;
+       }
+       /* Allocate memory for FIFOS */
+       /* the memory needs to be on a 32k boundary within the first 4G */
+       pci_set_dma_mask(hc->pdev, 0xFFFF8000);
+       buffer = pci_alloc_consistent(hc->pdev, 0x8000, &hc->hw.dmahandle);
+       /* We silently assume the address is okay if nonzero */
+       if (!buffer) {
+               printk(KERN_WARNING
+                   "HFC-PCI: Error allocating memory for FIFO!\n");
+               return 1;
+       }
+       hc->hw.fifos = buffer;
+       pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle);
+       hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
+       printk(KERN_INFO
+               "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n",
+               (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos,
+               (u_long) virt_to_bus(hc->hw.fifos),
+               hc->irq, HZ);
+       /* enable memory mapped ports, disable busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+       hc->hw.int_m2 = 0;
+       disable_hwirq(hc);
+       hc->hw.int_m1 = 0;
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       /* At this point the needed PCI config is done */
+       /* fifos are still not enabled */
+       hc->hw.timer.function = (void *) hfcpci_Timer;
+       hc->hw.timer.data = (long) hc;
+       init_timer(&hc->hw.timer);
+       /* default PCM master */
+       test_and_set_bit(HFC_CFG_MASTER, &hc->cfg);
+       return 0;
+}
+
+static void
+release_card(struct hfc_pci *hc) {
+       u_long  flags;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       hc->hw.int_m2 = 0; /* interrupt output off ! */
+       disable_hwirq(hc);
+       mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE);
+       mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE);
+       if (hc->dch.timer.function != NULL) {
+               del_timer(&hc->dch.timer);
+               hc->dch.timer.function = NULL;
+       }
+       spin_unlock_irqrestore(&hc->lock, flags);
+       if (hc->hw.protocol == ISDN_P_TE_S0)
+               l1_event(hc->dch.l1, CLOSE_CHANNEL);
+       if (hc->initdone)
+               free_irq(hc->irq, hc);
+       release_io_hfcpci(hc); /* must release after free_irq! */
+       mISDN_unregister_device(&hc->dch.dev);
+       mISDN_freebchannel(&hc->bch[1]);
+       mISDN_freebchannel(&hc->bch[0]);
+       mISDN_freedchannel(&hc->dch);
+       list_del(&hc->list);
+       pci_set_drvdata(hc->pdev, NULL);
+       kfree(hc);
+}
+
+static int
+setup_card(struct hfc_pci *card)
+{
+       int             err = -EINVAL;
+       u_int           i;
+       u_long          flags;
+       char            name[MISDN_MAX_IDLEN];
+
+       if (HFC_cnt >= MAX_CARDS)
+               return -EINVAL; /* maybe better value */
+
+       card->dch.debug = debug;
+       spin_lock_init(&card->lock);
+       mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state);
+       card->dch.hw = card;
+       card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+       card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       card->dch.dev.D.send = hfcpci_l2l1D;
+       card->dch.dev.D.ctrl = hfc_dctrl;
+       card->dch.dev.nrbchan = 2;
+       for (i = 0; i < 2; i++) {
+               card->bch[i].nr = i + 1;
+               test_and_set_bit(i + 1, &card->dch.dev.channelmap[0]);
+               card->bch[i].debug = debug;
+               mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+               card->bch[i].hw = card;
+               card->bch[i].ch.send = hfcpci_l2l1B;
+               card->bch[i].ch.ctrl = hfc_bctrl;
+               card->bch[i].ch.nr = i + 1;
+               list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels);
+       }
+       err = setup_hw(card);
+       if (err)
+               goto error;
+       snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1);
+       err = mISDN_register_device(&card->dch.dev, name);
+       if (err)
+               goto error;
+       HFC_cnt++;
+       write_lock_irqsave(&HFClock, flags);
+       list_add_tail(&card->list, &HFClist);
+       write_unlock_irqrestore(&HFClock, flags);
+       printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
+       return 0;
+error:
+       mISDN_freebchannel(&card->bch[1]);
+       mISDN_freebchannel(&card->bch[0]);
+       mISDN_freedchannel(&card->dch);
+       kfree(card);
+       return err;
+}
+
+/* private data in the PCI devices list */
+struct _hfc_map {
+       u_int   subtype;
+       u_int   flag;
+       char    *name;
+};
+
+static const struct _hfc_map hfc_map[] =
+{
+       {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"},
+       {HFC_CCD_B000, 0, "Billion B000"},
+       {HFC_CCD_B006, 0, "Billion B006"},
+       {HFC_CCD_B007, 0, "Billion B007"},
+       {HFC_CCD_B008, 0, "Billion B008"},
+       {HFC_CCD_B009, 0, "Billion B009"},
+       {HFC_CCD_B00A, 0, "Billion B00A"},
+       {HFC_CCD_B00B, 0, "Billion B00B"},
+       {HFC_CCD_B00C, 0, "Billion B00C"},
+       {HFC_CCD_B100, 0, "Seyeon B100"},
+       {HFC_CCD_B700, 0, "Primux II S0 B700"},
+       {HFC_CCD_B701, 0, "Primux II S0 NT B701"},
+       {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"},
+       {HFC_ASUS_0675, 0, "Asuscom/Askey 675"},
+       {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"},
+       {HFC_BERKOM_A1T, 0, "German telekom A1T"},
+       {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"},
+       {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"},
+       {HFC_DIGI_DF_M_IOM2_E, 0,
+           "Digi International DataFire Micro V IOM2 (Europe)"},
+       {HFC_DIGI_DF_M_E, 0,
+           "Digi International DataFire Micro V (Europe)"},
+       {HFC_DIGI_DF_M_IOM2_A, 0,
+           "Digi International DataFire Micro V IOM2 (North America)"},
+       {HFC_DIGI_DF_M_A, 0,
+           "Digi International DataFire Micro V (North America)"},
+       {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"},
+       {},
+};
+
+static struct pci_device_id hfc_ids[] =
+{
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[0]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[1]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[2]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[3]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[4]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[5]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[6]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[7]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[8]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[9]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[10]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[11]},
+       {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[12]},
+       {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[13]},
+       {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[14]},
+       {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[15]},
+       {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[16]},
+       {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[17]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[18]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[19]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[20]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[21]},
+       {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[22]},
+       {},
+};
+
+static int __devinit
+hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int             err = -ENOMEM;
+       struct hfc_pci  *card;
+       struct _hfc_map *m = (struct _hfc_map *)ent->driver_data;
+
+       card = kzalloc(sizeof(struct hfc_pci), GFP_ATOMIC);
+       if (!card) {
+               printk(KERN_ERR "No kmem for HFC card\n");
+               return err;
+       }
+       card->pdev = pdev;
+       card->subtype = m->subtype;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+
+       printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n",
+              m->name, pci_name(pdev));
+
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_card(card);
+       if (err)
+               pci_set_drvdata(pdev, NULL);
+       return err;
+}
+
+static void __devexit
+hfc_remove_pci(struct pci_dev *pdev)
+{
+       struct hfc_pci  *card = pci_get_drvdata(pdev);
+       u_long          flags;
+
+       if (card) {
+               write_lock_irqsave(&HFClock, flags);
+               release_card(card);
+               write_unlock_irqrestore(&HFClock, flags);
+       } else
+               if (debug)
+                       printk(KERN_WARNING "%s: drvdata allready removed\n",
+                           __func__);
+}
+
+
+static struct pci_driver hfc_driver = {
+       .name = "hfcpci",
+       .probe = hfc_probe,
+       .remove = __devexit_p(hfc_remove_pci),
+       .id_table = hfc_ids,
+};
+
+static int __init
+HFC_init(void)
+{
+       int             err;
+
+       err = pci_register_driver(&hfc_driver);
+       return err;
+}
+
+static void __exit
+HFC_cleanup(void)
+{
+       struct hfc_pci  *card, *next;
+
+       list_for_each_entry_safe(card, next, &HFClist, list) {
+               release_card(card);
+       }
+       pci_unregister_driver(&hfc_driver);
+}
+
+module_init(HFC_init);
+module_exit(HFC_cleanup);
diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig
new file mode 100644 (file)
index 0000000..4938355
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# modularer ISDN driver
+#
+
+menuconfig MISDN
+       tristate "Modular ISDN driver"
+       help
+         Enable support for the modular ISDN driver.
+
+if MISDN != n
+
+config MISDN_DSP
+       tristate "Digital Audio Processing of transparent data"
+       depends on MISDN
+       help
+         Enable support for digital audio processing capability.
+         This module may be used for special applications that require
+         cross connecting of bchannels, conferencing, dtmf decoding
+         echo cancelation, tone generation, and Blowfish encryption and
+         decryption.
+         It may use hardware features if available.
+         E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu
+         and get more informations about this module and it's usage.
+         If unsure, say 'N'.
+
+config MISDN_L1OIP
+       tristate "ISDN over IP tunnel"
+       depends on MISDN
+       help
+         Enable support for ISDN over IP tunnel.
+
+         It features:
+           - dynamic IP exchange, if one or both peers have dynamic IPs
+           - BRI (S0) and PRI (S2M) interface
+           - layer 1 control via network keepalive frames
+           - direct tunneling of physical interface via IP
+
+         NOTE: This protocol is called 'Layer 1 over IP' and is not
+         compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is
+         provided in the source code.
+
+source "drivers/isdn/hardware/mISDN/Kconfig"
+
+endif #MISDN
diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile
new file mode 100644 (file)
index 0000000..1cb5e63
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for the modular ISDN driver
+#
+
+obj-$(CONFIG_MISDN) += mISDN_core.o
+obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o
+obj-$(CONFIG_MISDN_L1OIP) += l1oip.o
+
+# multi objects
+
+mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
+mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o
+l1oip-objs := l1oip_core.o l1oip_codec.o
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
new file mode 100644 (file)
index 0000000..3306817
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static u_int debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+static LIST_HEAD(devices);
+DEFINE_RWLOCK(device_lock);
+static u64             device_ids;
+#define MAX_DEVICE_ID  63
+
+static LIST_HEAD(Bprotocols);
+DEFINE_RWLOCK(bp_lock);
+
+struct mISDNdevice
+*get_mdevice(u_int id)
+{
+       struct mISDNdevice      *dev;
+
+       read_lock(&device_lock);
+       list_for_each_entry(dev, &devices, D.list)
+               if (dev->id == id) {
+                       read_unlock(&device_lock);
+                       return dev;
+               }
+       read_unlock(&device_lock);
+       return NULL;
+}
+
+int
+get_mdevice_count(void)
+{
+       struct mISDNdevice      *dev;
+       int                     cnt = 0;
+
+       read_lock(&device_lock);
+       list_for_each_entry(dev, &devices, D.list)
+               cnt++;
+       read_unlock(&device_lock);
+       return cnt;
+}
+
+static int
+get_free_devid(void)
+{
+       u_int   i;
+
+       for (i = 0; i <= MAX_DEVICE_ID; i++)
+               if (!test_and_set_bit(i, (u_long *)&device_ids))
+                       return i;
+       return -1;
+}
+
+int
+mISDN_register_device(struct mISDNdevice *dev, char *name)
+{
+       u_long  flags;
+       int     err;
+
+       dev->id = get_free_devid();
+       if (dev->id < 0)
+               return -EBUSY;
+       if (name && name[0])
+               strcpy(dev->name, name);
+       else
+               sprintf(dev->name, "mISDN%d", dev->id);
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "mISDN_register %s %d\n",
+                       dev->name, dev->id);
+       err = create_stack(dev);
+       if (err)
+               return err;
+       write_lock_irqsave(&device_lock, flags);
+       list_add_tail(&dev->D.list, &devices);
+       write_unlock_irqrestore(&device_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_register_device);
+
+void
+mISDN_unregister_device(struct mISDNdevice *dev) {
+       u_long  flags;
+
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "mISDN_unregister %s %d\n",
+                       dev->name, dev->id);
+       write_lock_irqsave(&device_lock, flags);
+       list_del(&dev->D.list);
+       write_unlock_irqrestore(&device_lock, flags);
+       test_and_clear_bit(dev->id, (u_long *)&device_ids);
+       delete_stack(dev);
+}
+EXPORT_SYMBOL(mISDN_unregister_device);
+
+u_int
+get_all_Bprotocols(void)
+{
+       struct Bprotocol        *bp;
+       u_int   m = 0;
+
+       read_lock(&bp_lock);
+       list_for_each_entry(bp, &Bprotocols, list)
+               m |= bp->Bprotocols;
+       read_unlock(&bp_lock);
+       return m;
+}
+
+struct Bprotocol *
+get_Bprotocol4mask(u_int m)
+{
+       struct Bprotocol        *bp;
+
+       read_lock(&bp_lock);
+       list_for_each_entry(bp, &Bprotocols, list)
+               if (bp->Bprotocols & m) {
+                       read_unlock(&bp_lock);
+                       return bp;
+               }
+       read_unlock(&bp_lock);
+       return NULL;
+}
+
+struct Bprotocol *
+get_Bprotocol4id(u_int id)
+{
+       u_int   m;
+
+       if (id < ISDN_P_B_START || id > 63) {
+               printk(KERN_WARNING "%s id not in range  %d\n",
+                   __func__, id);
+               return NULL;
+       }
+       m = 1 << (id & ISDN_P_B_MASK);
+       return get_Bprotocol4mask(m);
+}
+
+int
+mISDN_register_Bprotocol(struct Bprotocol *bp)
+{
+       u_long                  flags;
+       struct Bprotocol        *old;
+
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "%s: %s/%x\n", __func__,
+                   bp->name, bp->Bprotocols);
+       old = get_Bprotocol4mask(bp->Bprotocols);
+       if (old) {
+               printk(KERN_WARNING
+                   "register duplicate protocol old %s/%x new %s/%x\n",
+                   old->name, old->Bprotocols, bp->name, bp->Bprotocols);
+               return -EBUSY;
+       }
+       write_lock_irqsave(&bp_lock, flags);
+       list_add_tail(&bp->list, &Bprotocols);
+       write_unlock_irqrestore(&bp_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_register_Bprotocol);
+
+void
+mISDN_unregister_Bprotocol(struct Bprotocol *bp)
+{
+       u_long  flags;
+
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
+                       bp->Bprotocols);
+       write_lock_irqsave(&bp_lock, flags);
+       list_del(&bp->list);
+       write_unlock_irqrestore(&bp_lock, flags);
+}
+EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
+
+int
+mISDNInit(void)
+{
+       int     err;
+
+       printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
+               MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
+       mISDN_initstack(&debug);
+       err = mISDN_inittimer(&debug);
+       if (err)
+               goto error;
+       err = l1_init(&debug);
+       if (err) {
+               mISDN_timer_cleanup();
+               goto error;
+       }
+       err = Isdnl2_Init(&debug);
+       if (err) {
+               mISDN_timer_cleanup();
+               l1_cleanup();
+               goto error;
+       }
+       err = misdn_sock_init(&debug);
+       if (err) {
+               mISDN_timer_cleanup();
+               l1_cleanup();
+               Isdnl2_cleanup();
+       }
+error:
+       return err;
+}
+
+void mISDN_cleanup(void)
+{
+       misdn_sock_cleanup();
+       mISDN_timer_cleanup();
+       l1_cleanup();
+       Isdnl2_cleanup();
+
+       if (!list_empty(&devices))
+               printk(KERN_ERR "%s devices still registered\n", __func__);
+
+       if (!list_empty(&Bprotocols))
+               printk(KERN_ERR "%s Bprotocols still registered\n", __func__);
+       printk(KERN_DEBUG "mISDNcore unloaded\n");
+}
+
+module_init(mISDNInit);
+module_exit(mISDN_cleanup);
+
diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h
new file mode 100644 (file)
index 0000000..7da7233
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef mISDN_CORE_H
+#define mISDN_CORE_H
+
+extern struct mISDNdevice      *get_mdevice(u_int);
+extern int                     get_mdevice_count(void);
+
+/* stack status flag */
+#define mISDN_STACK_ACTION_MASK                0x0000ffff
+#define mISDN_STACK_COMMAND_MASK       0x000f0000
+#define mISDN_STACK_STATUS_MASK                0xfff00000
+/* action bits 0-15 */
+#define mISDN_STACK_WORK       0
+#define mISDN_STACK_SETUP      1
+#define mISDN_STACK_CLEARING   2
+#define mISDN_STACK_RESTART    3
+#define mISDN_STACK_WAKEUP     4
+#define mISDN_STACK_ABORT      15
+/* command bits 16-19 */
+#define mISDN_STACK_STOPPED    16
+#define mISDN_STACK_INIT       17
+#define mISDN_STACK_THREADSTART        18
+/* status bits 20-31 */
+#define mISDN_STACK_BCHANNEL   20
+#define mISDN_STACK_ACTIVE      29
+#define mISDN_STACK_RUNNING     30
+#define mISDN_STACK_KILLED      31
+
+
+/* manager options */
+#define MGR_OPT_USER           24
+#define MGR_OPT_NETWORK                25
+
+extern int     connect_Bstack(struct mISDNdevice *, struct mISDNchannel *,
+                       u_int, struct sockaddr_mISDN *);
+extern int     connect_layer1(struct mISDNdevice *, struct mISDNchannel *,
+                       u_int, struct sockaddr_mISDN *);
+extern int     create_l2entity(struct mISDNdevice *, struct mISDNchannel *,
+                       u_int, struct sockaddr_mISDN *);
+
+extern int     create_stack(struct mISDNdevice *);
+extern int     create_teimanager(struct mISDNdevice *);
+extern void    delete_teimanager(struct mISDNchannel *);
+extern void    delete_channel(struct mISDNchannel *);
+extern void    delete_stack(struct mISDNdevice *);
+extern void    mISDN_initstack(u_int *);
+extern int      misdn_sock_init(u_int *);
+extern void     misdn_sock_cleanup(void);
+extern void    add_layer2(struct mISDNchannel *, struct mISDNstack *);
+extern void    __add_layer2(struct mISDNchannel *, struct mISDNstack *);
+
+extern u_int           get_all_Bprotocols(void);
+struct Bprotocol       *get_Bprotocol4mask(u_int);
+struct Bprotocol       *get_Bprotocol4id(u_int);
+
+extern int     mISDN_inittimer(u_int *);
+extern void    mISDN_timer_cleanup(void);
+
+extern int     l1_init(u_int *);
+extern void    l1_cleanup(void);
+extern int     Isdnl2_Init(u_int *);
+extern void    Isdnl2_cleanup(void);
+
+#endif
diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h
new file mode 100644 (file)
index 0000000..6c3fed6
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DEBUG_DSP_CTRL         0x0001
+#define DEBUG_DSP_CORE         0x0002
+#define DEBUG_DSP_DTMF         0x0004
+#define DEBUG_DSP_CMX          0x0010
+#define DEBUG_DSP_TONE         0x0020
+#define DEBUG_DSP_BLOWFISH     0x0040
+#define DEBUG_DSP_DELAY                0x0100
+#define DEBUG_DSP_DTMFCOEFF    0x8000 /* heavy output */
+
+/* options may be:
+ *
+ * bit 0 = use ulaw instead of alaw
+ * bit 1 = enable hfc hardware accelleration for all channels
+ *
+ */
+#define DSP_OPT_ULAW           (1<<0)
+#define DSP_OPT_NOHARDWARE     (1<<1)
+
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include "dsp_ecdis.h"
+
+extern int dsp_options;
+extern int dsp_debug;
+extern int dsp_poll;
+extern int dsp_tics;
+extern spinlock_t dsp_lock;
+extern struct work_struct dsp_workq;
+extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */
+
+/***************
+ * audio stuff *
+ ***************/
+
+extern s32 dsp_audio_alaw_to_s32[256];
+extern s32 dsp_audio_ulaw_to_s32[256];
+extern s32 *dsp_audio_law_to_s32;
+extern u8 dsp_audio_s16_to_law[65536];
+extern u8 dsp_audio_alaw_to_ulaw[256];
+extern u8 dsp_audio_mix_law[65536];
+extern u8 dsp_audio_seven2law[128];
+extern u8 dsp_audio_law2seven[256];
+extern void dsp_audio_generate_law_tables(void);
+extern void dsp_audio_generate_s2law_table(void);
+extern void dsp_audio_generate_seven(void);
+extern void dsp_audio_generate_mix_table(void);
+extern void dsp_audio_generate_ulaw_samples(void);
+extern void dsp_audio_generate_volume_changes(void);
+extern u8 dsp_silence;
+
+
+/*************
+ * cmx stuff *
+ *************/
+
+#define MAX_POLL       256     /* maximum number of send-chunks */
+
+#define CMX_BUFF_SIZE  0x8000  /* must be 2**n (0x1000 about 1/2 second) */
+#define CMX_BUFF_HALF  0x4000  /* CMX_BUFF_SIZE / 2 */
+#define CMX_BUFF_MASK  0x7fff  /* CMX_BUFF_SIZE - 1 */
+
+/* how many seconds will we check the lowest delay until the jitter buffer
+   is reduced by that delay */
+#define MAX_SECONDS_JITTER_CHECK 5
+
+extern struct timer_list dsp_spl_tl;
+extern u32 dsp_spl_jiffies;
+
+/* the structure of conferences:
+ *
+ * each conference has a unique number, given by user space.
+ * the conferences are linked in a chain.
+ * each conference has members linked in a chain.
+ * each dsplayer points to a member, each member points to a dsplayer.
+ */
+
+/* all members within a conference (this is linked 1:1 with the dsp) */
+struct dsp;
+struct dsp_conf_member {
+       struct list_head        list;
+       struct dsp              *dsp;
+};
+
+/* the list of all conferences */
+struct dsp_conf {
+       struct list_head        list;
+       u32                     id;
+                               /* all cmx stacks with the same ID are
+                                connected */
+       struct list_head        mlist;
+       int                     software; /* conf is processed by software */
+       int                     hardware; /* conf is processed by hardware */
+                               /* note: if both unset, has only one member */
+};
+
+
+/**************
+ * DTMF stuff *
+ **************/
+
+#define DSP_DTMF_NPOINTS 102
+
+#define ECHOCAN_BUFLEN (4*128)
+
+struct dsp_dtmf {
+       int             treshold; /* above this is dtmf (square of) */
+       int             software; /* dtmf uses software decoding */
+       int             hardware; /* dtmf uses hardware decoding */
+       int             size; /* number of bytes in buffer */
+       signed short    buffer[DSP_DTMF_NPOINTS];
+               /* buffers one full dtmf frame */
+       u8              lastwhat, lastdigit;
+       int             count;
+       u8              digits[16]; /* just the dtmf result */
+};
+
+
+/******************
+ * pipeline stuff *
+ ******************/
+struct dsp_pipeline {
+       rwlock_t  lock;
+       struct list_head list;
+       int inuse;
+};
+
+/***************
+ * tones stuff *
+ ***************/
+
+struct dsp_tone {
+       int             software; /* tones are generated by software */
+       int             hardware; /* tones are generated by hardware */
+       int             tone;
+       void            *pattern;
+       int             count;
+       int             index;
+       struct timer_list tl;
+};
+
+/*****************
+ * general stuff *
+ *****************/
+
+struct dsp {
+       struct list_head list;
+       struct mISDNchannel     ch;
+       struct mISDNchannel     *up;
+       unsigned char   name[64];
+       int             b_active;
+       int             echo; /* echo is enabled */
+       int             rx_disabled; /* what the user wants */
+       int             rx_is_off; /* what the card is */
+       int             tx_mix;
+       struct dsp_tone tone;
+       struct dsp_dtmf dtmf;
+       int             tx_volume, rx_volume;
+
+       /* queue for sending frames */
+       struct work_struct      workq;
+       struct sk_buff_head     sendq;
+       int             hdlc;   /* if mode is hdlc */
+       int             data_pending;   /* currently an unconfirmed frame */
+
+       /* conference stuff */
+       u32             conf_id;
+       struct dsp_conf *conf;
+       struct dsp_conf_member
+                       *member;
+
+       /* buffer stuff */
+       int             rx_W; /* current write pos for data without timestamp */
+       int             rx_R; /* current read pos for transmit clock */
+       int             rx_init; /* if set, pointers will be adjusted first */
+       int             tx_W; /* current write pos for transmit data */
+       int             tx_R; /* current read pos for transmit clock */
+       int             rx_delay[MAX_SECONDS_JITTER_CHECK];
+       int             tx_delay[MAX_SECONDS_JITTER_CHECK];
+       u8              tx_buff[CMX_BUFF_SIZE];
+       u8              rx_buff[CMX_BUFF_SIZE];
+       int             last_tx; /* if set, we transmitted last poll interval */
+       int             cmx_delay; /* initial delay of buffers,
+                               or 0 for dynamic jitter buffer */
+       int             tx_dejitter; /* if set, dejitter tx buffer */
+       int             tx_data; /* enables tx-data of CMX to upper layer */
+
+       /* hardware stuff */
+       struct dsp_features features;
+       int             features_rx_off; /* set if rx_off is featured */
+       int             pcm_slot_rx; /* current PCM slot (or -1) */
+       int             pcm_bank_rx;
+       int             pcm_slot_tx;
+       int             pcm_bank_tx;
+       int             hfc_conf; /* unique id of current conference (or -1) */
+
+       /* encryption stuff */
+       int             bf_enable;
+       u32             bf_p[18];
+       u32             bf_s[1024];
+       int             bf_crypt_pos;
+       u8              bf_data_in[9];
+       u8              bf_crypt_out[9];
+       int             bf_decrypt_in_pos;
+       int             bf_decrypt_out_pos;
+       u8              bf_crypt_inring[16];
+       u8              bf_data_out[9];
+       int             bf_sync;
+
+       struct dsp_pipeline
+                       pipeline;
+};
+
+/* functions */
+
+extern void dsp_change_volume(struct sk_buff *skb, int volume);
+
+extern struct list_head dsp_ilist;
+extern struct list_head conf_ilist;
+extern void dsp_cmx_debug(struct dsp *dsp);
+extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp);
+extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id);
+extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_send(void *arg);
+extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb);
+extern int dsp_cmx_del_conf_member(struct dsp *dsp);
+extern int dsp_cmx_del_conf(struct dsp_conf *conf);
+
+extern void dsp_dtmf_goertzel_init(struct dsp *dsp);
+extern void dsp_dtmf_hardware(struct dsp *dsp);
+extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len,
+               int fmt);
+
+extern int dsp_tone(struct dsp *dsp, int tone);
+extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len);
+extern void dsp_tone_timeout(void *arg);
+
+extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len);
+extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len);
+extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen);
+extern void dsp_bf_cleanup(struct dsp *dsp);
+
+extern int  dsp_pipeline_module_init(void);
+extern void dsp_pipeline_module_exit(void);
+extern int  dsp_pipeline_init(struct dsp_pipeline *pipeline);
+extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline);
+extern int  dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg);
+extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data,
+               int len);
+extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data,
+               int len);
+
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
new file mode 100644 (file)
index 0000000..1c2dd56
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Audio support data for mISDN_dsp.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ * Rewritten by Peter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/* ulaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_ulaw_to_s32[256];
+/* alaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_alaw_to_s32[256];
+
+s32 *dsp_audio_law_to_s32;
+EXPORT_SYMBOL(dsp_audio_law_to_s32);
+
+/* signed 16-bit -> law */
+u8 dsp_audio_s16_to_law[65536];
+EXPORT_SYMBOL(dsp_audio_s16_to_law);
+
+/* alaw -> ulaw */
+u8 dsp_audio_alaw_to_ulaw[256];
+/* ulaw -> alaw */
+u8 dsp_audio_ulaw_to_alaw[256];
+u8 dsp_silence;
+
+
+/*****************************************************
+ * generate table for conversion of s16 to alaw/ulaw *
+ *****************************************************/
+
+#define AMI_MASK 0x55
+
+static inline unsigned char linear2alaw(short int linear)
+{
+       int mask;
+       int seg;
+       int pcm_val;
+       static int seg_end[8] = {
+               0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+       };
+
+       pcm_val = linear;
+       if (pcm_val >= 0) {
+               /* Sign (7th) bit = 1 */
+               mask = AMI_MASK | 0x80;
+       } else {
+               /* Sign bit = 0 */
+               mask = AMI_MASK;
+               pcm_val = -pcm_val;
+       }
+
+       /* Convert the scaled magnitude to segment number. */
+       for (seg = 0;  seg < 8;  seg++) {
+               if (pcm_val <= seg_end[seg])
+                       break;
+       }
+       /* Combine the sign, segment, and quantization bits. */
+       return  ((seg << 4) |
+                ((pcm_val >> ((seg)  ?  (seg + 3)  :  4)) & 0x0F)) ^ mask;
+}
+
+
+static inline short int alaw2linear(unsigned char alaw)
+{
+       int i;
+       int seg;
+
+       alaw ^= AMI_MASK;
+       i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
+       seg = (((int) alaw & 0x70) >> 4);
+       if (seg)
+               i = (i + 0x100) << (seg - 1);
+       return (short int) ((alaw & 0x80)  ?  i  :  -i);
+}
+
+static inline short int ulaw2linear(unsigned char ulaw)
+{
+       short mu, e, f, y;
+       static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
+
+       mu = 255 - ulaw;
+       e = (mu & 0x70) / 16;
+       f = mu & 0x0f;
+       y = f * (1 << (e + 3));
+       y += etab[e];
+       if (mu & 0x80)
+               y = -y;
+       return y;
+}
+
+#define BIAS 0x84   /*!< define the add-in bias for 16 bit samples */
+
+static unsigned char linear2ulaw(short sample)
+{
+       static int exp_lut[256] = {
+               0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+               5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+               5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+       int sign, exponent, mantissa;
+       unsigned char ulawbyte;
+
+       /* Get the sample into sign-magnitude. */
+       sign = (sample >> 8) & 0x80;      /* set aside the sign */
+       if (sign != 0)
+               sample = -sample;             /* get magnitude */
+
+       /* Convert from 16 bit linear to ulaw. */
+       sample = sample + BIAS;
+       exponent = exp_lut[(sample >> 7) & 0xFF];
+       mantissa = (sample >> (exponent + 3)) & 0x0F;
+       ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+       return ulawbyte;
+}
+
+static int reverse_bits(int i)
+{
+       int z, j;
+       z = 0;
+
+       for (j = 0; j < 8; j++) {
+               if ((i & (1 << j)) != 0)
+                       z |= 1 << (7 - j);
+       }
+       return z;
+}
+
+
+void dsp_audio_generate_law_tables(void)
+{
+       int i;
+       for (i = 0; i < 256; i++)
+               dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i));
+
+       for (i = 0; i < 256; i++)
+               dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i));
+
+       for (i = 0; i < 256; i++) {
+               dsp_audio_alaw_to_ulaw[i] =
+                       linear2ulaw(dsp_audio_alaw_to_s32[i]);
+               dsp_audio_ulaw_to_alaw[i] =
+                       linear2alaw(dsp_audio_ulaw_to_s32[i]);
+       }
+}
+
+void
+dsp_audio_generate_s2law_table(void)
+{
+       int i;
+
+       if (dsp_options & DSP_OPT_ULAW) {
+               /* generating ulaw-table */
+               for (i = -32768; i < 32768; i++) {
+                       dsp_audio_s16_to_law[i & 0xffff] =
+                               reverse_bits(linear2ulaw(i));
+               }
+       } else {
+               /* generating alaw-table */
+               for (i = -32768; i < 32768; i++) {
+                       dsp_audio_s16_to_law[i & 0xffff] =
+                               reverse_bits(linear2alaw(i));
+               }
+       }
+}
+
+
+/*
+ * the seven bit sample is the number of every second alaw-sample ordered by
+ * aplitude. 0x00 is negative, 0x7f is positive amplitude.
+ */
+u8 dsp_audio_seven2law[128];
+u8 dsp_audio_law2seven[256];
+
+/********************************************************************
+ * generate table for conversion law from/to 7-bit alaw-like sample *
+ ********************************************************************/
+
+void
+dsp_audio_generate_seven(void)
+{
+       int i, j, k;
+       u8 spl;
+       u8 sorted_alaw[256];
+
+       /* generate alaw table, sorted by the linear value */
+       for (i = 0; i < 256; i++) {
+               j = 0;
+               for (k = 0; k < 256; k++) {
+                       if (dsp_audio_alaw_to_s32[k]
+                               < dsp_audio_alaw_to_s32[i]) {
+                       j++;
+                       }
+               }
+               sorted_alaw[j] = i;
+       }
+
+       /* generate tabels */
+       for (i = 0; i < 256; i++) {
+               /* spl is the source: the law-sample (converted to alaw) */
+               spl = i;
+               if (dsp_options & DSP_OPT_ULAW)
+                       spl = dsp_audio_ulaw_to_alaw[i];
+               /* find the 7-bit-sample */
+               for (j = 0; j < 256; j++) {
+                       if (sorted_alaw[j] == spl)
+                               break;
+               }
+               /* write 7-bit audio value */
+               dsp_audio_law2seven[i] = j >> 1;
+       }
+       for (i = 0; i < 128; i++) {
+               spl = sorted_alaw[i << 1];
+               if (dsp_options & DSP_OPT_ULAW)
+                       spl = dsp_audio_alaw_to_ulaw[spl];
+               dsp_audio_seven2law[i] = spl;
+       }
+}
+
+
+/* mix 2*law -> law */
+u8 dsp_audio_mix_law[65536];
+
+/******************************************************
+ * generate mix table to mix two law samples into one *
+ ******************************************************/
+
+void
+dsp_audio_generate_mix_table(void)
+{
+       int i, j;
+       s32 sample;
+
+       i = 0;
+       while (i < 256) {
+               j = 0;
+               while (j < 256) {
+                       sample = dsp_audio_law_to_s32[i];
+                       sample += dsp_audio_law_to_s32[j];
+                       if (sample > 32767)
+                               sample = 32767;
+                       if (sample < -32768)
+                               sample = -32768;
+                       dsp_audio_mix_law[(i<<8)|j] =
+                               dsp_audio_s16_to_law[sample & 0xffff];
+                       j++;
+               }
+               i++;
+       }
+}
+
+
+/*************************************
+ * generate different volume changes *
+ *************************************/
+
+static u8 dsp_audio_reduce8[256];
+static u8 dsp_audio_reduce7[256];
+static u8 dsp_audio_reduce6[256];
+static u8 dsp_audio_reduce5[256];
+static u8 dsp_audio_reduce4[256];
+static u8 dsp_audio_reduce3[256];
+static u8 dsp_audio_reduce2[256];
+static u8 dsp_audio_reduce1[256];
+static u8 dsp_audio_increase1[256];
+static u8 dsp_audio_increase2[256];
+static u8 dsp_audio_increase3[256];
+static u8 dsp_audio_increase4[256];
+static u8 dsp_audio_increase5[256];
+static u8 dsp_audio_increase6[256];
+static u8 dsp_audio_increase7[256];
+static u8 dsp_audio_increase8[256];
+
+static u8 *dsp_audio_volume_change[16] = {
+       dsp_audio_reduce8,
+       dsp_audio_reduce7,
+       dsp_audio_reduce6,
+       dsp_audio_reduce5,
+       dsp_audio_reduce4,
+       dsp_audio_reduce3,
+       dsp_audio_reduce2,
+       dsp_audio_reduce1,
+       dsp_audio_increase1,
+       dsp_audio_increase2,
+       dsp_audio_increase3,
+       dsp_audio_increase4,
+       dsp_audio_increase5,
+       dsp_audio_increase6,
+       dsp_audio_increase7,
+       dsp_audio_increase8,
+};
+
+void
+dsp_audio_generate_volume_changes(void)
+{
+       register s32 sample;
+       int i;
+       int num[]   = { 110, 125, 150, 175, 200, 300, 400, 500 };
+       int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 };
+
+       i = 0;
+       while (i < 256) {
+               dsp_audio_reduce8[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff];
+               dsp_audio_reduce7[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff];
+               dsp_audio_reduce6[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff];
+               dsp_audio_reduce5[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff];
+               dsp_audio_reduce4[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff];
+               dsp_audio_reduce3[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff];
+               dsp_audio_reduce2[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff];
+               dsp_audio_reduce1[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[0] / denum[0];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[1] / denum[1];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[2] / denum[2];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[3] / denum[3];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[4] / denum[4];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[5] / denum[5];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[6] / denum[6];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[7] / denum[7];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff];
+
+               i++;
+       }
+}
+
+
+/**************************************
+ * change the volume of the given skb *
+ **************************************/
+
+/* this is a helper function for changing volume of skb. the range may be
+ * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8
+ */
+void
+dsp_change_volume(struct sk_buff *skb, int volume)
+{
+       u8 *volume_change;
+       int i, ii;
+       u8 *p;
+       int shift;
+
+       if (volume == 0)
+               return;
+
+       /* get correct conversion table */
+       if (volume < 0) {
+               shift = volume + 8;
+               if (shift < 0)
+                       shift = 0;
+       } else {
+               shift = volume + 7;
+               if (shift > 15)
+                       shift = 15;
+       }
+       volume_change = dsp_audio_volume_change[shift];
+       i = 0;
+       ii = skb->len;
+       p = skb->data;
+       /* change volume */
+       while (i < ii) {
+               *p = volume_change[*p];
+               p++;
+               i++;
+       }
+}
+
diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h
new file mode 100644 (file)
index 0000000..038191b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * biquad.h - General telephony bi-quad section routines (currently this just
+ *            handles canonic/type 2 form)
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+struct biquad2_state {
+       int32_t gain;
+       int32_t a1;
+       int32_t a2;
+       int32_t b1;
+       int32_t b2;
+
+       int32_t z1;
+       int32_t z2;
+};
+
+static inline void biquad2_init(struct biquad2_state *bq,
+    int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2)
+{
+       bq->gain = gain;
+       bq->a1 = a1;
+       bq->a2 = a2;
+       bq->b1 = b1;
+       bq->b2 = b2;
+
+       bq->z1 = 0;
+       bq->z2 = 0;
+}
+
+static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample)
+{
+       int32_t y;
+       int32_t z0;
+
+       z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2;
+       y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2;
+
+       bq->z2 = bq->z1;
+       bq->z1 = z0 >> 15;
+       y >>= 15;
+       return  y;
+}
diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c
new file mode 100644 (file)
index 0000000..18e411e
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Blowfish encryption/decryption for mISDN_dsp.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/*
+ * how to encode a sample stream to 64-bit blocks that will be encryped
+ *
+ * first of all, data is collected until a block of 9 samples are received.
+ * of course, a packet may have much more than 9 sample, but is may have
+ * not excacly the multiple of 9 samples. if there is a rest, the next
+ * received data will complete the block.
+ *
+ * the block is then converted to 9 uLAW samples without the least sigificant
+ * bit. the result is a 7-bit encoded sample.
+ *
+ * the samples will be reoganised to form 8 bytes of data:
+ * (5(6) means: encoded sample no. 5, bit 6)
+ *
+ * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6)
+ * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5)
+ * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4)
+ * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3)
+ * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2)
+ * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1)
+ * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
+ * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0)
+ *
+ * the missing bit 0 of the last byte is filled with some
+ * random noise, to fill all 8 bytes.
+ *
+ * the 8 bytes will be encrypted using blowfish.
+ *
+ * the result will be converted into 9 bytes. the bit 7 is used for
+ * checksumme (CS) for sync (0, 1) and for the last bit:
+ * (5(6) means: crypted byte 5, bit 6)
+ *
+ * 1    0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1)
+ * 0    0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2)
+ * 0    1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3)
+ * 0    2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4)
+ * 0    3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5)
+ * CS   4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6)
+ * CS   5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7)
+ * CS   6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0)
+ * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
+ *
+ * the checksum is used to detect transmission errors and frame drops.
+ *
+ * synchronisation of received block is done by shifting the upper bit of each
+ * byte (bit 7) to a shift register. if the rigister has the first five bits
+ * (10000), this is used to find the sync. only if sync has been found, the
+ * current block of 9 received bytes are decrypted. before that the check
+ * sum is calculated. if it is incorrect the block is dropped.
+ * this will avoid loud noise due to corrupt encrypted data.
+ *
+ * if the last block is corrupt, the current decoded block is repeated
+ * until a valid block has been received.
+ */
+
+/*
+ *  some blowfish parts are taken from the
+ * crypto-api for faster implementation
+ */
+
+struct bf_ctx {
+       u32 p[18];
+       u32 s[1024];
+};
+
+static const u32 bf_pbox[16 + 2] = {
+       0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+       0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+       0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+       0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+       0x9216d5d9, 0x8979fb1b,
+};
+
+static const u32 bf_sbox[256 * 4] = {
+       0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+       0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+       0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+       0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+       0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+       0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+       0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+       0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+       0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+       0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+       0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+       0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+       0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+       0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+       0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+       0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+       0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+       0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+       0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+       0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+       0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+       0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+       0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+       0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+       0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+       0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+       0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+       0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+       0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+       0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+       0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+       0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+       0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+       0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+       0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+       0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+       0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+       0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+       0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+       0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+       0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+       0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+       0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+       0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+       0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+       0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+       0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+       0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+       0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+       0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+       0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+       0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+       0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+       0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+       0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+       0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+       0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+       0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+       0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+       0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+       0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+       0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+       0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+       0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+       0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+       0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+       0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+       0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+       0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+       0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+       0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+       0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+       0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+       0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+       0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+       0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+       0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+       0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+       0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+       0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+       0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+       0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+       0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+       0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+       0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+       0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+       0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+       0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+       0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+       0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+       0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+       0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+       0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+       0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+       0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+       0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+       0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+       0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+       0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+       0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+       0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+       0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+       0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+       0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+       0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+       0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+       0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+       0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+       0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+       0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+       0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+       0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+       0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+       0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+       0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+       0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+       0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+       0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+       0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+       0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+       0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+       0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+       0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+       0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+       0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+       0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+       0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+       0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+       0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+       0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+       0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+       0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+       0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+       0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+       0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+       0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+       0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+       0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+       0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+       0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+       0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+       0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+       0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+       0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+       0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+       0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+       0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+       0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+       0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+       0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+       0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+       0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+       0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+       0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+       0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+       0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+       0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+       0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+       0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+       0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+       0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+       0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+       0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+       0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+       0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+       0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+       0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+       0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+       0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+       0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+       0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+       0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+       0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+       0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+       0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+       0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+       0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+       0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+       0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+       0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+       0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+       0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+       0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+       0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+       0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+       0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+       0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+       0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+       0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+       0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+       0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+       0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+       0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+       0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+       0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+       0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+       0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+       0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+       0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+       0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+       0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+       0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+       0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+       0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+       0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+       0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+       0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+       0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+       0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+       0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+       0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+       0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+       0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+       0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+       0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+       0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+       0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+       0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+       0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+       0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+       0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+       0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+       0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+       0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+       0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+       0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+       0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+       0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+       0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+       0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+       0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+       0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+       0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+       0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+       0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+       0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+       0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+       0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+       0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+       0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+       0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+       0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+       0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+       0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+       0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+       0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+       0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+       0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+       0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+       0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+       0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+       0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+       0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+       0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+       0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+       0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+};
+
+/*
+ * Round loop unrolling macros, S is a pointer to a S-Box array
+ * organized in 4 unsigned longs at a row.
+ */
+#define GET32_3(x) (((x) & 0xff))
+#define GET32_2(x) (((x) >> (8)) & (0xff))
+#define GET32_1(x) (((x) >> (16)) & (0xff))
+#define GET32_0(x) (((x) >> (24)) & (0xff))
+
+#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \
+    S[512 + GET32_2(x)]) + S[768 + GET32_3(x)])
+
+#define EROUND(a, b, n)  do { b ^= P[n]; a ^= bf_F(b); } while (0)
+#define DROUND(a, b, n)  do { a ^= bf_F(b); b ^= P[n]; } while (0)
+
+
+/*
+ * encrypt isdn data frame
+ * every block with 9 samples is encrypted
+ */
+void
+dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len)
+{
+       int i = 0, j = dsp->bf_crypt_pos;
+       u8 *bf_data_in = dsp->bf_data_in;
+       u8 *bf_crypt_out = dsp->bf_crypt_out;
+       u32 *P = dsp->bf_p;
+       u32 *S = dsp->bf_s;
+       u32 yl, yr;
+       u32 cs;
+       u8 nibble;
+
+       while (i < len) {
+               /* collect a block of 9 samples */
+               if (j < 9) {
+                       bf_data_in[j] = *data;
+                       *data++ = bf_crypt_out[j++];
+                       i++;
+                       continue;
+               }
+               j = 0;
+               /* transcode 9 samples xlaw to 8 bytes */
+               yl = dsp_audio_law2seven[bf_data_in[0]];
+               yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]];
+               yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]];
+               yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]];
+               nibble = dsp_audio_law2seven[bf_data_in[4]];
+               yr = nibble;
+               yl = (yl<<4) | (nibble>>3);
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]];
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]];
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]];
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]];
+               yr = (yr<<1) | (bf_data_in[0] & 1);
+
+               /* fill unused bit with random noise of audio input */
+               /* encrypt */
+
+               EROUND(yr, yl, 0);
+               EROUND(yl, yr, 1);
+               EROUND(yr, yl, 2);
+               EROUND(yl, yr, 3);
+               EROUND(yr, yl, 4);
+               EROUND(yl, yr, 5);
+               EROUND(yr, yl, 6);
+               EROUND(yl, yr, 7);
+               EROUND(yr, yl, 8);
+               EROUND(yl, yr, 9);
+               EROUND(yr, yl, 10);
+               EROUND(yl, yr, 11);
+               EROUND(yr, yl, 12);
+               EROUND(yl, yr, 13);
+               EROUND(yr, yl, 14);
+               EROUND(yl, yr, 15);
+               yl ^= P[16];
+               yr ^= P[17];
+
+               /* calculate 3-bit checksumme */
+               cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15)
+                       ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30)
+                       ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10)
+                       ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25)
+                       ^ (yr>>28) ^ (yr>>31);
+
+               /*
+                * transcode 8 crypted bytes to 9 data bytes with sync
+                * and checksum information
+                */
+               bf_crypt_out[0] = (yl>>25) | 0x80;
+               bf_crypt_out[1] = (yl>>18) & 0x7f;
+               bf_crypt_out[2] = (yl>>11) & 0x7f;
+               bf_crypt_out[3] = (yl>>4) & 0x7f;
+               bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07);
+               bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80);
+               bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80);
+               bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7);
+               bf_crypt_out[8] = yr;
+       }
+
+       /* write current count */
+       dsp->bf_crypt_pos = j;
+
+}
+
+
+/*
+ * decrypt isdn data frame
+ * every block with 9 bytes is decrypted
+ */
+void
+dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len)
+{
+       int i = 0;
+       u8 j = dsp->bf_decrypt_in_pos;
+       u8 k = dsp->bf_decrypt_out_pos;
+       u8 *bf_crypt_inring = dsp->bf_crypt_inring;
+       u8 *bf_data_out = dsp->bf_data_out;
+       u16 sync = dsp->bf_sync;
+       u32 *P = dsp->bf_p;
+       u32 *S = dsp->bf_s;
+       u32 yl, yr;
+       u8 nibble;
+       u8 cs, cs0, cs1, cs2;
+
+       while (i < len) {
+               /*
+                * shift upper bit and rotate data to buffer ring
+                * send current decrypted data
+                */
+               sync = (sync<<1) | ((*data)>>7);
+               bf_crypt_inring[j++ & 15] = *data;
+               *data++ = bf_data_out[k++];
+               i++;
+               if (k == 9)
+                       k = 0; /* repeat if no sync has been found */
+               /* check if not in sync */
+               if ((sync&0x1f0) != 0x100)
+                       continue;
+               j -= 9;
+               /* transcode receive data to 64 bit block of encrypted data */
+               yl = bf_crypt_inring[j++ & 15];
+               yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               yr = nibble;
+               yl = (yl<<4) | (nibble>>3);
+               cs2 = bf_crypt_inring[j++ & 15];
+               yr = (yr<<7) | (cs2 & 0x7f);
+               cs1 = bf_crypt_inring[j++ & 15];
+               yr = (yr<<7) | (cs1 & 0x7f);
+               cs0 = bf_crypt_inring[j++ & 15];
+               yr = (yr<<7) | (cs0 & 0x7f);
+               yr = (yr<<8) | bf_crypt_inring[j++ & 15];
+
+               /* calculate 3-bit checksumme */
+               cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15)
+                       ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30)
+                       ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10)
+                       ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25)
+                       ^ (yr>>28) ^ (yr>>31);
+
+               /* check if frame is valid */
+               if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) {
+                       if (dsp_debug & DEBUG_DSP_BLOWFISH)
+                               printk(KERN_DEBUG
+                                   "DSP BLOWFISH: received corrupt frame, "
+                                   "checksumme is not correct\n");
+                       continue;
+               }
+
+               /* decrypt */
+               yr ^= P[17];
+               yl ^= P[16];
+               DROUND(yl, yr, 15);
+               DROUND(yr, yl, 14);
+               DROUND(yl, yr, 13);
+               DROUND(yr, yl, 12);
+               DROUND(yl, yr, 11);
+               DROUND(yr, yl, 10);
+               DROUND(yl, yr, 9);
+               DROUND(yr, yl, 8);
+               DROUND(yl, yr, 7);
+               DROUND(yr, yl, 6);
+               DROUND(yl, yr, 5);
+               DROUND(yr, yl, 4);
+               DROUND(yl, yr, 3);
+               DROUND(yr, yl, 2);
+               DROUND(yl, yr, 1);
+               DROUND(yr, yl, 0);
+
+               /* transcode 8 crypted bytes to 9 sample bytes */
+               bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f];
+               bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f];
+               bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f];
+               bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f];
+               bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) |
+                   ((yr>>29) & 0x07)];
+
+               bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f];
+               bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f];
+               bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f];
+               bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f];
+               k = 0; /* start with new decoded frame */
+       }
+
+       /* write current count and sync */
+       dsp->bf_decrypt_in_pos = j;
+       dsp->bf_decrypt_out_pos = k;
+       dsp->bf_sync = sync;
+}
+
+
+/* used to encrypt S and P boxes */
+static inline void
+encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src)
+{
+       u32 yl = src[0];
+       u32 yr = src[1];
+
+       EROUND(yr, yl, 0);
+       EROUND(yl, yr, 1);
+       EROUND(yr, yl, 2);
+       EROUND(yl, yr, 3);
+       EROUND(yr, yl, 4);
+       EROUND(yl, yr, 5);
+       EROUND(yr, yl, 6);
+       EROUND(yl, yr, 7);
+       EROUND(yr, yl, 8);
+       EROUND(yl, yr, 9);
+       EROUND(yr, yl, 10);
+       EROUND(yl, yr, 11);
+       EROUND(yr, yl, 12);
+       EROUND(yl, yr, 13);
+       EROUND(yr, yl, 14);
+       EROUND(yl, yr, 15);
+
+       yl ^= P[16];
+       yr ^= P[17];
+
+       dst[0] = yr;
+       dst[1] = yl;
+}
+
+/*
+ * initialize the dsp for encryption and decryption using the same key
+ * Calculates the blowfish S and P boxes for encryption and decryption.
+ * The margin of keylen must be 4-56 bytes.
+ * returns 0 if ok.
+ */
+int
+dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen)
+{
+       short i, j, count;
+       u32 data[2], temp;
+       u32 *P = (u32 *)dsp->bf_p;
+       u32 *S = (u32 *)dsp->bf_s;
+
+       if (keylen < 4 || keylen > 56)
+               return 1;
+
+       /* Set dsp states */
+       i = 0;
+       while (i < 9) {
+               dsp->bf_crypt_out[i] = 0xff;
+               dsp->bf_data_out[i] = dsp_silence;
+               i++;
+       }
+       dsp->bf_crypt_pos = 0;
+       dsp->bf_decrypt_in_pos = 0;
+       dsp->bf_decrypt_out_pos = 0;
+       dsp->bf_sync = 0x1ff;
+       dsp->bf_enable = 1;
+
+       /* Copy the initialization s-boxes */
+       for (i = 0, count = 0; i < 256; i++)
+               for (j = 0; j < 4; j++, count++)
+                       S[count] = bf_sbox[count];
+
+       /* Set the p-boxes */
+       for (i = 0; i < 16 + 2; i++)
+               P[i] = bf_pbox[i];
+
+       /* Actual subkey generation */
+       for (j = 0, i = 0; i < 16 + 2; i++) {
+               temp = (((u32)key[j] << 24) |
+                   ((u32)key[(j + 1) % keylen] << 16) |
+                   ((u32)key[(j + 2) % keylen] << 8) |
+                   ((u32)key[(j + 3) % keylen]));
+
+               P[i] = P[i] ^ temp;
+               j = (j + 4) % keylen;
+       }
+
+       data[0] = 0x00000000;
+       data[1] = 0x00000000;
+
+       for (i = 0; i < 16 + 2; i += 2) {
+               encrypt_block(P, S, data, data);
+
+               P[i] = data[0];
+               P[i + 1] = data[1];
+       }
+
+       for (i = 0; i < 4; i++) {
+               for (j = 0, count = i * 256; j < 256; j += 2, count += 2) {
+                       encrypt_block(P, S, data, data);
+
+                       S[count] = data[0];
+                       S[count + 1] = data[1];
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+ * turn encryption off
+ */
+void
+dsp_bf_cleanup(struct dsp *dsp)
+{
+       dsp->bf_enable = 0;
+}
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
new file mode 100644 (file)
index 0000000..e92b1ba
--- /dev/null
@@ -0,0 +1,1886 @@
+/*
+ * Audio crossconnecting/conferrencing (hardware level).
+ *
+ * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*
+ * The process of adding and removing parties to/from a conference:
+ *
+ * There is a chain of struct dsp_conf which has one or more members in a chain
+ * of struct dsp_conf_member.
+ *
+ * After a party is added, the conference is checked for hardware capability.
+ * Also if a party is removed, the conference is checked again.
+ *
+ * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect
+ * 1-n = hardware-conference. The n will give the conference number.
+ *
+ * Depending on the change after removal or insertion of a party, hardware
+ * commands are given.
+ *
+ * The current solution is stored within the struct dsp_conf entry.
+ */
+
+/*
+ * HOW THE CMX WORKS:
+ *
+ * There are 3 types of interaction: One member is alone, in this case only
+ * data flow from upper to lower layer is done.
+ * Two members will also exchange their data so they are crossconnected.
+ * Three or more members will be added in a conference and will hear each
+ * other but will not receive their own speech (echo) if not enabled.
+ *
+ * Features of CMX are:
+ *  - Crossconnecting or even conference, if more than two members are together.
+ *  - Force mixing of transmit data with other crossconnect/conference members.
+ *  - Echo generation to benchmark the delay of audio processing.
+ *  - Use hardware to minimize cpu load, disable FIFO load and minimize delay.
+ *  - Dejittering and clock generation.
+ *
+ * There are 2 buffers:
+ *
+ *
+ * RX-Buffer
+ *                 R             W
+ *                 |             |
+ * ----------------+-------------+-------------------
+ *
+ * The rx-buffer is a ring buffer used to store the received data for each
+ * individual member. This is only the case if data needs to be dejittered
+ * or in case of a conference where different clocks require reclocking.
+ * The transmit-clock (R) will read the buffer.
+ * If the clock overruns the write-pointer, we will have a buffer underrun.
+ * If the write pointer always has a certain distance from the transmit-
+ * clock, we will have a delay. The delay will dynamically be increased and
+ * reduced.
+ *
+ *
+ * TX-Buffer
+ *                  R        W
+ *                  |        |
+ * -----------------+--------+-----------------------
+ *
+ * The tx-buffer is a ring buffer to queue the transmit data from user space
+ * until it will be mixed or sent. There are two pointers, R and W. If the write
+ * pointer W would reach or overrun R, the buffer would overrun. In this case
+ * (some) data is dropped so that it will not overrun.
+ * Additionally a dynamic dejittering can be enabled. this allows data from
+ * user space that have jitter and different clock source.
+ *
+ *
+ * Clock:
+ *
+ * A Clock is not required, if the data source has exactly one clock. In this
+ * case the data source is forwarded to the destination.
+ *
+ * A Clock is required, because the data source
+ *  - has multiple clocks.
+ *  - has no usable clock due to jitter or packet loss (VoIP).
+ * In this case the system's clock is used. The clock resolution depends on
+ * the jiffie resolution.
+ *
+ * If a member joins a conference:
+ *
+ * - If a member joins, its rx_buff is set to silence and change read pointer
+ *   to transmit clock.
+ *
+ * The procedure of received data from card is explained in cmx_receive.
+ * The procedure of received data from user space is explained in cmx_transmit.
+ * The procedure of transmit data to card is cmx_send.
+ *
+ *
+ * Interaction with other features:
+ *
+ * DTMF:
+ * DTMF decoding is done before the data is crossconnected.
+ *
+ * Volume change:
+ * Changing rx-volume is done before the data is crossconnected. The tx-volume
+ * must be changed whenever data is transmitted to the card by the cmx.
+ *
+ * Tones:
+ * If a tone is enabled, it will be processed whenever data is transmitted to
+ * the card. It will replace the tx-data from the user space.
+ * If tones are generated by hardware, this conference member is removed for
+ * this time.
+ *
+ * Disable rx-data:
+ * If cmx is realized in hardware, rx data will be disabled if requested by
+ * the upper layer. If dtmf decoding is done by software and enabled, rx data
+ * will not be diabled but blocked to the upper layer.
+ *
+ * HFC conference engine:
+ * If it is possible to realize all features using hardware, hardware will be
+ * used if not forbidden by control command. Disabling rx-data provides
+ * absolutely traffic free audio processing. (except for the quick 1-frame
+ * upload of a tone loop, only once for a new tone)
+ *
+ */
+
+/* delay.h is required for hw_lock.h */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+/*
+ * debugging of multi party conference,
+ * by using conference even with two members
+ */
+
+/* #define CMX_CONF_DEBUG */
+
+/*#define CMX_DEBUG * massive read/write pointer output */
+/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */
+
+static inline int
+count_list_member(struct list_head *head)
+{
+       int                     cnt = 0;
+       struct list_head        *m;
+
+       list_for_each(m, head)
+               cnt++;
+       return cnt;
+}
+
+/*
+ * debug cmx memory structure
+ */
+void
+dsp_cmx_debug(struct dsp *dsp)
+{
+       struct dsp_conf *conf;
+       struct dsp_conf_member  *member;
+       struct dsp              *odsp;
+
+       printk(KERN_DEBUG "-----Current DSP\n");
+       list_for_each_entry(odsp, &dsp_ilist, list) {
+               printk(KERN_DEBUG "* %s echo=%d txmix=%d",
+                   odsp->name, odsp->echo, odsp->tx_mix);
+               if (odsp->conf)
+                       printk(" (Conf %d)", odsp->conf->id);
+               if (dsp == odsp)
+                       printk(" *this*");
+               printk("\n");
+       }
+       printk(KERN_DEBUG "-----Current Conf:\n");
+       list_for_each_entry(conf, &conf_ilist, list) {
+               printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf);
+               list_for_each_entry(member, &conf->mlist, list) {
+                       printk(KERN_DEBUG
+                           "  - member = %s (slot_tx %d, bank_tx %d, "
+                           "slot_rx %d, bank_rx %d hfc_conf %d)%s\n",
+                           member->dsp->name, member->dsp->pcm_slot_tx,
+                           member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx,
+                           member->dsp->pcm_bank_rx, member->dsp->hfc_conf,
+                           (member->dsp == dsp) ? " *this*" : "");
+               }
+       }
+       printk(KERN_DEBUG "-----end\n");
+}
+
+/*
+ * search conference
+ */
+static struct dsp_conf *
+dsp_cmx_search_conf(u32 id)
+{
+       struct dsp_conf *conf;
+
+       if (!id) {
+               printk(KERN_WARNING "%s: conference ID is 0.\n", __func__);
+               return NULL;
+       }
+
+       /* search conference */
+       list_for_each_entry(conf, &conf_ilist, list)
+               if (conf->id == id)
+                       return conf;
+
+       return NULL;
+}
+
+
+/*
+ * add member to conference
+ */
+static int
+dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf)
+{
+       struct dsp_conf_member *member;
+
+       if (!conf || !dsp) {
+               printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__);
+               return -EINVAL;
+       }
+       if (dsp->member) {
+               printk(KERN_WARNING "%s: dsp is already member in a conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (dsp->conf) {
+               printk(KERN_WARNING "%s: dsp is already in a conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC);
+       if (!member) {
+               printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n");
+               return -ENOMEM;
+       }
+       member->dsp = dsp;
+       /* clear rx buffer */
+       memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+       dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */
+       dsp->rx_W = 0;
+       dsp->rx_R = 0;
+
+       list_add_tail(&member->list, &conf->mlist);
+
+       dsp->conf = conf;
+       dsp->member = member;
+
+       return 0;
+}
+
+
+/*
+ * del member from conference
+ */
+int
+dsp_cmx_del_conf_member(struct dsp *dsp)
+{
+       struct dsp_conf_member *member;
+
+       if (!dsp) {
+               printk(KERN_WARNING "%s: dsp is 0.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (!dsp->conf) {
+               printk(KERN_WARNING "%s: dsp is not in a conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (list_empty(&dsp->conf->mlist)) {
+               printk(KERN_WARNING "%s: dsp has linked an empty conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       /* find us in conf */
+       list_for_each_entry(member, &dsp->conf->mlist, list) {
+               if (member->dsp == dsp) {
+                       list_del(&member->list);
+                       dsp->conf = NULL;
+                       dsp->member = NULL;
+                       kfree(member);
+                       return 0;
+               }
+       }
+       printk(KERN_WARNING
+           "%s: dsp is not present in its own conf_meber list.\n",
+           __func__);
+
+       return -EINVAL;
+}
+
+
+/*
+ * new conference
+ */
+static struct dsp_conf
+*dsp_cmx_new_conf(u32 id)
+{
+       struct dsp_conf *conf;
+
+       if (!id) {
+               printk(KERN_WARNING "%s: id is 0.\n",
+                   __func__);
+               return NULL;
+       }
+
+       conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC);
+       if (!conf) {
+               printk(KERN_ERR "kmalloc struct dsp_conf failed\n");
+               return NULL;
+       }
+       INIT_LIST_HEAD(&conf->mlist);
+       conf->id = id;
+
+       list_add_tail(&conf->list, &conf_ilist);
+
+       return conf;
+}
+
+
+/*
+ * del conference
+ */
+int
+dsp_cmx_del_conf(struct dsp_conf *conf)
+{
+       if (!conf) {
+               printk(KERN_WARNING "%s: conf is null.\n",
+                   __func__);
+               return -EINVAL;
+       }
+
+       if (!list_empty(&conf->mlist)) {
+               printk(KERN_WARNING "%s: conf not empty.\n",
+                   __func__);
+               return -EINVAL;
+       }
+       list_del(&conf->list);
+       kfree(conf);
+
+       return 0;
+}
+
+
+/*
+ * send HW message to hfc card
+ */
+static void
+dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2,
+    u32 param3, u32 param4)
+{
+       struct mISDN_ctrl_req cq;
+
+       memset(&cq, 0, sizeof(cq));
+       cq.op = message;
+       cq.p1 = param1 | (param2 << 8);
+       cq.p2 = param3 | (param4 << 8);
+       if (dsp->ch.peer)
+               dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq);
+}
+
+
+/*
+ * do hardware update and set the software/hardware flag
+ *
+ * either a conference or a dsp instance can be given
+ * if only dsp instance is given, the instance is not associated with a conf
+ * and therefore removed. if a conference is given, the dsp is expected to
+ * be member of that conference.
+ */
+void
+dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp)
+{
+       struct dsp_conf_member  *member, *nextm;
+       struct dsp              *finddsp;
+       int             memb = 0, i, ii, i1, i2;
+       int             freeunits[8];
+       u_char          freeslots[256];
+       int             same_hfc = -1, same_pcm = -1, current_conf = -1,
+           all_conf = 1;
+
+       /* dsp gets updated (no conf) */
+       if (!conf) {
+               if (!dsp)
+                       return;
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG "%s checking dsp %s\n",
+                           __func__, dsp->name);
+one_member:
+               /* remove HFC conference if enabled */
+               if (dsp->hfc_conf >= 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s removing %s from HFC conf %d "
+                                   "because dsp is split\n", __func__,
+                                   dsp->name, dsp->hfc_conf);
+                       dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT,
+                           0, 0, 0, 0);
+                       dsp->hfc_conf = -1;
+               }
+               /* process hw echo */
+               if (dsp->features.pcm_banks < 1)
+                       return;
+               if (!dsp->echo) {
+                       /* NO ECHO: remove PCM slot if assigned */
+                       if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG "%s removing %s from"
+                                           " PCM slot %d (TX) %d (RX) because"
+                                           " dsp is split (no echo)\n",
+                                           __func__, dsp->name,
+                                           dsp->pcm_slot_tx, dsp->pcm_slot_rx);
+                               dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC,
+                                   0, 0, 0, 0);
+                               dsp->pcm_slot_tx = -1;
+                               dsp->pcm_bank_tx = -1;
+                               dsp->pcm_slot_rx = -1;
+                               dsp->pcm_bank_rx = -1;
+                       }
+                       return;
+               }
+               /* ECHO: already echo */
+               if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 &&
+                   dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2)
+                       return;
+               /* ECHO: if slot already assigned */
+               if (dsp->pcm_slot_tx >= 0) {
+                       dsp->pcm_slot_rx = dsp->pcm_slot_tx;
+                       dsp->pcm_bank_tx = 2; /* 2 means loop */
+                       dsp->pcm_bank_rx = 2;
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s refresh %s for echo using slot %d\n",
+                                   __func__, dsp->name,
+                                   dsp->pcm_slot_tx);
+                       dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+                       return;
+               }
+               /* ECHO: find slot */
+               dsp->pcm_slot_tx = -1;
+               dsp->pcm_slot_rx = -1;
+               memset(freeslots, 1, sizeof(freeslots));
+               list_for_each_entry(finddsp, &dsp_ilist, list) {
+                       if (finddsp->features.pcm_id == dsp->features.pcm_id) {
+                               if (finddsp->pcm_slot_rx >= 0 &&
+                                   finddsp->pcm_slot_rx < sizeof(freeslots))
+                                       freeslots[finddsp->pcm_slot_tx] = 0;
+                               if (finddsp->pcm_slot_tx >= 0 &&
+                                   finddsp->pcm_slot_tx < sizeof(freeslots))
+                                       freeslots[finddsp->pcm_slot_rx] = 0;
+                       }
+               }
+               i = 0;
+               ii = dsp->features.pcm_slots;
+               while (i < ii) {
+                       if (freeslots[i])
+                               break;
+                       i++;
+               }
+               if (i == ii) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s no slot available for echo\n",
+                                   __func__);
+                       /* no more slots available */
+                       return;
+               }
+               /* assign free slot */
+               dsp->pcm_slot_tx = i;
+               dsp->pcm_slot_rx = i;
+               dsp->pcm_bank_tx = 2; /* loop */
+               dsp->pcm_bank_rx = 2;
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s assign echo for %s using slot %d\n",
+                           __func__, dsp->name, dsp->pcm_slot_tx);
+               dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+                   dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+               return;
+       }
+
+       /* conf gets updated (all members) */
+       if (dsp_debug & DEBUG_DSP_CMX)
+               printk(KERN_DEBUG "%s checking conference %d\n",
+                   __func__, conf->id);
+
+       if (list_empty(&conf->mlist)) {
+               printk(KERN_ERR "%s: conference whithout members\n",
+                   __func__);
+               return;
+       }
+       member = list_entry(conf->mlist.next, struct dsp_conf_member, list);
+       same_hfc = member->dsp->features.hfc_id;
+       same_pcm = member->dsp->features.pcm_id;
+       /* check all members in our conference */
+       list_for_each_entry(member, &conf->mlist, list) {
+               /* check if member uses mixing */
+               if (member->dsp->tx_mix) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_mix is turned on\n", __func__,
+                                   member->dsp->name);
+conf_software:
+                       list_for_each_entry(member, &conf->mlist, list) {
+                               dsp = member->dsp;
+                               /* remove HFC conference if enabled */
+                               if (dsp->hfc_conf >= 0) {
+                                       if (dsp_debug & DEBUG_DSP_CMX)
+                                               printk(KERN_DEBUG
+                                                   "%s removing %s from HFC "
+                                                   "conf %d because not "
+                                                   "possible with hardware\n",
+                                                   __func__,
+                                                   dsp->name,
+                                                   dsp->hfc_conf);
+                                       dsp_cmx_hw_message(dsp,
+                                           MISDN_CTRL_HFC_CONF_SPLIT,
+                                           0, 0, 0, 0);
+                                       dsp->hfc_conf = -1;
+                               }
+                               /* remove PCM slot if assigned */
+                               if (dsp->pcm_slot_tx >= 0 ||
+                                   dsp->pcm_slot_rx >= 0) {
+                                       if (dsp_debug & DEBUG_DSP_CMX)
+                                               printk(KERN_DEBUG "%s removing "
+                                                   "%s from PCM slot %d (TX)"
+                                                   " slot %d (RX) because not"
+                                                   " possible with hardware\n",
+                                                   __func__,
+                                                   dsp->name,
+                                                   dsp->pcm_slot_tx,
+                                                   dsp->pcm_slot_rx);
+                                       dsp_cmx_hw_message(dsp,
+                                           MISDN_CTRL_HFC_PCM_DISC,
+                                           0, 0, 0, 0);
+                                       dsp->pcm_slot_tx = -1;
+                                       dsp->pcm_bank_tx = -1;
+                                       dsp->pcm_slot_rx = -1;
+                                       dsp->pcm_bank_rx = -1;
+                               }
+                       }
+                       conf->hardware = 0;
+                       conf->software = 1;
+                       return;
+               }
+               /* check if member has echo turned on */
+               if (member->dsp->echo) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "echo is turned on\n", __func__,
+                                   member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if member has tx_mix turned on */
+               if (member->dsp->tx_mix) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_mix is turned on\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if member changes volume at an not suppoted level */
+               if (member->dsp->tx_volume) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_volume is changed\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               if (member->dsp->rx_volume) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "rx_volume is changed\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if tx-data turned on */
+               if (member->dsp->tx_data) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_data is turned on\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if pipeline exists */
+               if (member->dsp->pipeline.inuse) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "pipeline exists\n", __func__,
+                                   member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if encryption is enabled */
+               if (member->dsp->bf_enable) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG "%s dsp %s cannot form a "
+                                   "conf, because encryption is enabled\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if member is on a card with PCM support */
+               if (member->dsp->features.pcm_id < 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "dsp has no PCM bus\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if relations are on the same PCM bus */
+               if (member->dsp->features.pcm_id != same_pcm) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "dsp is on a different PCM bus than the "
+                                   "first dsp\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* determine if members are on the same hfc chip */
+               if (same_hfc != member->dsp->features.hfc_id)
+                       same_hfc = -1;
+               /* if there are members already in a conference */
+               if (current_conf < 0 && member->dsp->hfc_conf >= 0)
+                       current_conf = member->dsp->hfc_conf;
+               /* if any member is not in a conference */
+               if (member->dsp->hfc_conf < 0)
+                       all_conf = 0;
+
+               memb++;
+       }
+
+       /* if no member, this is an error */
+       if (memb < 1)
+               return;
+
+       /* one member */
+       if (memb == 1) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s conf %d cannot form a HW conference, "
+                           "because dsp is alone\n", __func__, conf->id);
+               conf->hardware = 0;
+               conf->software = 0;
+               member = list_entry(conf->mlist.next, struct dsp_conf_member,
+                       list);
+               dsp = member->dsp;
+               goto one_member;
+       }
+
+       /*
+        * ok, now we are sure that all members are on the same pcm.
+        * now we will see if we have only two members, so we can do
+        * crossconnections, which don't have any limitations.
+        */
+
+       /* if we have only two members */
+       if (memb == 2) {
+               member = list_entry(conf->mlist.next, struct dsp_conf_member,
+                       list);
+               nextm = list_entry(member->list.next, struct dsp_conf_member,
+                       list);
+               /* remove HFC conference if enabled */
+               if (member->dsp->hfc_conf >= 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s removing %s from HFC conf %d because "
+                                   "two parties require only a PCM slot\n",
+                                   __func__, member->dsp->name,
+                                   member->dsp->hfc_conf);
+                       dsp_cmx_hw_message(member->dsp,
+                           MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
+                       member->dsp->hfc_conf = -1;
+               }
+               if (nextm->dsp->hfc_conf >= 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s removing %s from HFC conf %d because "
+                                   "two parties require only a PCM slot\n",
+                                   __func__, nextm->dsp->name,
+                                   nextm->dsp->hfc_conf);
+                       dsp_cmx_hw_message(nextm->dsp,
+                           MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
+                       nextm->dsp->hfc_conf = -1;
+               }
+               /* if members have two banks (and not on the same chip) */
+               if (member->dsp->features.pcm_banks > 1 &&
+                   nextm->dsp->features.pcm_banks > 1 &&
+                   member->dsp->features.hfc_id !=
+                   nextm->dsp->features.hfc_id) {
+                       /* if both members have same slots with crossed banks */
+                       if (member->dsp->pcm_slot_tx >= 0 &&
+                           member->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx >= 0 &&
+                           nextm->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx ==
+                           member->dsp->pcm_slot_rx &&
+                           nextm->dsp->pcm_slot_rx ==
+                           member->dsp->pcm_slot_tx &&
+                           nextm->dsp->pcm_slot_tx ==
+                           member->dsp->pcm_slot_tx &&
+                           member->dsp->pcm_bank_tx !=
+                           member->dsp->pcm_bank_rx &&
+                           nextm->dsp->pcm_bank_tx !=
+                           nextm->dsp->pcm_bank_rx) {
+                               /* all members have same slot */
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s dsp %s & %s stay joined on "
+                                           "PCM slot %d bank %d (TX) bank %d "
+                                           "(RX) (on different chips)\n",
+                                           __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name,
+                                           member->dsp->pcm_slot_tx,
+                                           member->dsp->pcm_bank_tx,
+                                           member->dsp->pcm_bank_rx);
+                               conf->hardware = 0;
+                               conf->software = 1;
+                               return;
+                       }
+                       /* find a new slot */
+                       memset(freeslots, 1, sizeof(freeslots));
+                       list_for_each_entry(dsp, &dsp_ilist, list) {
+                               if (dsp != member->dsp &&
+                                   dsp != nextm->dsp &&
+                                   member->dsp->features.pcm_id ==
+                                   dsp->features.pcm_id) {
+                                       if (dsp->pcm_slot_rx >= 0 &&
+                                           dsp->pcm_slot_rx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_tx] = 0;
+                                       if (dsp->pcm_slot_tx >= 0 &&
+                                           dsp->pcm_slot_tx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_rx] = 0;
+                               }
+                       }
+                       i = 0;
+                       ii = member->dsp->features.pcm_slots;
+                       while (i < ii) {
+                               if (freeslots[i])
+                                       break;
+                               i++;
+                       }
+                       if (i == ii) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s no slot available for "
+                                           "%s & %s\n", __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name);
+                               /* no more slots available */
+                               goto conf_software;
+                       }
+                       /* assign free slot */
+                       member->dsp->pcm_slot_tx = i;
+                       member->dsp->pcm_slot_rx = i;
+                       nextm->dsp->pcm_slot_tx = i;
+                       nextm->dsp->pcm_slot_rx = i;
+                       member->dsp->pcm_bank_rx = 0;
+                       member->dsp->pcm_bank_tx = 1;
+                       nextm->dsp->pcm_bank_rx = 1;
+                       nextm->dsp->pcm_bank_tx = 0;
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s adding %s & %s to new PCM slot %d "
+                                   "(TX and RX on different chips) because "
+                                   "both members have not same slots\n",
+                                   __func__,
+                                   member->dsp->name,
+                                   nextm->dsp->name,
+                                   member->dsp->pcm_slot_tx);
+                       dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
+                           member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
+                       dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
+                           nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
+                       conf->hardware = 1;
+                       conf->software = 0;
+                       return;
+               /* if members have one bank (or on the same chip) */
+               } else {
+                       /* if both members have different crossed slots */
+                       if (member->dsp->pcm_slot_tx >= 0 &&
+                           member->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx >= 0 &&
+                           nextm->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx ==
+                           member->dsp->pcm_slot_rx &&
+                           nextm->dsp->pcm_slot_rx ==
+                           member->dsp->pcm_slot_tx &&
+                           member->dsp->pcm_slot_tx !=
+                           member->dsp->pcm_slot_rx &&
+                           member->dsp->pcm_bank_tx == 0 &&
+                           member->dsp->pcm_bank_rx == 0 &&
+                           nextm->dsp->pcm_bank_tx == 0 &&
+                           nextm->dsp->pcm_bank_rx == 0) {
+                               /* all members have same slot */
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s dsp %s & %s stay joined on PCM "
+                                           "slot %d (TX) %d (RX) on same chip "
+                                           "or one bank PCM)\n", __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name,
+                                           member->dsp->pcm_slot_tx,
+                                           member->dsp->pcm_slot_rx);
+                               conf->hardware = 0;
+                               conf->software = 1;
+                               return;
+                       }
+                       /* find two new slot */
+                       memset(freeslots, 1, sizeof(freeslots));
+                       list_for_each_entry(dsp, &dsp_ilist, list) {
+                               if (dsp != member->dsp &&
+                                   dsp != nextm->dsp &&
+                                   member->dsp->features.pcm_id ==
+                                   dsp->features.pcm_id) {
+                                       if (dsp->pcm_slot_rx >= 0 &&
+                                           dsp->pcm_slot_rx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_tx] = 0;
+                                       if (dsp->pcm_slot_tx >= 0 &&
+                                           dsp->pcm_slot_tx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_rx] = 0;
+                               }
+                       }
+                       i1 = 0;
+                       ii = member->dsp->features.pcm_slots;
+                       while (i1 < ii) {
+                               if (freeslots[i1])
+                                       break;
+                               i1++;
+                       }
+                       if (i1 == ii) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s no slot available "
+                                           "for %s & %s\n", __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name);
+                               /* no more slots available */
+                               goto conf_software;
+                       }
+                       i2 = i1+1;
+                       while (i2 < ii) {
+                               if (freeslots[i2])
+                                       break;
+                               i2++;
+                       }
+                       if (i2 == ii) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s no slot available "
+                                           "for %s & %s\n",
+                                           __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name);
+                               /* no more slots available */
+                               goto conf_software;
+                       }
+                       /* assign free slots */
+                       member->dsp->pcm_slot_tx = i1;
+                       member->dsp->pcm_slot_rx = i2;
+                       nextm->dsp->pcm_slot_tx = i2;
+                       nextm->dsp->pcm_slot_rx = i1;
+                       member->dsp->pcm_bank_rx = 0;
+                       member->dsp->pcm_bank_tx = 0;
+                       nextm->dsp->pcm_bank_rx = 0;
+                       nextm->dsp->pcm_bank_tx = 0;
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s adding %s & %s to new PCM slot %d "
+                                   "(TX) %d (RX) on same chip or one bank "
+                                   "PCM, because both members have not "
+                                   "crossed slots\n", __func__,
+                                   member->dsp->name,
+                                   nextm->dsp->name,
+                                   member->dsp->pcm_slot_tx,
+                                   member->dsp->pcm_slot_rx);
+                       dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
+                           member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
+                       dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
+                           nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
+                       conf->hardware = 1;
+                       conf->software = 0;
+                       return;
+               }
+       }
+
+       /*
+        * if we have more than two, we may check if we have a conference
+        * unit available on the chip. also all members must be on the same
+        */
+
+       /* if not the same HFC chip */
+       if (same_hfc < 0) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s conference %d cannot be formed, because "
+                           "members are on different chips or not "
+                           "on HFC chip\n",
+                           __func__, conf->id);
+               goto conf_software;
+       }
+
+       /* for more than two members.. */
+
+       /* in case of hdlc, we change to software */
+       if (dsp->hdlc)
+               goto conf_software;
+
+       /* if all members already have the same conference */
+       if (all_conf)
+               return;
+
+       /*
+        * if there is an existing conference, but not all members have joined
+        */
+       if (current_conf >= 0) {
+join_members:
+               list_for_each_entry(member, &conf->mlist, list) {
+                       /* join to current conference */
+                       if (member->dsp->hfc_conf == current_conf)
+                               continue;
+                       /* get a free timeslot first */
+                       memset(freeslots, 1, sizeof(freeslots));
+                       list_for_each_entry(dsp, &dsp_ilist, list) {
+                               /*
+                                * not checking current member, because
+                                * slot will be overwritten.
+                                */
+                               if (
+                                   dsp != member->dsp &&
+                               /* dsp must be on the same PCM */
+                                   member->dsp->features.pcm_id ==
+                                   dsp->features.pcm_id) {
+                                       /* dsp must be on a slot */
+                                       if (dsp->pcm_slot_tx >= 0 &&
+                                           dsp->pcm_slot_tx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_tx] = 0;
+                                       if (dsp->pcm_slot_rx >= 0 &&
+                                           dsp->pcm_slot_rx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_rx] = 0;
+                               }
+                       }
+                       i = 0;
+                       ii = member->dsp->features.pcm_slots;
+                       while (i < ii) {
+                               if (freeslots[i])
+                                       break;
+                               i++;
+                       }
+                       if (i == ii) {
+                               /* no more slots available */
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s conference %d cannot be formed,"
+                                           " because no slot free\n",
+                                           __func__, conf->id);
+                               goto conf_software;
+                       }
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s changing dsp %s to HW conference "
+                                   "%d slot %d\n", __func__,
+                                   member->dsp->name, current_conf, i);
+                       /* assign free slot & set PCM & join conf */
+                       member->dsp->pcm_slot_tx = i;
+                       member->dsp->pcm_slot_rx = i;
+                       member->dsp->pcm_bank_tx = 2; /* loop */
+                       member->dsp->pcm_bank_rx = 2;
+                       member->dsp->hfc_conf = current_conf;
+                       dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           i, 2, i, 2);
+                       dsp_cmx_hw_message(member->dsp,
+                           MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0);
+               }
+               return;
+       }
+
+       /*
+        * no member is in a conference yet, so we find a free one
+        */
+       memset(freeunits, 1, sizeof(freeunits));
+       list_for_each_entry(dsp, &dsp_ilist, list) {
+               /* dsp must be on the same chip */
+               if (dsp->features.hfc_id == same_hfc &&
+                   /* dsp must have joined a HW conference */
+                   dsp->hfc_conf >= 0 &&
+                   /* slot must be within range */
+                   dsp->hfc_conf < 8)
+                       freeunits[dsp->hfc_conf] = 0;
+       }
+       i = 0;
+       ii = 8;
+       while (i < ii) {
+               if (freeunits[i])
+                       break;
+               i++;
+       }
+       if (i == ii) {
+               /* no more conferences available */
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s conference %d cannot be formed, because "
+                           "no conference number free\n",
+                           __func__, conf->id);
+               goto conf_software;
+       }
+       /* join all members */
+       current_conf = i;
+       goto join_members;
+}
+
+
+/*
+ * conf_id != 0: join or change conference
+ * conf_id == 0: split from conference if not already
+ */
+int
+dsp_cmx_conf(struct dsp *dsp, u32 conf_id)
+{
+       int err;
+       struct dsp_conf *conf;
+       struct dsp_conf_member  *member;
+
+       /* if conference doesn't change */
+       if (dsp->conf_id == conf_id)
+               return 0;
+
+       /* first remove us from current conf */
+       if (dsp->conf_id) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG "removing us from conference %d\n",
+                               dsp->conf->id);
+               /* remove us from conf */
+               conf = dsp->conf;
+               err = dsp_cmx_del_conf_member(dsp);
+               if (err)
+                       return err;
+               dsp->conf_id = 0;
+
+               /* update hardware */
+               dsp_cmx_hardware(NULL, dsp);
+
+               /* conf now empty? */
+               if (list_empty(&conf->mlist)) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "conference is empty, so we remove it.\n");
+                       err = dsp_cmx_del_conf(conf);
+                       if (err)
+                               return err;
+               } else {
+                       /* update members left on conf */
+                       dsp_cmx_hardware(conf, NULL);
+               }
+       }
+
+       /* if split */
+       if (!conf_id)
+               return 0;
+
+       /* now add us to conf */
+       if (dsp_debug & DEBUG_DSP_CMX)
+               printk(KERN_DEBUG "searching conference %d\n",
+                       conf_id);
+       conf = dsp_cmx_search_conf(conf_id);
+       if (!conf) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "conference doesn't exist yet, creating.\n");
+               /* the conference doesn't exist, so we create */
+               conf = dsp_cmx_new_conf(conf_id);
+               if (!conf)
+                       return -EINVAL;
+       } else if (!list_empty(&conf->mlist)) {
+               member = list_entry(conf->mlist.next, struct dsp_conf_member,
+                       list);
+               if (dsp->hdlc && !member->dsp->hdlc) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "cannot join transparent conference.\n");
+                       return -EINVAL;
+               }
+               if (!dsp->hdlc && member->dsp->hdlc) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "cannot join hdlc conference.\n");
+                       return -EINVAL;
+               }
+       }
+       /* add conference member */
+       err = dsp_cmx_add_conf_member(dsp, conf);
+       if (err)
+               return err;
+       dsp->conf_id = conf_id;
+
+       /* if we are alone, we do nothing! */
+       if (list_empty(&conf->mlist)) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "we are alone in this conference, so exit.\n");
+               /* update hardware */
+               dsp_cmx_hardware(NULL, dsp);
+               return 0;
+       }
+
+       /* update members on conf */
+       dsp_cmx_hardware(conf, NULL);
+
+       return 0;
+}
+
+
+/*
+ * audio data is received from card
+ */
+void
+dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
+{
+       u8 *d, *p;
+       int len = skb->len;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       int w, i, ii;
+
+       /* check if we have sompen */
+       if (len < 1)
+               return;
+
+       /* half of the buffer should be larger than maximum packet size */
+       if (len >= CMX_BUFF_HALF) {
+               printk(KERN_ERR
+                   "%s line %d: packet from card is too large (%d bytes). "
+                   "please make card send smaller packets OR increase "
+                   "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len);
+               return;
+       }
+
+       /*
+        * initialize pointers if not already -
+        * also add delay if requested by PH_SIGNAL
+        */
+       if (dsp->rx_init) {
+               dsp->rx_init = 0;
+               if (dsp->features.unordered) {
+                       dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+                       dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+                               & CMX_BUFF_MASK;
+               } else {
+                       dsp->rx_R = 0;
+                       dsp->rx_W = dsp->cmx_delay;
+               }
+       }
+       /* if frame contains time code, write directly */
+       if (dsp->features.unordered) {
+               dsp->rx_W = (hh->id & CMX_BUFF_MASK);
+               /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */
+       }
+       /*
+        * if we underrun (or maybe overrun),
+        * we set our new read pointer, and write silence to buffer
+        */
+       if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
+                           "maximum delay), adjusting read pointer! "
+                           "(inst %s)\n", (u_long)dsp, dsp->name);
+               /* flush buffer */
+               if (dsp->features.unordered) {
+                       dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+                       dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+                               & CMX_BUFF_MASK;
+               } else {
+                       dsp->rx_R = 0;
+                       dsp->rx_W = dsp->cmx_delay;
+               }
+               memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+       }
+       /* if we have reached double delay, jump back to middle */
+       if (dsp->cmx_delay)
+               if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >=
+                   (dsp->cmx_delay << 1)) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "cmx_receive(dsp=%lx): OVERRUN (because "
+                                   "twice the delay is reached), adjusting "
+                                   "read pointer! (inst %s)\n",
+                                   (u_long)dsp, dsp->name);
+               /* flush buffer */
+               if (dsp->features.unordered) {
+                       dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+                       dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+                               & CMX_BUFF_MASK;
+               } else {
+                       dsp->rx_R = 0;
+                       dsp->rx_W = dsp->cmx_delay;
+               }
+               memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+       }
+
+       /* show where to write */
+#ifdef CMX_DEBUG
+       printk(KERN_DEBUG
+           "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n",
+           (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name);
+#endif
+
+       /* write data into rx_buffer */
+       p = skb->data;
+       d = dsp->rx_buff;
+       w = dsp->rx_W;
+       i = 0;
+       ii = len;
+       while (i < ii) {
+               d[w++ & CMX_BUFF_MASK] = *p++;
+               i++;
+       }
+
+       /* increase write-pointer */
+       dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
+}
+
+
+/*
+ * send (mixed) audio data to card and control jitter
+ */
+static void
+dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
+{
+       struct dsp_conf *conf = dsp->conf;
+       struct dsp *member, *other;
+       register s32 sample;
+       u8 *d, *p, *q, *o_q;
+       struct sk_buff *nskb, *txskb;
+       int r, rr, t, tt, o_r, o_rr;
+       int preload = 0;
+       struct mISDNhead *hh, *thh;
+
+       /* don't process if: */
+       if (!dsp->b_active) { /* if not active */
+               dsp->last_tx = 0;
+               return;
+       }
+       if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */
+           dsp->tx_R == dsp->tx_W && /* AND no tx-data */
+           !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */
+               dsp->last_tx = 0;
+               return;
+       }
+
+#ifdef CMX_DEBUG
+       printk(KERN_DEBUG
+           "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n",
+           members, dsp->name, conf, dsp->rx_R, dsp->rx_W);
+#endif
+
+       /* preload if we have delay set */
+       if (dsp->cmx_delay && !dsp->last_tx) {
+               preload = len;
+               if (preload < 128)
+                       preload = 128;
+       }
+
+       /* PREPARE RESULT */
+       nskb = mI_alloc_skb(len + preload, GFP_ATOMIC);
+       if (!nskb) {
+               printk(KERN_ERR
+                   "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n",
+                   len + preload);
+               return;
+       }
+       hh = mISDN_HEAD_P(nskb);
+       hh->prim = PH_DATA_REQ;
+       hh->id = 0;
+       dsp->last_tx = 1;
+
+       /* set pointers, indexes and stuff */
+       member = dsp;
+       p = dsp->tx_buff; /* transmit data */
+       q = dsp->rx_buff; /* received data */
+       d = skb_put(nskb, preload + len); /* result */
+       t = dsp->tx_R; /* tx-pointers */
+       tt = dsp->tx_W;
+       r = dsp->rx_R; /* rx-pointers */
+       rr = (r + len) & CMX_BUFF_MASK;
+
+       /* preload with silence, if required */
+       if (preload) {
+               memset(d, dsp_silence, preload);
+               d += preload;
+       }
+
+       /* PROCESS TONES/TX-DATA ONLY */
+       if (dsp->tone.tone && dsp->tone.software) {
+               /* -> copy tone */
+               dsp_tone_copy(dsp, d, len);
+               dsp->tx_R = 0; /* clear tx buffer */
+               dsp->tx_W = 0;
+               goto send_packet;
+       }
+       /* if we have tx-data but do not use mixing */
+       if (!dsp->tx_mix && t != tt) {
+               /* -> send tx-data and continue when not enough */
+#ifdef CMX_TX_DEBUG
+       sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p);
+#endif
+               while (r != rr && t != tt) {
+#ifdef CMX_TX_DEBUG
+                       if (strlen(debugbuf) < 48)
+                           sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]);
+#endif
+                       *d++ = p[t]; /* write tx_buff */
+                       t = (t+1) & CMX_BUFF_MASK;
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+               if (r == rr) {
+                       dsp->tx_R = t;
+#ifdef CMX_TX_DEBUG
+       printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+                       goto send_packet;
+               }
+       }
+#ifdef CMX_TX_DEBUG
+       printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+       /* PROCESS DATA (one member / no conf) */
+       if (!conf || members <= 1) {
+               /* -> if echo is NOT enabled */
+               if (!dsp->echo) {
+                       /* -> send tx-data if available or use 0-volume */
+                       while (r != rr && t != tt) {
+                               *d++ = p[t]; /* write tx_buff */
+                               t = (t+1) & CMX_BUFF_MASK;
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+                       if (r != rr)
+                               memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
+               /* -> if echo is enabled */
+               } else {
+                       /*
+                        * -> mix tx-data with echo if available,
+                        * or use echo only
+                        */
+                       while (r != rr && t != tt) {
+                               *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]];
+                               t = (t+1) & CMX_BUFF_MASK;
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+                       while (r != rr) {
+                               *d++ = q[r]; /* echo */
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+               }
+               dsp->tx_R = t;
+               goto send_packet;
+       }
+       /* PROCESS DATA (two members) */
+#ifdef CMX_CONF_DEBUG
+       if (0) {
+#else
+       if (members == 2) {
+#endif
+               /* "other" becomes other party */
+               other = (list_entry(conf->mlist.next,
+                   struct dsp_conf_member, list))->dsp;
+               if (other == member)
+                       other = (list_entry(conf->mlist.prev,
+                           struct dsp_conf_member, list))->dsp;
+               o_q = other->rx_buff; /* received data */
+               o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
+                       /* end of rx-pointer */
+               o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
+                       /* start rx-pointer at current read position*/
+               /* -> if echo is NOT enabled */
+               if (!dsp->echo) {
+                       /*
+                        * -> copy other member's rx-data,
+                        * if tx-data is available, mix
+                        */
+                       while (o_r != o_rr && t != tt) {
+                               *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
+                               t = (t+1) & CMX_BUFF_MASK;
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+                       while (o_r != o_rr) {
+                               *d++ = o_q[o_r];
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+               /* -> if echo is enabled */
+               } else {
+                       /*
+                        * -> mix other member's rx-data with echo,
+                        * if tx-data is available, mix
+                        */
+                       while (r != rr && t != tt) {
+                               sample = dsp_audio_law_to_s32[p[t]] +
+                                   dsp_audio_law_to_s32[q[r]] +
+                                   dsp_audio_law_to_s32[o_q[o_r]];
+                               if (sample < -32768)
+                                       sample = -32768;
+                               else if (sample > 32767)
+                                       sample = 32767;
+                               *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                                   /* tx-data + rx_data + echo */
+                               t = (t+1) & CMX_BUFF_MASK;
+                               r = (r+1) & CMX_BUFF_MASK;
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+                       while (r != rr) {
+                               *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
+                               r = (r+1) & CMX_BUFF_MASK;
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+               }
+               dsp->tx_R = t;
+               goto send_packet;
+       }
+#ifdef DSP_NEVER_DEFINED
+       }
+#endif
+       /* PROCESS DATA (three or more members) */
+       /* -> if echo is NOT enabled */
+       if (!dsp->echo) {
+               /*
+                * -> substract rx-data from conf-data,
+                * if tx-data is available, mix
+                */
+               while (r != rr && t != tt) {
+                       sample = dsp_audio_law_to_s32[p[t]] + *c++ -
+                           dsp_audio_law_to_s32[q[r]];
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf-rx+tx */
+                       r = (r+1) & CMX_BUFF_MASK;
+                       t = (t+1) & CMX_BUFF_MASK;
+               }
+               while (r != rr) {
+                       sample = *c++ - dsp_audio_law_to_s32[q[r]];
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf-rx */
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+       /* -> if echo is enabled */
+       } else {
+               /*
+                * -> encode conf-data, if tx-data
+                * is available, mix
+                */
+               while (r != rr && t != tt) {
+                       sample = dsp_audio_law_to_s32[p[t]] + *c++;
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf(echo)+tx */
+                       t = (t+1) & CMX_BUFF_MASK;
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+               while (r != rr) {
+                       sample = *c++;
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf(echo) */
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+       }
+       dsp->tx_R = t;
+       goto send_packet;
+
+send_packet:
+       /*
+        * send tx-data if enabled - don't filter,
+        * becuase we want what we send, not what we filtered
+        */
+       if (dsp->tx_data) {
+               /* PREPARE RESULT */
+               txskb = mI_alloc_skb(len, GFP_ATOMIC);
+               if (!txskb) {
+                       printk(KERN_ERR
+                           "FATAL ERROR in mISDN_dsp.o: "
+                           "cannot alloc %d bytes\n", len);
+               } else {
+                       thh = mISDN_HEAD_P(txskb);
+                       thh->prim = DL_DATA_REQ;
+                       thh->id = 0;
+                       memcpy(skb_put(txskb, len), nskb->data+preload, len);
+                       /* queue (trigger later) */
+                       skb_queue_tail(&dsp->sendq, txskb);
+               }
+       }
+       /* adjust volume */
+       if (dsp->tx_volume)
+               dsp_change_volume(nskb, dsp->tx_volume);
+       /* pipeline */
+       if (dsp->pipeline.inuse)
+               dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len);
+       /* crypt */
+       if (dsp->bf_enable)
+               dsp_bf_encrypt(dsp, nskb->data, nskb->len);
+       /* queue and trigger */
+       skb_queue_tail(&dsp->sendq, nskb);
+       schedule_work(&dsp->workq);
+}
+
+u32    samplecount;
+struct timer_list dsp_spl_tl;
+u32    dsp_spl_jiffies; /* calculate the next time to fire */
+u32    dsp_start_jiffies; /* jiffies at the time, the calculation begins */
+struct timeval dsp_start_tv; /* time at start of calculation */
+
+void
+dsp_cmx_send(void *arg)
+{
+       struct dsp_conf *conf;
+       struct dsp_conf_member *member;
+       struct dsp *dsp;
+       int mustmix, members;
+       s32 mixbuffer[MAX_POLL+100], *c;
+       u8 *p, *q;
+       int r, rr;
+       int jittercheck = 0, delay, i;
+       u_long flags;
+       struct timeval tv;
+       u32 elapsed;
+       s16 length;
+
+       /* lock */
+       spin_lock_irqsave(&dsp_lock, flags);
+
+       if (!dsp_start_tv.tv_sec) {
+               do_gettimeofday(&dsp_start_tv);
+               length = dsp_poll;
+       } else {
+               do_gettimeofday(&tv);
+               elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000)
+                   + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125));
+               dsp_start_tv.tv_sec = tv.tv_sec;
+               dsp_start_tv.tv_usec = tv.tv_usec;
+               length = elapsed;
+       }
+       if (length > MAX_POLL + 100)
+               length = MAX_POLL + 100;
+/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n",
+ length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16,
+ dsp_poll_diff & 0xffff);
+ */
+
+       /*
+        * check if jitter needs to be checked
+        * (this is about every second = 8192 samples)
+        */
+       samplecount += length;
+       if ((samplecount & 8191) < length)
+               jittercheck = 1;
+
+       /* loop all members that do not require conference mixing */
+       list_for_each_entry(dsp, &dsp_ilist, list) {
+               if (dsp->hdlc)
+                       continue;
+               conf = dsp->conf;
+               mustmix = 0;
+               members = 0;
+               if (conf) {
+                       members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+                       if (conf->software && members > 1)
+#else
+                       if (conf->software && members > 2)
+#endif
+                               mustmix = 1;
+               }
+
+               /* transmission required */
+               if (!mustmix) {
+                       dsp_cmx_send_member(dsp, length, mixbuffer, members);
+
+                       /*
+                        * unused mixbuffer is given to prevent a
+                        * potential null-pointer-bug
+                        */
+               }
+       }
+
+       /* loop all members that require conference mixing */
+       list_for_each_entry(conf, &conf_ilist, list) {
+               /* count members and check hardware */
+               members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+               if (conf->software && members > 1) {
+#else
+               if (conf->software && members > 2) {
+#endif
+                       /* check for hdlc conf */
+                       member = list_entry(conf->mlist.next,
+                               struct dsp_conf_member, list);
+                       if (member->dsp->hdlc)
+                               continue;
+                       /* mix all data */
+                       memset(mixbuffer, 0, length*sizeof(s32));
+                       list_for_each_entry(member, &conf->mlist, list) {
+                               dsp = member->dsp;
+                               /* get range of data to mix */
+                               c = mixbuffer;
+                               q = dsp->rx_buff;
+                               r = dsp->rx_R;
+                               rr = (r + length) & CMX_BUFF_MASK;
+                               /* add member's data */
+                               while (r != rr) {
+                                       *c++ += dsp_audio_law_to_s32[q[r]];
+                                       r = (r+1) & CMX_BUFF_MASK;
+                               }
+                       }
+
+                       /* process each member */
+                       list_for_each_entry(member, &conf->mlist, list) {
+                               /* transmission */
+                               dsp_cmx_send_member(member->dsp, length,
+                                   mixbuffer, members);
+                       }
+               }
+       }
+
+       /* delete rx-data, increment buffers, change pointers */
+       list_for_each_entry(dsp, &dsp_ilist, list) {
+               if (dsp->hdlc)
+                       continue;
+               p = dsp->rx_buff;
+               q = dsp->tx_buff;
+               r = dsp->rx_R;
+               /* move receive pointer when receiving */
+               if (!dsp->rx_is_off) {
+                       rr = (r + length) & CMX_BUFF_MASK;
+                       /* delete rx-data */
+                       while (r != rr) {
+                               p[r] = dsp_silence;
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+                       /* increment rx-buffer pointer */
+                       dsp->rx_R = r; /* write incremented read pointer */
+               }
+
+               /* check current rx_delay */
+               delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK;
+               if (delay >= CMX_BUFF_HALF)
+                       delay = 0; /* will be the delay before next write */
+               /* check for lower delay */
+               if (delay < dsp->rx_delay[0])
+                       dsp->rx_delay[0] = delay;
+               /* check current tx_delay */
+               delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK;
+               if (delay >= CMX_BUFF_HALF)
+                       delay = 0; /* will be the delay before next write */
+               /* check for lower delay */
+               if (delay < dsp->tx_delay[0])
+                       dsp->tx_delay[0] = delay;
+               if (jittercheck) {
+                       /* find the lowest of all rx_delays */
+                       delay = dsp->rx_delay[0];
+                       i = 1;
+                       while (i < MAX_SECONDS_JITTER_CHECK) {
+                               if (delay > dsp->rx_delay[i])
+                                       delay = dsp->rx_delay[i];
+                               i++;
+                       }
+                       /*
+                        * remove rx_delay only if we have delay AND we
+                        * have not preset cmx_delay
+                        */
+                       if (delay && !dsp->cmx_delay) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s lowest rx_delay of %d bytes for"
+                                           " dsp %s are now removed.\n",
+                                           __func__, delay,
+                                           dsp->name);
+                               r = dsp->rx_R;
+                               rr = (r + delay) & CMX_BUFF_MASK;
+                               /* delete rx-data */
+                               while (r != rr) {
+                                       p[r] = dsp_silence;
+                                       r = (r+1) & CMX_BUFF_MASK;
+                               }
+                               /* increment rx-buffer pointer */
+                               dsp->rx_R = r;
+                                   /* write incremented read pointer */
+                       }
+                       /* find the lowest of all tx_delays */
+                       delay = dsp->tx_delay[0];
+                       i = 1;
+                       while (i < MAX_SECONDS_JITTER_CHECK) {
+                               if (delay > dsp->tx_delay[i])
+                                       delay = dsp->tx_delay[i];
+                               i++;
+                       }
+                       /*
+                        * remove delay only if we have delay AND we
+                        * have enabled tx_dejitter
+                        */
+                       if (delay && dsp->tx_dejitter) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s lowest tx_delay of %d bytes for"
+                                           " dsp %s are now removed.\n",
+                                           __func__, delay,
+                                           dsp->name);
+                               r = dsp->tx_R;
+                               rr = (r + delay) & CMX_BUFF_MASK;
+                               /* delete tx-data */
+                               while (r != rr) {
+                                       q[r] = dsp_silence;
+                                       r = (r+1) & CMX_BUFF_MASK;
+                               }
+                               /* increment rx-buffer pointer */
+                               dsp->tx_R = r;
+                                   /* write incremented read pointer */
+                       }
+                       /* scroll up delays */
+                       i = MAX_SECONDS_JITTER_CHECK - 1;
+                       while (i) {
+                               dsp->rx_delay[i] = dsp->rx_delay[i-1];
+                               dsp->tx_delay[i] = dsp->tx_delay[i-1];
+                               i--;
+                       }
+                       dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+                       dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+               }
+       }
+
+       /* if next event would be in the past ... */
+       if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0)
+               dsp_spl_jiffies = jiffies + 1;
+       else
+               dsp_spl_jiffies += dsp_tics;
+
+       dsp_spl_tl.expires = dsp_spl_jiffies;
+       add_timer(&dsp_spl_tl);
+
+       /* unlock */
+       spin_unlock_irqrestore(&dsp_lock, flags);
+}
+
+/*
+ * audio data is transmitted from upper layer to the dsp
+ */
+void
+dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
+{
+       u_int w, ww;
+       u8 *d, *p;
+       int space; /* todo: , l = skb->len; */
+#ifdef CMX_TX_DEBUG
+       char debugbuf[256] = "";
+#endif
+
+       /* check if there is enough space, and then copy */
+       w = dsp->tx_W;
+       ww = dsp->tx_R;
+       p = dsp->tx_buff;
+       d = skb->data;
+       space = ww-w;
+       if (space <= 0)
+               space += CMX_BUFF_SIZE;
+       /* write-pointer should not overrun nor reach read pointer */
+       if (space-1 < skb->len)
+               /* write to the space we have left */
+               ww = (ww - 1) & CMX_BUFF_MASK;
+       else
+               /* write until all byte are copied */
+               ww = (w + skb->len) & CMX_BUFF_MASK;
+       dsp->tx_W = ww;
+
+       /* show current buffer */
+#ifdef CMX_DEBUG
+       printk(KERN_DEBUG
+           "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n",
+           (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name);
+#endif
+
+       /* copy transmit data to tx-buffer */
+#ifdef CMX_TX_DEBUG
+       sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p);
+#endif
+       while (w != ww) {
+#ifdef CMX_TX_DEBUG
+               if (strlen(debugbuf) < 48)
+                       sprintf(debugbuf+strlen(debugbuf), " %02x", *d);
+#endif
+               p[w] = *d++;
+               w = (w+1) & CMX_BUFF_MASK;
+       }
+#ifdef CMX_TX_DEBUG
+       printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+}
+
+/*
+ * hdlc data is received from card and sent to all members.
+ */
+void
+dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb)
+{
+       struct sk_buff *nskb = NULL;
+       struct dsp_conf_member *member;
+       struct mISDNhead *hh;
+
+       /* not if not active */
+       if (!dsp->b_active)
+               return;
+
+       /* check if we have sompen */
+       if (skb->len < 1)
+               return;
+
+       /* no conf */
+       if (!dsp->conf) {
+               /* in case of hardware (echo) */
+               if (dsp->pcm_slot_tx >= 0)
+                       return;
+               if (dsp->echo)
+                       nskb = skb_clone(skb, GFP_ATOMIC);
+                       if (nskb) {
+                               hh = mISDN_HEAD_P(nskb);
+                               hh->prim = PH_DATA_REQ;
+                               hh->id = 0;
+                               skb_queue_tail(&dsp->sendq, nskb);
+                               schedule_work(&dsp->workq);
+                       }
+               return;
+       }
+       /* in case of hardware conference */
+       if (dsp->conf->hardware)
+               return;
+       list_for_each_entry(member, &dsp->conf->mlist, list) {
+               if (dsp->echo || member->dsp != dsp) {
+                       nskb = skb_clone(skb, GFP_ATOMIC);
+                       if (nskb) {
+                               hh = mISDN_HEAD_P(nskb);
+                               hh->prim = PH_DATA_REQ;
+                               hh->id = 0;
+                               skb_queue_tail(&member->dsp->sendq, nskb);
+                               schedule_work(&member->dsp->workq);
+                       }
+               }
+       }
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
new file mode 100644 (file)
index 0000000..2f10ed8
--- /dev/null
@@ -0,0 +1,1191 @@
+/*
+ * Author       Andreas Eversberg (jolly@eversberg.eu)
+ * Based on source code structure by
+ *             Karsten Keil (keil@isdn4linux.de)
+ *
+ *             This file is (c) under GNU PUBLIC LICENSE
+ *             For changes and modifications please read
+ *             ../../../Documentation/isdn/mISDN.cert
+ *
+ * Thanks to    Karsten Keil (great drivers)
+ *              Cologne Chip (great chips)
+ *
+ * This module does:
+ *             Real-time tone generation
+ *             DTMF detection
+ *             Real-time cross-connection and conferrence
+ *             Compensate jitter due to system load and hardware fault.
+ *             All features are done in kernel space and will be realized
+ *             using hardware, if available and supported by chip set.
+ *             Blowfish encryption/decryption
+ */
+
+/* STRUCTURE:
+ *
+ * The dsp module provides layer 2 for b-channels (64kbit). It provides
+ * transparent audio forwarding with special digital signal processing:
+ *
+ * - (1) generation of tones
+ * - (2) detection of dtmf tones
+ * - (3) crossconnecting and conferences (clocking)
+ * - (4) echo generation for delay test
+ * - (5) volume control
+ * - (6) disable receive data
+ * - (7) pipeline
+ * - (8) encryption/decryption
+ *
+ * Look:
+ *             TX            RX
+ *         ------upper layer------
+ *             |             ^
+ *             |             |(6)
+ *             v             |
+ *       +-----+-------------+-----+
+ *       |(3)(4)                   |
+ *       |           CMX           |
+ *       |                         |
+ *       |           +-------------+
+ *       |           |       ^
+ *       |           |       |
+ *       |+---------+|  +----+----+
+ *       ||(1)      ||  |(2)      |
+ *       ||         ||  |         |
+ *       ||  Tones  ||  |  DTMF   |
+ *       ||         ||  |         |
+ *       ||         ||  |         |
+ *       |+----+----+|  +----+----+
+ *       +-----+-----+       ^
+ *             |             |
+ *             v             |
+ *        +----+----+   +----+----+
+ *        |(5)      |   |(5)      |
+ *        |         |   |         |
+ *        |TX Volume|   |RX Volume|
+ *        |         |   |         |
+ *        |         |   |         |
+ *        +----+----+   +----+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *        +----+-------------+----+
+ *        |(7)                    |
+ *        |                       |
+ *        |  Pipeline Processing  |
+ *        |                       |
+ *        |                       |
+ *        +----+-------------+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *        +----+----+   +----+----+
+ *        |(8)      |   |(8)      |
+ *        |         |   |         |
+ *        | Encrypt |   | Decrypt |
+ *        |         |   |         |
+ *        |         |   |         |
+ *        +----+----+   +----+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *         ------card  layer------
+ *             TX            RX
+ *
+ * Above you can see the logical data flow. If software is used to do the
+ * process, it is actually the real data flow. If hardware is used, data
+ * may not flow, but hardware commands to the card, to provide the data flow
+ * as shown.
+ *
+ * NOTE: The channel must be activated in order to make dsp work, even if
+ * no data flow to the upper layer is intended. Activation can be done
+ * after and before controlling the setting using PH_CONTROL requests.
+ *
+ * DTMF: Will be detected by hardware if possible. It is done before CMX
+ * processing.
+ *
+ * Tones: Will be generated via software if endless looped audio fifos are
+ * not supported by hardware. Tones will override all data from CMX.
+ * It is not required to join a conference to use tones at any time.
+ *
+ * CMX: Is transparent when not used. When it is used, it will do
+ * crossconnections and conferences via software if not possible through
+ * hardware. If hardware capability is available, hardware is used.
+ *
+ * Echo: Is generated by CMX and is used to check performane of hard and
+ * software CMX.
+ *
+ * The CMX has special functions for conferences with one, two and more
+ * members. It will allow different types of data flow. Receive and transmit
+ * data to/form upper layer may be swithed on/off individually without loosing
+ * features of CMX, Tones and DTMF.
+ *
+ * Echo Cancellation: Sometimes we like to cancel echo from the interface.
+ * Note that a VoIP call may not have echo caused by the IP phone. The echo
+ * is generated by the telephone line connected to it. Because the delay
+ * is high, it becomes an echo. RESULT: Echo Cachelation is required if
+ * both echo AND delay is applied to an interface.
+ * Remember that software CMX always generates a more or less delay.
+ *
+ * If all used features can be realized in hardware, and if transmit and/or
+ * receive data ist disabled, the card may not send/receive any data at all.
+ * Not receiving is usefull if only announcements are played. Not sending is
+ * usefull if an answering machine records audio. Not sending and receiving is
+ * usefull during most states of the call. If supported by hardware, tones
+ * will be played without cpu load. Small PBXs and NT-Mode applications will
+ * not need expensive hardware when processing calls.
+ *
+ *
+ * LOCKING:
+ *
+ * When data is received from upper or lower layer (card), the complete dsp
+ * module is locked by a global lock.  This lock MUST lock irq, because it
+ * must lock timer events by DSP poll timer.
+ * When data is ready to be transmitted down, the data is queued and sent
+ * outside lock and timer event.
+ * PH_CONTROL must not change any settings, join or split conference members
+ * during process of data.
+ *
+ * HDLC:
+ *
+ * It works quite the same as transparent, except that HDLC data is forwarded
+ * to all other conference members if no hardware bridging is possible.
+ * Send data will be writte to sendq. Sendq will be sent if confirm is received.
+ * Conference cannot join, if one member is not hdlc.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "core.h"
+#include "dsp.h"
+
+const char *mISDN_dsp_revision = "2.0";
+
+static int debug;
+static int options;
+static int poll;
+static int dtmfthreshold = 100;
+
+MODULE_AUTHOR("Andreas Eversberg");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(options, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR);
+MODULE_LICENSE("GPL");
+
+/*int spinnest = 0;*/
+
+spinlock_t dsp_lock; /* global dsp lock */
+struct list_head dsp_ilist;
+struct list_head conf_ilist;
+int dsp_debug;
+int dsp_options;
+int dsp_poll, dsp_tics;
+
+/* check if rx may be turned off or must be turned on */
+static void
+dsp_rx_off_member(struct dsp *dsp)
+{
+       struct mISDN_ctrl_req   cq;
+       int rx_off = 1;
+
+       if (!dsp->features_rx_off)
+               return;
+
+       /* not disabled */
+       if (!dsp->rx_disabled)
+               rx_off = 0;
+       /* software dtmf */
+       else if (dsp->dtmf.software)
+               rx_off = 0;
+       /* echo in software */
+       else if (dsp->echo && dsp->pcm_slot_tx < 0)
+               rx_off = 0;
+       /* bridge in software */
+       else if (dsp->conf) {
+               if (dsp->conf->software)
+                       rx_off = 0;
+       }
+
+       if (rx_off == dsp->rx_is_off)
+               return;
+
+       if (!dsp->ch.peer) {
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: no peer, no rx_off\n",
+                               __func__);
+               return;
+       }
+       cq.op = MISDN_CTRL_RX_OFF;
+       cq.p1 = rx_off;
+       if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
+               printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+       dsp->rx_is_off = rx_off;
+       if (dsp_debug & DEBUG_DSP_CORE)
+               printk(KERN_DEBUG "%s: %s set rx_off = %d\n",
+                       __func__, dsp->name, rx_off);
+}
+static void
+dsp_rx_off(struct dsp *dsp)
+{
+       struct dsp_conf_member  *member;
+
+       if (dsp_options & DSP_OPT_NOHARDWARE)
+               return;
+
+       /* no conf */
+       if (!dsp->conf) {
+               dsp_rx_off_member(dsp);
+               return;
+       }
+       /* check all members in conf */
+       list_for_each_entry(member, &dsp->conf->mlist, list) {
+               dsp_rx_off_member(member->dsp);
+       }
+}
+
+static int
+dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb)
+{
+       struct          sk_buff *nskb;
+       int ret = 0;
+       int cont;
+       u8 *data;
+       int len;
+
+       if (skb->len < sizeof(int))
+               printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__);
+       cont = *((int *)skb->data);
+       len = skb->len - sizeof(int);
+       data = skb->data + sizeof(int);
+
+       switch (cont) {
+       case DTMF_TONE_START: /* turn on DTMF */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: start dtmf\n", __func__);
+               if (len == sizeof(int)) {
+                       printk(KERN_NOTICE "changing DTMF Threshold "
+                               "to %d\n", *((int *)data));
+                       dsp->dtmf.treshold = (*(int *)data) * 10000;
+               }
+               /* init goertzel */
+               dsp_dtmf_goertzel_init(dsp);
+
+               /* check dtmf hardware */
+               dsp_dtmf_hardware(dsp);
+               break;
+       case DTMF_TONE_STOP: /* turn off DTMF */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: stop dtmf\n", __func__);
+               dsp->dtmf.hardware = 0;
+               dsp->dtmf.software = 0;
+               break;
+       case DSP_CONF_JOIN: /* join / update conference */
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (*((u32 *)data) == 0)
+                       goto conf_split;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: join conference %d\n",
+                               __func__, *((u32 *)data));
+               ret = dsp_cmx_conf(dsp, *((u32 *)data));
+                       /* dsp_cmx_hardware will also be called here */
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_CONF_SPLIT: /* remove from conference */
+conf_split:
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: release conference\n", __func__);
+               ret = dsp_cmx_conf(dsp, 0);
+                       /* dsp_cmx_hardware will also be called here */
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               dsp_rx_off(dsp);
+               break;
+       case DSP_TONE_PATT_ON: /* play tone */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn tone 0x%x on\n",
+                               __func__, *((int *)skb->data));
+               ret = dsp_tone(dsp, *((int *)data));
+               if (!ret) {
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_rx_off(dsp);
+               }
+               if (!dsp->tone.tone)
+                       goto tone_off;
+               break;
+       case DSP_TONE_PATT_OFF: /* stop tone */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn tone off\n", __func__);
+               dsp_tone(dsp, 0);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               /* reset tx buffers (user space data) */
+tone_off:
+               dsp->rx_W = 0;
+               dsp->rx_R = 0;
+               break;
+       case DSP_VOL_CHANGE_TX: /* change volume */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->tx_volume = *((int *)data);
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: change tx vol to %d\n",
+                               __func__, dsp->tx_volume);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               break;
+       case DSP_VOL_CHANGE_RX: /* change volume */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->rx_volume = *((int *)data);
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: change rx vol to %d\n",
+                               __func__, dsp->tx_volume);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               break;
+       case DSP_ECHO_ON: /* enable echo */
+               dsp->echo = 1; /* soft echo */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_ECHO_OFF: /* disable echo */
+               dsp->echo = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_RECEIVE_ON: /* enable receive to user space */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable receive to user "
+                               "space\n", __func__);
+               dsp->rx_disabled = 0;
+               dsp_rx_off(dsp);
+               break;
+       case DSP_RECEIVE_OFF: /* disable receive to user space */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable receive to "
+                               "user space\n", __func__);
+               dsp->rx_disabled = 1;
+               dsp_rx_off(dsp);
+               break;
+       case DSP_MIX_ON: /* enable mixing of tx data */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable mixing of "
+                               "tx-data with conf mebers\n", __func__);
+               dsp->tx_mix = 1;
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_MIX_OFF: /* disable mixing of tx data */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable mixing of "
+                               "tx-data with conf mebers\n", __func__);
+               dsp->tx_mix = 0;
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_TXDATA_ON: /* enable txdata */
+               dsp->tx_data = 1;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable tx-data\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_TXDATA_OFF: /* disable txdata */
+               dsp->tx_data = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable tx-data\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_DELAY: /* use delay algorithm instead of dynamic
+                          jitter algorithm */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->cmx_delay = (*((int *)data)) << 3;
+                       /* miliseconds to samples */
+               if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1))
+                       /* clip to half of maximum usable buffer
+                       (half of half buffer) */
+                       dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use delay algorithm to "
+                               "compensate jitter (%d samples)\n",
+                               __func__, dsp->cmx_delay);
+               break;
+       case DSP_JITTER: /* use dynamic jitter algorithm instead of
+                   delay algorithm */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->cmx_delay = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use jitter algorithm to "
+                               "compensate jitter\n", __func__);
+               break;
+       case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->tx_dejitter = 1;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use dejitter on TX "
+                               "buffer\n", __func__);
+               break;
+       case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->tx_dejitter = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use TX buffer without "
+                               "dejittering\n", __func__);
+               break;
+       case DSP_PIPELINE_CFG:
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len > 0 && ((char *)data)[len - 1]) {
+                       printk(KERN_DEBUG "%s: pipeline config string "
+                               "is not NULL terminated!\n", __func__);
+                       ret = -EINVAL;
+               } else {
+                       dsp->pipeline.inuse = 1;
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       ret = dsp_pipeline_build(&dsp->pipeline,
+                               len > 0 ? (char *)data : NULL);
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_rx_off(dsp);
+               }
+               break;
+       case DSP_BF_ENABLE_KEY: /* turn blowfish on */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < 4 || len > 56) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn blowfish on (key "
+                               "not shown)\n", __func__);
+               ret = dsp_bf_init(dsp, (u8 *)data, len);
+               /* set new cont */
+               if (!ret)
+                       cont = DSP_BF_ACCEPT;
+               else
+                       cont = DSP_BF_REJECT;
+               /* send indication if it worked to set it */
+               nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY,
+                       sizeof(int), &cont, GFP_ATOMIC);
+               if (nskb) {
+                       if (dsp->up) {
+                               if (dsp->up->send(dsp->up, nskb))
+                                       dev_kfree_skb(nskb);
+                       } else
+                               dev_kfree_skb(nskb);
+               }
+               if (!ret) {
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_dtmf_hardware(dsp);
+                       dsp_rx_off(dsp);
+               }
+               break;
+       case DSP_BF_DISABLE: /* turn blowfish off */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn blowfish off\n", __func__);
+               dsp_bf_cleanup(dsp);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               break;
+       default:
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: ctrl req %x unhandled\n",
+                               __func__, cont);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static void
+get_features(struct mISDNchannel *ch)
+{
+       struct dsp              *dsp = container_of(ch, struct dsp, ch);
+       struct mISDN_ctrl_req   cq;
+
+       if (dsp_options & DSP_OPT_NOHARDWARE)
+               return;
+       if (!ch->peer) {
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: no peer, no features\n",
+                               __func__);
+               return;
+       }
+       memset(&cq, 0, sizeof(cq));
+       cq.op = MISDN_CTRL_GETOP;
+       if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) {
+               printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+       if (cq.op & MISDN_CTRL_RX_OFF)
+               dsp->features_rx_off = 1;
+       if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) {
+               cq.op = MISDN_CTRL_HW_FEATURES;
+               *((u_long *)&cq.p1) = (u_long)&dsp->features;
+               if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) {
+                       printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+                               __func__);
+               }
+       } else
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: features not supported for %s\n",
+                               __func__, dsp->name);
+}
+
+static int
+dsp_function(struct mISDNchannel *ch,  struct sk_buff *skb)
+{
+       struct dsp                      *dsp = container_of(ch, struct dsp, ch);
+       struct mISDNhead        *hh;
+       int                     ret = 0;
+       u8                      *digits;
+       int                     cont;
+       struct                  sk_buff *nskb;
+       u_long                  flags;
+
+       hh = mISDN_HEAD_P(skb);
+       switch (hh->prim) {
+       /* FROM DOWN */
+       case (PH_DATA_CNF):
+               dsp->data_pending = 0;
+               /* trigger next hdlc frame, if any */
+               if (dsp->hdlc) {
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       if (dsp->b_active)
+                               schedule_work(&dsp->workq);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+               }
+               break;
+       case (PH_DATA_IND):
+       case (DL_DATA_IND):
+               if (skb->len < 1) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp->rx_is_off) {
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: rx-data during rx_off"
+                                       " for %s\n",
+                               __func__, dsp->name);
+               }
+               if (dsp->hdlc) {
+                       /* hdlc */
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       dsp_cmx_hdlc(dsp, skb);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+                       if (dsp->rx_disabled) {
+                               /* if receive is not allowed */
+                               break;
+                       }
+                       hh->prim = DL_DATA_IND;
+                       if (dsp->up)
+                               return dsp->up->send(dsp->up, skb);
+                       break;
+               }
+
+               /* decrypt if enabled */
+               if (dsp->bf_enable)
+                       dsp_bf_decrypt(dsp, skb->data, skb->len);
+               /* pipeline */
+               if (dsp->pipeline.inuse)
+                       dsp_pipeline_process_rx(&dsp->pipeline, skb->data,
+                               skb->len);
+               /* change volume if requested */
+               if (dsp->rx_volume)
+                       dsp_change_volume(skb, dsp->rx_volume);
+
+               /* check if dtmf soft decoding is turned on */
+               if (dsp->dtmf.software) {
+                       digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+                               skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
+                       while (*digits) {
+                               if (dsp_debug & DEBUG_DSP_DTMF)
+                                       printk(KERN_DEBUG "%s: digit"
+                                           "(%c) to layer %s\n",
+                                           __func__, *digits, dsp->name);
+                               cont = DTMF_TONE_VAL | *digits;
+                               nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+                                   MISDN_ID_ANY, sizeof(int), &cont,
+                                   GFP_ATOMIC);
+                               if (nskb) {
+                                       if (dsp->up) {
+                                               if (dsp->up->send(
+                                                   dsp->up, nskb))
+                                               dev_kfree_skb(nskb);
+                                       } else
+                                               dev_kfree_skb(nskb);
+                               }
+                               digits++;
+                       }
+               }
+               /* we need to process receive data if software */
+               spin_lock_irqsave(&dsp_lock, flags);
+               if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) {
+                       /* process data from card at cmx */
+                       dsp_cmx_receive(dsp, skb);
+               }
+               spin_unlock_irqrestore(&dsp_lock, flags);
+
+               if (dsp->rx_disabled) {
+                       /* if receive is not allowed */
+                       break;
+               }
+               hh->prim = DL_DATA_IND;
+               if (dsp->up)
+                       return dsp->up->send(dsp->up, skb);
+               break;
+       case (PH_CONTROL_IND):
+               if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+                       printk(KERN_DEBUG "%s: PH_CONTROL INDICATION "
+                               "received: %x (len %d) %s\n", __func__,
+                               hh->id, skb->len, dsp->name);
+               switch (hh->id) {
+               case (DTMF_HFC_COEF): /* getting coefficients */
+                       if (!dsp->dtmf.hardware) {
+                               if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+                                       printk(KERN_DEBUG "%s: ignoring DTMF "
+                                               "coefficients from HFC\n",
+                                               __func__);
+                               break;
+                       }
+                       digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+                               skb->len, 2);
+                       while (*digits) {
+                               int k;
+                               struct sk_buff *nskb;
+                               if (dsp_debug & DEBUG_DSP_DTMF)
+                                       printk(KERN_DEBUG "%s: digit"
+                                           "(%c) to layer %s\n",
+                                           __func__, *digits, dsp->name);
+                               k = *digits | DTMF_TONE_VAL;
+                               nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+                                       MISDN_ID_ANY, sizeof(int), &k,
+                                       GFP_ATOMIC);
+                               if (nskb) {
+                                       if (dsp->up) {
+                                               if (dsp->up->send(
+                                                   dsp->up, nskb))
+                                               dev_kfree_skb(nskb);
+                                       } else
+                                               dev_kfree_skb(nskb);
+                               }
+                               digits++;
+                       }
+                       break;
+               case (HFC_VOL_CHANGE_TX): /* change volume */
+                       if (skb->len != sizeof(int)) {
+                               ret = -EINVAL;
+                               break;
+                       }
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       dsp->tx_volume = *((int *)skb->data);
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: change tx volume to "
+                                       "%d\n", __func__, dsp->tx_volume);
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_dtmf_hardware(dsp);
+                       dsp_rx_off(dsp);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+                       break;
+               default:
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: ctrl ind %x unhandled "
+                                       "%s\n", __func__, hh->id, dsp->name);
+                       ret = -EINVAL;
+               }
+               break;
+       case (PH_ACTIVATE_IND):
+       case (PH_ACTIVATE_CNF):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: b_channel is now active %s\n",
+                               __func__, dsp->name);
+               /* bchannel now active */
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->b_active = 1;
+               dsp->data_pending = 0;
+               dsp->rx_init = 1;
+                       /* rx_W and rx_R will be adjusted on first frame */
+               dsp->rx_W = 0;
+               dsp->rx_R = 0;
+               memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: done with activation, sending "
+                               "confirm to user space. %s\n", __func__,
+                               dsp->name);
+               /* send activation to upper layer */
+               hh->prim = DL_ESTABLISH_CNF;
+               if (dsp->up)
+                       return dsp->up->send(dsp->up, skb);
+               break;
+       case (PH_DEACTIVATE_IND):
+       case (PH_DEACTIVATE_CNF):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: b_channel is now inactive %s\n",
+                               __func__, dsp->name);
+               /* bchannel now inactive */
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->b_active = 0;
+               dsp->data_pending = 0;
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               hh->prim = DL_RELEASE_CNF;
+               if (dsp->up)
+                       return dsp->up->send(dsp->up, skb);
+               break;
+       /* FROM UP */
+       case (DL_DATA_REQ):
+       case (PH_DATA_REQ):
+               if (skb->len < 1) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp->hdlc) {
+                       /* hdlc */
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       if (dsp->b_active) {
+                               skb_queue_tail(&dsp->sendq, skb);
+                               schedule_work(&dsp->workq);
+                       }
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+                       return 0;
+               }
+               /* send data to tx-buffer (if no tone is played) */
+               if (!dsp->tone.tone) {
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       dsp_cmx_transmit(dsp, skb);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+               }
+               break;
+       case (PH_CONTROL_REQ):
+               spin_lock_irqsave(&dsp_lock, flags);
+               ret = dsp_control_req(dsp, hh, skb);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               break;
+       case (DL_ESTABLISH_REQ):
+       case (PH_ACTIVATE_REQ):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: activating b_channel %s\n",
+                               __func__, dsp->name);
+               if (dsp->dtmf.hardware || dsp->dtmf.software)
+                       dsp_dtmf_goertzel_init(dsp);
+               get_features(ch);
+               /* send ph_activate */
+               hh->prim = PH_ACTIVATE_REQ;
+               if (ch->peer)
+                       return ch->recv(ch->peer, skb);
+               break;
+       case (DL_RELEASE_REQ):
+       case (PH_DEACTIVATE_REQ):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: releasing b_channel %s\n",
+                               __func__, dsp->name);
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->tone.tone = 0;
+               dsp->tone.hardware = 0;
+               dsp->tone.software = 0;
+               if (timer_pending(&dsp->tone.tl))
+                       del_timer(&dsp->tone.tl);
+               if (dsp->conf)
+                       dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be
+                                                called here */
+               skb_queue_purge(&dsp->sendq);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               hh->prim = PH_DEACTIVATE_REQ;
+               if (ch->peer)
+                       return ch->recv(ch->peer, skb);
+               break;
+       default:
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: msg %x unhandled %s\n",
+                               __func__, hh->prim, dsp->name);
+               ret = -EINVAL;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct dsp              *dsp = container_of(ch, struct dsp, ch);
+       u_long          flags;
+       int             err = 0;
+
+       if (debug & DEBUG_DSP_CTRL)
+       printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               break;
+       case CLOSE_CHANNEL:
+               if (dsp->ch.peer)
+                       dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL);
+
+               /* wait until workqueue has finished,
+                * must lock here, or we may hit send-process currently
+                * queueing. */
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->b_active = 0;
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               /* MUST not be locked, because it waits until queue is done. */
+               cancel_work_sync(&dsp->workq);
+               spin_lock_irqsave(&dsp_lock, flags);
+               if (timer_pending(&dsp->tone.tl))
+                       del_timer(&dsp->tone.tl);
+               skb_queue_purge(&dsp->sendq);
+               if (dsp_debug & DEBUG_DSP_CTRL)
+                       printk(KERN_DEBUG "%s: releasing member %s\n",
+                               __func__, dsp->name);
+               dsp->b_active = 0;
+               dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called
+                                        here */
+               dsp_pipeline_destroy(&dsp->pipeline);
+
+               if (dsp_debug & DEBUG_DSP_CTRL)
+                       printk(KERN_DEBUG "%s: remove & destroy object %s\n",
+                               __func__, dsp->name);
+               list_del(&dsp->list);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+
+               if (dsp_debug & DEBUG_DSP_CTRL)
+                       printk(KERN_DEBUG "%s: dsp instance released\n",
+                               __func__);
+               vfree(dsp);
+               module_put(THIS_MODULE);
+               break;
+       }
+       return err;
+}
+
+static void
+dsp_send_bh(struct work_struct *work)
+{
+       struct dsp *dsp = container_of(work, struct dsp, workq);
+       struct sk_buff *skb;
+       struct mISDNhead        *hh;
+
+       if (dsp->hdlc && dsp->data_pending)
+               return; /* wait until data has been acknowledged */
+
+       /* send queued data */
+       while ((skb = skb_dequeue(&dsp->sendq))) {
+               /* in locked date, we must have still data in queue */
+               if (dsp->data_pending) {
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: fifo full %s, this is "
+                                       "no bug!\n", __func__, dsp->name);
+                       /* flush transparent data, if not acked */
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+               hh = mISDN_HEAD_P(skb);
+               if (hh->prim == DL_DATA_REQ) {
+                       /* send packet up */
+                       if (dsp->up) {
+                               if (dsp->up->send(dsp->up, skb))
+                                       dev_kfree_skb(skb);
+                       } else
+                               dev_kfree_skb(skb);
+               } else {
+                       /* send packet down */
+                       if (dsp->ch.peer) {
+                               dsp->data_pending = 1;
+                               if (dsp->ch.recv(dsp->ch.peer, skb)) {
+                                       dev_kfree_skb(skb);
+                                       dsp->data_pending = 0;
+                               }
+                       } else
+                               dev_kfree_skb(skb);
+               }
+       }
+}
+
+static int
+dspcreate(struct channel_req *crq)
+{
+       struct dsp              *ndsp;
+       u_long          flags;
+
+       if (crq->protocol != ISDN_P_B_L2DSP
+        && crq->protocol != ISDN_P_B_L2DSPHDLC)
+               return -EPROTONOSUPPORT;
+       ndsp = vmalloc(sizeof(struct dsp));
+       if (!ndsp) {
+               printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__);
+               return -ENOMEM;
+       }
+       memset(ndsp, 0, sizeof(struct dsp));
+       if (dsp_debug & DEBUG_DSP_CTRL)
+               printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__);
+
+       /* default enabled */
+       INIT_WORK(&ndsp->workq, (void *)dsp_send_bh);
+       skb_queue_head_init(&ndsp->sendq);
+       ndsp->ch.send = dsp_function;
+       ndsp->ch.ctrl = dsp_ctrl;
+       ndsp->up = crq->ch;
+       crq->ch = &ndsp->ch;
+       if (crq->protocol == ISDN_P_B_L2DSP) {
+               crq->protocol = ISDN_P_B_RAW;
+               ndsp->hdlc = 0;
+       } else {
+               crq->protocol = ISDN_P_B_HDLC;
+               ndsp->hdlc = 1;
+       }
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n",
+                       __func__);
+
+       sprintf(ndsp->name, "DSP_C%x(0x%p)",
+               ndsp->up->st->dev->id + 1, ndsp);
+       /* set frame size to start */
+       ndsp->features.hfc_id = -1; /* current PCM id */
+       ndsp->features.pcm_id = -1; /* current PCM id */
+       ndsp->pcm_slot_rx = -1; /* current CPM slot */
+       ndsp->pcm_slot_tx = -1;
+       ndsp->pcm_bank_rx = -1;
+       ndsp->pcm_bank_tx = -1;
+       ndsp->hfc_conf = -1; /* current conference number */
+       /* set tone timer */
+       ndsp->tone.tl.function = (void *)dsp_tone_timeout;
+       ndsp->tone.tl.data = (long) ndsp;
+       init_timer(&ndsp->tone.tl);
+
+       if (dtmfthreshold < 20 || dtmfthreshold > 500)
+               dtmfthreshold = 200;
+       ndsp->dtmf.treshold = dtmfthreshold*10000;
+
+       /* init pipeline append to list */
+       spin_lock_irqsave(&dsp_lock, flags);
+       dsp_pipeline_init(&ndsp->pipeline);
+       list_add_tail(&ndsp->list, &dsp_ilist);
+       spin_unlock_irqrestore(&dsp_lock, flags);
+
+       return 0;
+}
+
+
+static struct Bprotocol DSP = {
+       .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))
+               | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)),
+       .name = "dsp",
+       .create = dspcreate
+};
+
+static int dsp_init(void)
+{
+       int err;
+       int tics;
+
+       printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision);
+
+       dsp_options = options;
+       dsp_debug = debug;
+
+       /* set packet size */
+       dsp_poll = poll;
+       if (dsp_poll) {
+               if (dsp_poll > MAX_POLL) {
+                       printk(KERN_ERR "%s: Wrong poll value (%d), use %d "
+                               "maximum.\n", __func__, poll, MAX_POLL);
+                       err = -EINVAL;
+                       return err;
+               }
+               if (dsp_poll < 8) {
+                       printk(KERN_ERR "%s: Wrong poll value (%d), use 8 "
+                               "minimum.\n", __func__, dsp_poll);
+                       err = -EINVAL;
+                       return err;
+               }
+               dsp_tics = poll * HZ / 8000;
+               if (dsp_tics * 8000 != poll * HZ) {
+                       printk(KERN_INFO "mISDN_dsp: Cannot clock every %d "
+                               "samples (0,125 ms). It is not a multiple of "
+                               "%d HZ.\n", poll, HZ);
+                       err = -EINVAL;
+                       return err;
+               }
+       } else {
+               poll = 8;
+               while (poll <= MAX_POLL) {
+                       tics = poll * HZ / 8000;
+                       if (tics * 8000 == poll * HZ) {
+                               dsp_tics = tics;
+                               dsp_poll = poll;
+                               if (poll >= 64)
+                                       break;
+                       }
+                       poll++;
+               }
+       }
+       if (dsp_poll == 0) {
+               printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel "
+                       "clock that equals exactly the duration of 8-256 "
+                       "samples. (Choose kernel clock speed like 100, 250, "
+                       "300, 1000)\n");
+               err = -EINVAL;
+               return err;
+       }
+       printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals "
+               "%d jiffies.\n", dsp_poll, dsp_tics);
+
+       spin_lock_init(&dsp_lock);
+       INIT_LIST_HEAD(&dsp_ilist);
+       INIT_LIST_HEAD(&conf_ilist);
+
+       /* init conversion tables */
+       dsp_audio_generate_law_tables();
+       dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a;
+       dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:
+               dsp_audio_alaw_to_s32;
+       dsp_audio_generate_s2law_table();
+       dsp_audio_generate_seven();
+       dsp_audio_generate_mix_table();
+       if (dsp_options & DSP_OPT_ULAW)
+               dsp_audio_generate_ulaw_samples();
+       dsp_audio_generate_volume_changes();
+
+       err = dsp_pipeline_module_init();
+       if (err) {
+               printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, "
+                       "error(%d)\n", err);
+               return err;
+       }
+
+       err = mISDN_register_Bprotocol(&DSP);
+       if (err) {
+               printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err);
+               return err;
+       }
+
+       /* set sample timer */
+       dsp_spl_tl.function = (void *)dsp_cmx_send;
+       dsp_spl_tl.data = 0;
+       init_timer(&dsp_spl_tl);
+       dsp_spl_tl.expires = jiffies + dsp_tics;
+       dsp_spl_jiffies = dsp_spl_tl.expires;
+       add_timer(&dsp_spl_tl);
+
+       return 0;
+}
+
+
+static void dsp_cleanup(void)
+{
+       mISDN_unregister_Bprotocol(&DSP);
+
+       if (timer_pending(&dsp_spl_tl))
+               del_timer(&dsp_spl_tl);
+
+       if (!list_empty(&dsp_ilist)) {
+               printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not "
+                       "empty.\n");
+       }
+       if (!list_empty(&conf_ilist)) {
+               printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not "
+                       "all memory freed.\n");
+       }
+
+       dsp_pipeline_module_exit();
+}
+
+module_init(dsp_init);
+module_exit(dsp_cleanup);
+
diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c
new file mode 100644 (file)
index 0000000..efc371c
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * DTMF decoder.
+ *
+ * Copyright            by Andreas Eversberg (jolly@eversberg.eu)
+ *                     based on different decoders such as ISDN4Linux
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+#define NCOEFF            8     /* number of frequencies to be analyzed */
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static u64 cos2pik[NCOEFF] =
+{
+       /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */
+       55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630
+};
+
+/* digit matrix */
+static char dtmf_matrix[4][4] =
+{
+       {'1', '2', '3', 'A'},
+       {'4', '5', '6', 'B'},
+       {'7', '8', '9', 'C'},
+       {'*', '0', '#', 'D'}
+};
+
+/* dtmf detection using goertzel algorithm
+ * init function
+ */
+void dsp_dtmf_goertzel_init(struct dsp *dsp)
+{
+       dsp->dtmf.size = 0;
+       dsp->dtmf.lastwhat = '\0';
+       dsp->dtmf.lastdigit = '\0';
+       dsp->dtmf.count = 0;
+}
+
+/* check for hardware or software features
+ */
+void dsp_dtmf_hardware(struct dsp *dsp)
+{
+       int hardware = 1;
+
+       if (!dsp->features.hfc_dtmf)
+               hardware = 0;
+
+       /* check for volume change */
+       if (dsp->tx_volume) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because tx_volume is changed\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+       if (dsp->rx_volume) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because rx_volume is changed\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+       /* check if encryption is enabled */
+       if (dsp->bf_enable) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because encryption is enabled\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+       /* check if pipeline exists */
+       if (dsp->pipeline.inuse) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because pipeline exists.\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+
+       dsp->dtmf.hardware = hardware;
+       dsp->dtmf.software = !hardware;
+}
+
+
+/*************************************************************
+ * calculate the coefficients of the given sample and decode *
+ *************************************************************/
+
+/* the given sample is decoded. if the sample is not long enough for a
+ * complete frame, the decoding is finished and continued with the next
+ * call of this function.
+ *
+ * the algorithm is very good for detection with a minimum of errors. i
+ * tested it allot. it even works with very short tones (40ms). the only
+ * disadvantage is, that it doesn't work good with different volumes of both
+ * tones. this will happen, if accoustically coupled dialers are used.
+ * it sometimes detects tones during speach, which is normal for decoders.
+ * use sequences to given commands during calls.
+ *
+ * dtmf - points to a structure of the current dtmf state
+ * spl and len - the sample
+ * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder
+ */
+
+u8
+*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt)
+{
+       u8 what;
+       int size;
+       signed short *buf;
+       s32 sk, sk1, sk2;
+       int k, n, i;
+       s32 *hfccoeff;
+       s32 result[NCOEFF], tresh, treshl;
+       int lowgroup, highgroup;
+       s64 cos2pik_;
+
+       dsp->dtmf.digits[0] = '\0';
+
+       /* Note: The function will loop until the buffer has not enough samples
+        * left to decode a full frame.
+        */
+again:
+       /* convert samples */
+       size = dsp->dtmf.size;
+       buf = dsp->dtmf.buffer;
+       switch (fmt) {
+       case 0: /* alaw */
+       case 1: /* ulaw */
+               while (size < DSP_DTMF_NPOINTS && len) {
+                       buf[size++] = dsp_audio_law_to_s32[*data++];
+                       len--;
+               }
+               break;
+
+       case 2: /* HFC coefficients */
+       default:
+               if (len < 64) {
+                       if (len > 0)
+                               printk(KERN_ERR "%s: coefficients have invalid "
+                                       "size. (is=%d < must=%d)\n",
+                                       __func__, len, 64);
+                       return dsp->dtmf.digits;
+               }
+               hfccoeff = (s32 *)data;
+               for (k = 0; k < NCOEFF; k++) {
+                       sk2 = (*hfccoeff++)>>4;
+                       sk = (*hfccoeff++)>>4;
+                       if (sk > 32767 || sk < -32767 || sk2 > 32767
+                           || sk2 < -32767)
+                               printk(KERN_WARNING
+                                       "DTMF-Detection overflow\n");
+                       /* compute |X(k)|**2 */
+                       result[k] =
+                                (sk * sk) -
+                                (((cos2pik[k] * sk) >> 15) * sk2) +
+                                (sk2 * sk2);
+               }
+               data += 64;
+               len -= 64;
+               goto coefficients;
+               break;
+       }
+       dsp->dtmf.size = size;
+
+       if (size < DSP_DTMF_NPOINTS)
+               return dsp->dtmf.digits;
+
+       dsp->dtmf.size = 0;
+
+       /* now we have a full buffer of signed long samples - we do goertzel */
+       for (k = 0; k < NCOEFF; k++) {
+               sk = 0;
+               sk1 = 0;
+               sk2 = 0;
+               buf = dsp->dtmf.buffer;
+               cos2pik_ = cos2pik[k];
+               for (n = 0; n < DSP_DTMF_NPOINTS; n++) {
+                       sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++);
+                       sk2 = sk1;
+                       sk1 = sk;
+               }
+               sk >>= 8;
+               sk2 >>= 8;
+               if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767)
+                       printk(KERN_WARNING "DTMF-Detection overflow\n");
+               /* compute |X(k)|**2 */
+               result[k] =
+                       (sk * sk) -
+                       (((cos2pik[k] * sk) >> 15) * sk2) +
+                       (sk2 * sk2);
+       }
+
+       /* our (squared) coefficients have been calculated, we need to process
+        * them.
+        */
+coefficients:
+       tresh = 0;
+       for (i = 0; i < NCOEFF; i++) {
+               if (result[i] < 0)
+                       result[i] = 0;
+               if (result[i] > dsp->dtmf.treshold) {
+                       if (result[i] > tresh)
+                               tresh = result[i];
+               }
+       }
+
+       if (tresh == 0) {
+               what = 0;
+               goto storedigit;
+       }
+
+       if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+               printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d"
+                       " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n",
+                       result[0]/10000, result[1]/10000, result[2]/10000,
+                       result[3]/10000, result[4]/10000, result[5]/10000,
+                       result[6]/10000, result[7]/10000, tresh/10000,
+                       result[0]/(tresh/100), result[1]/(tresh/100),
+                       result[2]/(tresh/100), result[3]/(tresh/100),
+                       result[4]/(tresh/100), result[5]/(tresh/100),
+                       result[6]/(tresh/100), result[7]/(tresh/100));
+
+       /* calc digit (lowgroup/highgroup) */
+       lowgroup = -1;
+       highgroup = -1;
+       treshl = tresh >> 3;  /* tones which are not on, must be below 9 dB */
+       tresh = tresh >> 2;  /* touchtones must match within 6 dB */
+       for (i = 0; i < NCOEFF; i++) {
+               if (result[i] < treshl)
+                       continue;  /* ignore */
+               if (result[i] < tresh) {
+                       lowgroup = -1;
+                       highgroup = -1;
+                       break;  /* noise inbetween */
+               }
+               /* good level found. This is allowed only one time per group */
+               if (i < NCOEFF/2) {
+                       /* lowgroup */
+                       if (lowgroup >= 0) {
+                               /* Bad. Another tone found. */
+                               lowgroup = -1;
+                               break;
+                       } else
+                               lowgroup = i;
+               } else {
+                       /* higroup */
+                       if (highgroup >= 0) {
+                               /* Bad. Another tone found. */
+                               highgroup = -1;
+                               break;
+                       } else
+                               highgroup = i-(NCOEFF/2);
+               }
+       }
+
+       /* get digit or null */
+       what = 0;
+       if (lowgroup >= 0 && highgroup >= 0)
+               what = dtmf_matrix[lowgroup][highgroup];
+
+storedigit:
+       if (what && (dsp_debug & DEBUG_DSP_DTMF))
+               printk(KERN_DEBUG "DTMF what: %c\n", what);
+
+       if (dsp->dtmf.lastwhat != what)
+               dsp->dtmf.count = 0;
+
+       /* the tone (or no tone) must remain 3 times without change */
+       if (dsp->dtmf.count == 2) {
+               if (dsp->dtmf.lastdigit != what) {
+                       dsp->dtmf.lastdigit = what;
+                       if (what) {
+                               if (dsp_debug & DEBUG_DSP_DTMF)
+                                       printk(KERN_DEBUG "DTMF digit: %c\n",
+                                               what);
+                               if ((strlen(dsp->dtmf.digits)+1)
+                                       < sizeof(dsp->dtmf.digits)) {
+                                       dsp->dtmf.digits[strlen(
+                                               dsp->dtmf.digits)+1] = '\0';
+                                       dsp->dtmf.digits[strlen(
+                                               dsp->dtmf.digits)] = what;
+                               }
+                       }
+               }
+       } else
+               dsp->dtmf.count++;
+
+       dsp->dtmf.lastwhat = what;
+
+       goto again;
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h
new file mode 100644 (file)
index 0000000..8a20af4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * ec_disable_detector.h - A detector which should eventually meet the
+ *                         G.164/G.165 requirements for detecting the
+ *                         2100Hz echo cancellor disable tone.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dsp_biquad.h"
+
+struct ec_disable_detector_state {
+       struct biquad2_state notch;
+       int notch_level;
+       int channel_level;
+       int tone_present;
+       int tone_cycle_duration;
+       int good_cycles;
+       int hit;
+};
+
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static inline void
+echo_can_disable_detector_init(struct ec_disable_detector_state *det)
+{
+    /* Elliptic notch */
+    /* This is actually centred at 2095Hz, but gets the balance we want, due
+       to the asymmetric walls of the notch */
+       biquad2_init(&det->notch,
+               (int32_t) (-0.7600000*32768.0),
+               (int32_t) (-0.1183852*32768.0),
+               (int32_t) (-0.5104039*32768.0),
+               (int32_t) (0.1567596*32768.0),
+               (int32_t) (1.0000000*32768.0));
+
+       det->channel_level = 0;
+       det->notch_level = 0;
+       det->tone_present = FALSE;
+       det->tone_cycle_duration = 0;
+       det->good_cycles = 0;
+       det->hit = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static inline int
+echo_can_disable_detector_update(struct ec_disable_detector_state *det,
+int16_t amp)
+{
+       int16_t notched;
+
+       notched = biquad2(&det->notch, amp);
+       /* Estimate the overall energy in the channel, and the energy in
+          the notch (i.e. overall channel energy - tone energy => noise).
+          Use abs instead of multiply for speed (is it really faster?).
+          Damp the overall energy a little more for a stable result.
+          Damp the notch energy a little less, so we don't damp out the
+          blip every time the phase reverses */
+       det->channel_level += ((abs(amp) - det->channel_level) >> 5);
+       det->notch_level += ((abs(notched) - det->notch_level) >> 4);
+       if (det->channel_level > 280) {
+               /* There is adequate energy in the channel.
+                Is it mostly at 2100Hz? */
+               if (det->notch_level*6 < det->channel_level) {
+                       /* The notch says yes, so we have the tone. */
+                       if (!det->tone_present) {
+                               /* Do we get a kick every 450+-25ms? */
+                               if (det->tone_cycle_duration >= 425*8
+                                       && det->tone_cycle_duration <= 475*8) {
+                                       det->good_cycles++;
+                                       if (det->good_cycles > 2)
+                                       det->hit = TRUE;
+                               }
+                               det->tone_cycle_duration = 0;
+                       }
+                       det->tone_present = TRUE;
+               } else
+                       det->tone_present = FALSE;
+               det->tone_cycle_duration++;
+       } else {
+               det->tone_present = FALSE;
+               det->tone_cycle_duration = 0;
+               det->good_cycles = 0;
+       }
+       return det->hit;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c
new file mode 100644 (file)
index 0000000..eb892d9
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * dsp_hwec.c:
+ * builtin mISDN dsp pipeline element for enabling the hw echocanceller
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mISDNdsp.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+static struct mISDN_dsp_element_arg args[] = {
+       { "deftaps", "128", "Set the number of taps of cancellation." },
+};
+
+static struct mISDN_dsp_element dsp_hwec_p = {
+       .name = "hwec",
+       .new = NULL,
+       .free = NULL,
+       .process_tx = NULL,
+       .process_rx = NULL,
+       .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg),
+       .args = args,
+};
+struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p;
+
+void dsp_hwec_enable(struct dsp *dsp, const char *arg)
+{
+       int deftaps = 128,
+               len;
+       struct mISDN_ctrl_req   cq;
+
+       if (!dsp) {
+               printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n",
+                       __func__);
+               return;
+       }
+
+       if (!arg)
+               goto _do;
+
+       len = strlen(arg);
+       if (!len)
+               goto _do;
+
+       {
+               char _dup[len + 1];
+               char *dup, *tok, *name, *val;
+               int tmp;
+
+               strcpy(_dup, arg);
+               dup = _dup;
+
+               while ((tok = strsep(&dup, ","))) {
+                       if (!strlen(tok))
+                               continue;
+                       name = strsep(&tok, "=");
+                       val = tok;
+
+                       if (!val)
+                               continue;
+
+                       if (!strcmp(name, "deftaps")) {
+                               if (sscanf(val, "%d", &tmp) == 1)
+                                       deftaps = tmp;
+                       }
+               }
+       }
+
+_do:
+       printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n",
+               __func__, deftaps);
+       memset(&cq, 0, sizeof(cq));
+       cq.op = MISDN_CTRL_HFC_ECHOCAN_ON;
+       cq.p1 = deftaps;
+       if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+               printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+}
+
+void dsp_hwec_disable(struct dsp *dsp)
+{
+       struct mISDN_ctrl_req   cq;
+
+       if (!dsp) {
+               printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n",
+                       __func__);
+               return;
+       }
+
+       printk(KERN_DEBUG "%s: disabling hwec\n", __func__);
+       memset(&cq, 0, sizeof(cq));
+       cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF;
+       if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+               printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+}
+
+int dsp_hwec_init(void)
+{
+       mISDN_dsp_element_register(dsp_hwec);
+
+       return 0;
+}
+
+void dsp_hwec_exit(void)
+{
+       mISDN_dsp_element_unregister(dsp_hwec);
+}
+
diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h
new file mode 100644 (file)
index 0000000..eebe80c
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * dsp_hwec.h
+ */
+
+extern struct mISDN_dsp_element *dsp_hwec;
+extern void dsp_hwec_enable(struct dsp *dsp, const char *arg);
+extern void dsp_hwec_disable(struct dsp *dsp);
+extern int  dsp_hwec_init(void);
+extern void dsp_hwec_exit(void);
+
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
new file mode 100644 (file)
index 0000000..850260a
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * dsp_pipeline.c: pipelined audio processing
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+/* uncomment for debugging */
+/*#define PIPELINE_DEBUG*/
+
+struct dsp_pipeline_entry {
+       struct mISDN_dsp_element *elem;
+       void                *p;
+       struct list_head     list;
+};
+struct dsp_element_entry {
+       struct mISDN_dsp_element *elem;
+       struct device        dev;
+       struct list_head     list;
+};
+
+static LIST_HEAD(dsp_elements);
+
+/* sysfs */
+static struct class *elements_class;
+
+static ssize_t
+attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
+       ssize_t len = 0;
+       int i = 0;
+
+       *buf = 0;
+       for (; i < elem->num_args; ++i)
+               len = sprintf(buf, "%sName:        %s\n%s%s%sDescription: %s\n"
+                       "\n", buf,
+                         elem->args[i].name,
+                         elem->args[i].def ? "Default:     " : "",
+                         elem->args[i].def ? elem->args[i].def : "",
+                         elem->args[i].def ? "\n" : "",
+                         elem->args[i].desc);
+
+       return len;
+}
+
+static struct device_attribute element_attributes[] = {
+       __ATTR(args, 0444, attr_show_args, NULL),
+};
+
+int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
+{
+       struct dsp_element_entry *entry;
+       int ret, i;
+
+       if (!elem)
+               return -EINVAL;
+
+       entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->elem = elem;
+
+       entry->dev.class = elements_class;
+       dev_set_drvdata(&entry->dev, elem);
+       snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name);
+       ret = device_register(&entry->dev);
+       if (ret) {
+               printk(KERN_ERR "%s: failed to register %s\n",
+                       __func__, elem->name);
+               goto err1;
+       }
+
+       for (i = 0; i < (sizeof(element_attributes)
+               / sizeof(struct device_attribute)); ++i)
+               ret = device_create_file(&entry->dev,
+                               &element_attributes[i]);
+               if (ret) {
+                       printk(KERN_ERR "%s: failed to create device file\n",
+                               __func__);
+                       goto err2;
+               }
+
+       list_add_tail(&entry->list, &dsp_elements);
+
+       printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
+
+       return 0;
+
+err2:
+       device_unregister(&entry->dev);
+err1:
+       kfree(entry);
+       return ret;
+}
+EXPORT_SYMBOL(mISDN_dsp_element_register);
+
+void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
+{
+       struct dsp_element_entry *entry, *n;
+
+       if (!elem)
+               return;
+
+       list_for_each_entry_safe(entry, n, &dsp_elements, list)
+               if (entry->elem == elem) {
+                       list_del(&entry->list);
+                       device_unregister(&entry->dev);
+                       kfree(entry);
+                       printk(KERN_DEBUG "%s: %s unregistered\n",
+                               __func__, elem->name);
+                       return;
+               }
+       printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
+}
+EXPORT_SYMBOL(mISDN_dsp_element_unregister);
+
+int dsp_pipeline_module_init(void)
+{
+       elements_class = class_create(THIS_MODULE, "dsp_pipeline");
+       if (IS_ERR(elements_class))
+               return PTR_ERR(elements_class);
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
+#endif
+
+       dsp_hwec_init();
+
+       return 0;
+}
+
+void dsp_pipeline_module_exit(void)
+{
+       struct dsp_element_entry *entry, *n;
+
+       dsp_hwec_exit();
+
+       class_destroy(elements_class);
+
+       list_for_each_entry_safe(entry, n, &dsp_elements, list) {
+               list_del(&entry->list);
+               printk(KERN_WARNING "%s: element was still registered: %s\n",
+                       __func__, entry->elem->name);
+               kfree(entry);
+       }
+
+       printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
+}
+
+int dsp_pipeline_init(struct dsp_pipeline *pipeline)
+{
+       if (!pipeline)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&pipeline->list);
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
+#endif
+
+       return 0;
+}
+
+static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+       struct dsp_pipeline_entry *entry, *n;
+
+       list_for_each_entry_safe(entry, n, &pipeline->list, list) {
+               list_del(&entry->list);
+               if (entry->elem == dsp_hwec)
+                       dsp_hwec_disable(container_of(pipeline, struct dsp,
+                               pipeline));
+               else
+                       entry->elem->free(entry->p);
+               kfree(entry);
+       }
+}
+
+void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+
+       if (!pipeline)
+               return;
+
+       _dsp_pipeline_destroy(pipeline);
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
+#endif
+}
+
+int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
+{
+       int len, incomplete = 0, found = 0;
+       char *dup, *tok, *name, *args;
+       struct dsp_element_entry *entry, *n;
+       struct dsp_pipeline_entry *pipeline_entry;
+       struct mISDN_dsp_element *elem;
+
+       if (!pipeline)
+               return -EINVAL;
+
+       if (!list_empty(&pipeline->list))
+               _dsp_pipeline_destroy(pipeline);
+
+       if (!cfg)
+               return 0;
+
+       len = strlen(cfg);
+       if (!len)
+               return 0;
+
+       dup = kmalloc(len + 1, GFP_KERNEL);
+       if (!dup)
+               return 0;
+       strcpy(dup, cfg);
+       while ((tok = strsep(&dup, "|"))) {
+               if (!strlen(tok))
+                       continue;
+               name = strsep(&tok, "(");
+               args = strsep(&tok, ")");
+               if (args && !*args)
+                       args = 0;
+
+               list_for_each_entry_safe(entry, n, &dsp_elements, list)
+                       if (!strcmp(entry->elem->name, name)) {
+                               elem = entry->elem;
+
+                               pipeline_entry = kmalloc(sizeof(struct
+                                       dsp_pipeline_entry), GFP_KERNEL);
+                               if (!pipeline_entry) {
+                                       printk(KERN_DEBUG "%s: failed to add "
+                                           "entry to pipeline: %s (out of "
+                                           "memory)\n", __func__, elem->name);
+                                       incomplete = 1;
+                                       goto _out;
+                               }
+                               pipeline_entry->elem = elem;
+
+                               if (elem == dsp_hwec) {
+                                       /* This is a hack to make the hwec
+                                          available as a pipeline module */
+                                       dsp_hwec_enable(container_of(pipeline,
+                                               struct dsp, pipeline), args);
+                                       list_add_tail(&pipeline_entry->list,
+                                               &pipeline->list);
+                               } else {
+                                       pipeline_entry->p = elem->new(args);
+                                       if (pipeline_entry->p) {
+                                               list_add_tail(&pipeline_entry->
+                                                       list, &pipeline->list);
+#ifdef PIPELINE_DEBUG
+                                               printk(KERN_DEBUG "%s: created "
+                                                   "instance of %s%s%s\n",
+                                                   __func__, name, args ?
+                                                   " with args " : "", args ?
+                                                   args : "");
+#endif
+                                       } else {
+                                               printk(KERN_DEBUG "%s: failed "
+                                                 "to add entry to pipeline: "
+                                                 "%s (new() returned NULL)\n",
+                                                 __func__, elem->name);
+                                               kfree(pipeline_entry);
+                                               incomplete = 1;
+                                       }
+                               }
+                               found = 1;
+                               break;
+                       }
+
+               if (found)
+                       found = 0;
+               else {
+                       printk(KERN_DEBUG "%s: element not found, skipping: "
+                               "%s\n", __func__, name);
+                       incomplete = 1;
+               }
+       }
+
+_out:
+       if (!list_empty(&pipeline->list))
+               pipeline->inuse = 1;
+       else
+               pipeline->inuse = 0;
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
+               __func__, incomplete ? " incomplete" : "", cfg);
+#endif
+       kfree(dup);
+       return 0;
+}
+
+void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+       struct dsp_pipeline_entry *entry;
+
+       if (!pipeline)
+               return;
+
+       list_for_each_entry(entry, &pipeline->list, list)
+               if (entry->elem->process_tx)
+                       entry->elem->process_tx(entry->p, data, len);
+}
+
+void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+       struct dsp_pipeline_entry *entry;
+
+       if (!pipeline)
+               return;
+
+       list_for_each_entry_reverse(entry, &pipeline->list, list)
+               if (entry->elem->process_rx)
+                       entry->elem->process_rx(entry->p, data, len);
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c
new file mode 100644 (file)
index 0000000..23dd0dd
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+
+#define DATA_S sample_silence
+#define SIZE_S (&sizeof_silence)
+#define DATA_GA sample_german_all
+#define SIZE_GA (&sizeof_german_all)
+#define DATA_GO sample_german_old
+#define SIZE_GO (&sizeof_german_old)
+#define DATA_DT sample_american_dialtone
+#define SIZE_DT (&sizeof_american_dialtone)
+#define DATA_RI sample_american_ringing
+#define SIZE_RI (&sizeof_american_ringing)
+#define DATA_BU sample_american_busy
+#define SIZE_BU (&sizeof_american_busy)
+#define DATA_S1 sample_special1
+#define SIZE_S1 (&sizeof_special1)
+#define DATA_S2 sample_special2
+#define SIZE_S2 (&sizeof_special2)
+#define DATA_S3 sample_special3
+#define SIZE_S3 (&sizeof_special3)
+
+/***************/
+/* tones loops */
+/***************/
+
+/* all tones are alaw encoded */
+/* the last sample+1 is in phase with the first sample. the error is low */
+
+static u8 sample_german_all[] = {
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+};
+static u32 sizeof_german_all = sizeof(sample_german_all);
+
+static u8 sample_german_old[] = {
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+};
+static u32 sizeof_german_old = sizeof(sample_german_old);
+
+static u8 sample_american_dialtone[] = {
+       0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c,
+       0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d,
+       0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0,
+       0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67,
+       0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67,
+       0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef,
+       0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8,
+       0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61,
+       0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e,
+       0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30,
+       0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d,
+       0x6d, 0x91, 0x19,
+};
+static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone);
+
+static u8 sample_american_ringing[] = {
+       0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90,
+       0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed,
+       0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c,
+       0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d,
+       0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec,
+       0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11,
+       0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00,
+       0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39,
+       0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6,
+       0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3,
+       0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b,
+       0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f,
+       0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56,
+       0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59,
+       0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30,
+       0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d,
+       0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c,
+       0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd,
+       0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc,
+       0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d,
+       0x4d, 0xbd, 0x0d, 0xad, 0xe1,
+};
+static u32 sizeof_american_ringing = sizeof(sample_american_ringing);
+
+static u8 sample_american_busy[] = {
+       0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66,
+       0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96,
+       0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57,
+       0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f,
+       0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40,
+       0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d,
+       0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c,
+       0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d,
+       0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40,
+       0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7,
+       0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a,
+       0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7,
+       0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40,
+       0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d,
+       0x4d, 0x4d, 0x6d, 0x01,
+};
+static u32 sizeof_american_busy = sizeof(sample_american_busy);
+
+static u8 sample_special1[] = {
+       0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d,
+       0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd,
+       0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd,
+       0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd,
+       0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed,
+       0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41,
+       0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7,
+       0x6d, 0xbd, 0x2d,
+};
+static u32 sizeof_special1 = sizeof(sample_special1);
+
+static u8 sample_special2[] = {
+       0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
+       0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
+       0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
+       0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
+       0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
+       0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
+       0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
+       0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
+       0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
+       0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
+};
+static u32 sizeof_special2 = sizeof(sample_special2);
+
+static u8 sample_special3[] = {
+       0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
+       0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
+       0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
+       0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
+       0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
+       0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
+       0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
+       0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
+       0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
+       0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
+};
+static u32 sizeof_special3 = sizeof(sample_special3);
+
+static u8 sample_silence[] = {
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+};
+static u32 sizeof_silence = sizeof(sample_silence);
+
+struct tones_samples {
+       u32 *len;
+       u8 *data;
+};
+static struct
+tones_samples samples[] = {
+       {&sizeof_german_all, sample_german_all},
+       {&sizeof_german_old, sample_german_old},
+       {&sizeof_american_dialtone, sample_american_dialtone},
+       {&sizeof_american_ringing, sample_american_ringing},
+       {&sizeof_american_busy, sample_american_busy},
+       {&sizeof_special1, sample_special1},
+       {&sizeof_special2, sample_special2},
+       {&sizeof_special3, sample_special3},
+       {NULL, NULL},
+};
+
+/***********************************
+ * generate ulaw from alaw samples *
+ ***********************************/
+
+void
+dsp_audio_generate_ulaw_samples(void)
+{
+       int i, j;
+
+       i = 0;
+       while (samples[i].len) {
+               j = 0;
+               while (j < (*samples[i].len)) {
+                       samples[i].data[j] =
+                               dsp_audio_alaw_to_ulaw[samples[i].data[j]];
+                       j++;
+               }
+               i++;
+       }
+}
+
+
+/****************************
+ * tone sequence definition *
+ ****************************/
+
+struct pattern {
+       int tone;
+       u8 *data[10];
+       u32 *siz[10];
+       u32 seq[10];
+} pattern[] = {
+       {TONE_GERMAN_DIALTONE,
+       {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDDIALTONE,
+       {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_DIALTONE,
+       {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_DIALPBX,
+       {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0},
+       {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDDIALPBX,
+       {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0},
+       {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_DIALPBX,
+       {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0},
+       {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0},
+       {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_RINGING,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDRINGING,
+       {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_RINGING,
+       {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_RINGPBX,
+       {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDRINGPBX,
+       {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_RINGPBX,
+       {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_BUSY,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDBUSY,
+       {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_BUSY,
+       {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_HANGUP,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDHANGUP,
+       {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_HANGUP,
+       {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_SPECIAL_INFO,
+       {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_GASSENBESETZT,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_AUFSCHALTTON,
+       {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
+
+       {0,
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+/******************
+ * copy tone data *
+ ******************/
+
+/* an sk_buff is generated from the number of samples needed.
+ * the count will be changed and may begin from 0 each pattern period.
+ * the clue is to precalculate the pointers and legths to use only one
+ * memcpy per function call, or two memcpy if the tone sequence changes.
+ *
+ * pattern - the type of the pattern
+ * count - the sample from the beginning of the pattern (phase)
+ * len - the number of bytes
+ *
+ * return - the sk_buff with the sample
+ *
+ * if tones has finished (e.g. knocking tone), dsp->tones is turned off
+ */
+void dsp_tone_copy(struct dsp *dsp, u8 *data, int len)
+{
+       int index, count, start, num;
+       struct pattern *pat;
+       struct dsp_tone *tone = &dsp->tone;
+
+       /* if we have no tone, we copy silence */
+       if (!tone->tone) {
+               memset(data, dsp_silence, len);
+               return;
+       }
+
+       /* process pattern */
+       pat = (struct pattern *)tone->pattern;
+               /* points to the current pattern */
+       index = tone->index; /* gives current sequence index */
+       count = tone->count; /* gives current sample */
+
+       /* copy sample */
+       while (len) {
+               /* find sample to start with */
+               while (42) {
+                       /* warp arround */
+                       if (!pat->seq[index]) {
+                               count = 0;
+                               index = 0;
+                       }
+                       /* check if we are currently playing this tone */
+                       if (count < pat->seq[index])
+                               break;
+                       if (dsp_debug & DEBUG_DSP_TONE)
+                               printk(KERN_DEBUG "%s: reaching next sequence "
+                                       "(index=%d)\n", __func__, index);
+                       count -= pat->seq[index];
+                       index++;
+               }
+               /* calculate start and number of samples */
+               start = count % (*(pat->siz[index]));
+               num = len;
+               if (num+count > pat->seq[index])
+                       num = pat->seq[index] - count;
+               if (num+start > (*(pat->siz[index])))
+                       num = (*(pat->siz[index])) - start;
+               /* copy memory */
+               memcpy(data, pat->data[index]+start, num);
+               /* reduce length */
+               data += num;
+               count += num;
+               len -= num;
+       }
+       tone->index = index;
+       tone->count = count;
+
+       /* return sk_buff */
+       return;
+}
+
+
+/*******************************
+ * send HW message to hfc card *
+ *******************************/
+
+static void
+dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len)
+{
+       struct sk_buff *nskb;
+
+       /* unlocking is not required, because we don't expect a response */
+       nskb = _alloc_mISDN_skb(PH_CONTROL_REQ,
+               (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample,
+               GFP_ATOMIC);
+       if (nskb) {
+               if (dsp->ch.peer) {
+                       if (dsp->ch.recv(dsp->ch.peer, nskb))
+                               dev_kfree_skb(nskb);
+               } else
+                       dev_kfree_skb(nskb);
+       }
+}
+
+
+/*****************
+ * timer expires *
+ *****************/
+void
+dsp_tone_timeout(void *arg)
+{
+       struct dsp *dsp = arg;
+       struct dsp_tone *tone = &dsp->tone;
+       struct pattern *pat = (struct pattern *)tone->pattern;
+       int index = tone->index;
+
+       if (!tone->tone)
+               return;
+
+       index++;
+       if (!pat->seq[index])
+               index = 0;
+       tone->index = index;
+
+       /* set next tone */
+       if (pat->data[index] == DATA_S)
+               dsp_tone_hw_message(dsp, 0, 0);
+       else
+               dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
+       /* set timer */
+       init_timer(&tone->tl);
+       tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000;
+       add_timer(&tone->tl);
+}
+
+
+/********************
+ * set/release tone *
+ ********************/
+
+/*
+ * tones are relaized by streaming or by special loop commands if supported
+ * by hardware. when hardware is used, the patterns will be controlled by
+ * timers.
+ */
+int
+dsp_tone(struct dsp *dsp, int tone)
+{
+       struct pattern *pat;
+       int i;
+       struct dsp_tone *tonet = &dsp->tone;
+
+       tonet->software = 0;
+       tonet->hardware = 0;
+
+       /* we turn off the tone */
+       if (!tone) {
+               if (dsp->features.hfc_loops)
+               if (timer_pending(&tonet->tl))
+                       del_timer(&tonet->tl);
+               if (dsp->features.hfc_loops)
+                       dsp_tone_hw_message(dsp, NULL, 0);
+               tonet->tone = 0;
+               return 0;
+       }
+
+       pat = NULL;
+       i = 0;
+       while (pattern[i].tone) {
+               if (pattern[i].tone == tone) {
+                       pat = &pattern[i];
+                       break;
+               }
+               i++;
+       }
+       if (!pat) {
+               printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone);
+               return -EINVAL;
+       }
+       if (dsp_debug & DEBUG_DSP_TONE)
+               printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n",
+                       __func__, tone, 0);
+       tonet->tone = tone;
+       tonet->pattern = pat;
+       tonet->index = 0;
+       tonet->count = 0;
+
+       if (dsp->features.hfc_loops) {
+               tonet->hardware = 1;
+               /* set first tone */
+               dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0]));
+               /* set timer */
+               if (timer_pending(&tonet->tl))
+                       del_timer(&tonet->tl);
+               init_timer(&tonet->tl);
+               tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000;
+               add_timer(&tonet->tl);
+       } else {
+               tonet->software = 1;
+       }
+
+       return 0;
+}
+
+
+
+
+
diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c
new file mode 100644 (file)
index 0000000..b5d6553
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * finite state machine implementation
+ *
+ * Author       Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "fsm.h"
+
+#define FSM_TIMER_DEBUG 0
+
+void
+mISDN_FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+       int i;
+
+       fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count *
+               fsm->event_count, GFP_KERNEL);
+
+       for (i = 0; i < fncount; i++)
+               if ((fnlist[i].state >= fsm->state_count) ||
+                   (fnlist[i].event >= fsm->event_count)) {
+                       printk(KERN_ERR
+                           "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
+                           i, (long)fnlist[i].state, (long)fsm->state_count,
+                           (long)fnlist[i].event, (long)fsm->event_count);
+               } else
+                       fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+                           fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
+}
+EXPORT_SYMBOL(mISDN_FsmNew);
+
+void
+mISDN_FsmFree(struct Fsm *fsm)
+{
+       kfree((void *) fsm->jumpmatrix);
+}
+EXPORT_SYMBOL(mISDN_FsmFree);
+
+int
+mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+       FSMFNPTR r;
+
+       if ((fi->state >= fi->fsm->state_count) ||
+           (event >= fi->fsm->event_count)) {
+               printk(KERN_ERR
+                   "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+                   (long)fi->state, (long)fi->fsm->state_count, event,
+                   (long)fi->fsm->event_count);
+               return 1;
+       }
+       r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+       if (r) {
+               if (fi->debug)
+                       fi->printdebug(fi, "State %s Event %s",
+                               fi->fsm->strState[fi->state],
+                               fi->fsm->strEvent[event]);
+               r(fi, event, arg);
+               return 0;
+       } else {
+               if (fi->debug)
+                       fi->printdebug(fi, "State %s Event %s no action",
+                               fi->fsm->strState[fi->state],
+                               fi->fsm->strEvent[event]);
+               return 1;
+       }
+}
+EXPORT_SYMBOL(mISDN_FsmEvent);
+
+void
+mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
+{
+       fi->state = newstate;
+       if (fi->debug)
+               fi->printdebug(fi, "ChangeState %s",
+                       fi->fsm->strState[newstate]);
+}
+EXPORT_SYMBOL(mISDN_FsmChangeState);
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
+#endif
+       mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+       ft->fi = fi;
+       ft->tl.function = (void *) FsmExpireTimer;
+       ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
+#endif
+       init_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmInitTimer);
+
+void
+mISDN_FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
+                       (long) ft, where);
+#endif
+       del_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmDelTimer);
+
+int
+mISDN_FsmAddTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
+                       (long) ft, millisec, where);
+#endif
+
+       if (timer_pending(&ft->tl)) {
+               if (ft->fi->debug) {
+                       printk(KERN_WARNING
+                               "mISDN_FsmAddTimer: timer already active!\n");
+                       ft->fi->printdebug(ft->fi,
+                               "mISDN_FsmAddTimer already active!");
+               }
+               return -1;
+       }
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_FsmAddTimer);
+
+void
+mISDN_FsmRestartTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
+                       (long) ft, millisec, where);
+#endif
+
+       if (timer_pending(&ft->tl))
+               del_timer(&ft->tl);
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmRestartTimer);
diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h
new file mode 100644 (file)
index 0000000..928f5be
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ * Author       Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MISDN_FSM_H
+#define _MISDN_FSM_H
+
+#include <linux/timer.h>
+
+/* Statemachine */
+
+struct FsmInst;
+
+typedef void (*FSMFNPTR)(struct FsmInst *, int, void *);
+
+struct Fsm {
+       FSMFNPTR *jumpmatrix;
+       int state_count, event_count;
+       char **strEvent, **strState;
+};
+
+struct FsmInst {
+       struct Fsm *fsm;
+       int state;
+       int debug;
+       void *userdata;
+       int userint;
+       void (*printdebug) (struct FsmInst *, char *, ...);
+};
+
+struct FsmNode {
+       int state, event;
+       void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+       struct FsmInst *fi;
+       struct timer_list tl;
+       int event;
+       void *arg;
+};
+
+extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int);
+extern void mISDN_FsmFree(struct Fsm *);
+extern int mISDN_FsmEvent(struct FsmInst *, int , void *);
+extern void mISDN_FsmChangeState(struct FsmInst *, int);
+extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *);
+extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int);
+extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
+extern void mISDN_FsmDelTimer(struct FsmTimer *, int);
+
+#endif
diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
new file mode 100644 (file)
index 0000000..2596fba
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+
+static void
+dchannel_bh(struct work_struct *ws)
+{
+       struct dchannel *dch  = container_of(ws, struct dchannel, workq);
+       struct sk_buff  *skb;
+       int             err;
+
+       if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
+               while ((skb = skb_dequeue(&dch->rqueue))) {
+                       if (likely(dch->dev.D.peer)) {
+                               err = dch->dev.D.recv(dch->dev.D.peer, skb);
+                               if (err)
+                                       dev_kfree_skb(skb);
+                       } else
+                               dev_kfree_skb(skb);
+               }
+       }
+       if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
+               if (dch->phfunc)
+                       dch->phfunc(dch);
+       }
+}
+
+static void
+bchannel_bh(struct work_struct *ws)
+{
+       struct bchannel *bch  = container_of(ws, struct bchannel, workq);
+       struct sk_buff  *skb;
+       int             err;
+
+       if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
+               while ((skb = skb_dequeue(&bch->rqueue))) {
+                       if (bch->rcount >= 64)
+                               printk(KERN_WARNING "B-channel %p receive "
+                                       "queue if full, but empties...\n", bch);
+                       bch->rcount--;
+                       if (likely(bch->ch.peer)) {
+                               err = bch->ch.recv(bch->ch.peer, skb);
+                               if (err)
+                                       dev_kfree_skb(skb);
+                       } else
+                               dev_kfree_skb(skb);
+               }
+       }
+}
+
+int
+mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
+{
+       test_and_set_bit(FLG_HDLC, &ch->Flags);
+       ch->maxlen = maxlen;
+       ch->hw = NULL;
+       ch->rx_skb = NULL;
+       ch->tx_skb = NULL;
+       ch->tx_idx = 0;
+       ch->phfunc = phf;
+       skb_queue_head_init(&ch->squeue);
+       skb_queue_head_init(&ch->rqueue);
+       INIT_LIST_HEAD(&ch->dev.bchannels);
+       INIT_WORK(&ch->workq, dchannel_bh);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_initdchannel);
+
+int
+mISDN_initbchannel(struct bchannel *ch, int maxlen)
+{
+       ch->Flags = 0;
+       ch->maxlen = maxlen;
+       ch->hw = NULL;
+       ch->rx_skb = NULL;
+       ch->tx_skb = NULL;
+       ch->tx_idx = 0;
+       skb_queue_head_init(&ch->rqueue);
+       ch->rcount = 0;
+       ch->next_skb = NULL;
+       INIT_WORK(&ch->workq, bchannel_bh);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_initbchannel);
+
+int
+mISDN_freedchannel(struct dchannel *ch)
+{
+       if (ch->tx_skb) {
+               dev_kfree_skb(ch->tx_skb);
+               ch->tx_skb = NULL;
+       }
+       if (ch->rx_skb) {
+               dev_kfree_skb(ch->rx_skb);
+               ch->rx_skb = NULL;
+       }
+       skb_queue_purge(&ch->squeue);
+       skb_queue_purge(&ch->rqueue);
+       flush_scheduled_work();
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_freedchannel);
+
+int
+mISDN_freebchannel(struct bchannel *ch)
+{
+       if (ch->tx_skb) {
+               dev_kfree_skb(ch->tx_skb);
+               ch->tx_skb = NULL;
+       }
+       if (ch->rx_skb) {
+               dev_kfree_skb(ch->rx_skb);
+               ch->rx_skb = NULL;
+       }
+       if (ch->next_skb) {
+               dev_kfree_skb(ch->next_skb);
+               ch->next_skb = NULL;
+       }
+       skb_queue_purge(&ch->rqueue);
+       ch->rcount = 0;
+       flush_scheduled_work();
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_freebchannel);
+
+static inline u_int
+get_sapi_tei(u_char *p)
+{
+       u_int   sapi, tei;
+
+       sapi = *p >> 2;
+       tei = p[1] >> 1;
+       return sapi | (tei << 8);
+}
+
+void
+recv_Dchannel(struct dchannel *dch)
+{
+       struct mISDNhead *hh;
+
+       if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
+               dev_kfree_skb(dch->rx_skb);
+               dch->rx_skb = NULL;
+               return;
+       }
+       hh = mISDN_HEAD_P(dch->rx_skb);
+       hh->prim = PH_DATA_IND;
+       hh->id = get_sapi_tei(dch->rx_skb->data);
+       skb_queue_tail(&dch->rqueue, dch->rx_skb);
+       dch->rx_skb = NULL;
+       schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel);
+
+void
+recv_Bchannel(struct bchannel *bch)
+{
+       struct mISDNhead *hh;
+
+       hh = mISDN_HEAD_P(bch->rx_skb);
+       hh->prim = PH_DATA_IND;
+       hh->id = MISDN_ID_ANY;
+       if (bch->rcount >= 64) {
+               dev_kfree_skb(bch->rx_skb);
+               bch->rx_skb = NULL;
+               return;
+       }
+       bch->rcount++;
+       skb_queue_tail(&bch->rqueue, bch->rx_skb);
+       bch->rx_skb = NULL;
+       schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel);
+
+void
+recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
+{
+       skb_queue_tail(&dch->rqueue, skb);
+       schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel_skb);
+
+void
+recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
+{
+       if (bch->rcount >= 64) {
+               dev_kfree_skb(skb);
+               return;
+       }
+       bch->rcount++;
+       skb_queue_tail(&bch->rqueue, skb);
+       schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel_skb);
+
+static void
+confirm_Dsend(struct dchannel *dch)
+{
+       struct sk_buff  *skb;
+
+       skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
+           0, NULL, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_ERR "%s: no skb id %x\n", __func__,
+                   mISDN_HEAD_ID(dch->tx_skb));
+               return;
+       }
+       skb_queue_tail(&dch->rqueue, skb);
+       schedule_event(dch, FLG_RECVQUEUE);
+}
+
+int
+get_next_dframe(struct dchannel *dch)
+{
+       dch->tx_idx = 0;
+       dch->tx_skb = skb_dequeue(&dch->squeue);
+       if (dch->tx_skb) {
+               confirm_Dsend(dch);
+               return 1;
+       }
+       dch->tx_skb = NULL;
+       test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+       return 0;
+}
+EXPORT_SYMBOL(get_next_dframe);
+
+void
+confirm_Bsend(struct bchannel *bch)
+{
+       struct sk_buff  *skb;
+
+       if (bch->rcount >= 64)
+               return;
+       skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
+           0, NULL, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_ERR "%s: no skb id %x\n", __func__,
+                   mISDN_HEAD_ID(bch->tx_skb));
+               return;
+       }
+       bch->rcount++;
+       skb_queue_tail(&bch->rqueue, skb);
+       schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(confirm_Bsend);
+
+int
+get_next_bframe(struct bchannel *bch)
+{
+       bch->tx_idx = 0;
+       if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
+               bch->tx_skb = bch->next_skb;
+               if (bch->tx_skb) {
+                       bch->next_skb = NULL;
+                       test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               confirm_Bsend(bch); /* not for transparent */
+                       return 1;
+               } else {
+                       test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+                       printk(KERN_WARNING "B TX_NEXT without skb\n");
+               }
+       }
+       bch->tx_skb = NULL;
+       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+       return 0;
+}
+EXPORT_SYMBOL(get_next_bframe);
+
+void
+queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
+{
+       struct mISDNhead *hh;
+
+       if (!skb) {
+               _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
+       } else {
+               if (ch->peer) {
+                       hh = mISDN_HEAD_P(skb);
+                       hh->prim = pr;
+                       hh->id = id;
+                       if (!ch->recv(ch->peer, skb))
+                               return;
+               }
+               dev_kfree_skb(skb);
+       }
+}
+EXPORT_SYMBOL(queue_ch_frame);
+
+int
+dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
+{
+       /* check oversize */
+       if (skb->len <= 0) {
+               printk(KERN_WARNING "%s: skb too small\n", __func__);
+               return -EINVAL;
+       }
+       if (skb->len > ch->maxlen) {
+               printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+                       __func__, skb->len, ch->maxlen);
+               return -EINVAL;
+       }
+       /* HW lock must be obtained */
+       if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+               skb_queue_tail(&ch->squeue, skb);
+               return 0;
+       } else {
+               /* write to fifo */
+               ch->tx_skb = skb;
+               ch->tx_idx = 0;
+               return 1;
+       }
+}
+EXPORT_SYMBOL(dchannel_senddata);
+
+int
+bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
+{
+
+       /* check oversize */
+       if (skb->len <= 0) {
+               printk(KERN_WARNING "%s: skb too small\n", __func__);
+               return -EINVAL;
+       }
+       if (skb->len > ch->maxlen) {
+               printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+                       __func__, skb->len, ch->maxlen);
+               return -EINVAL;
+       }
+       /* HW lock must be obtained */
+       /* check for pending next_skb */
+       if (ch->next_skb) {
+               printk(KERN_WARNING
+                   "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
+                   __func__, skb->len, ch->next_skb->len);
+               return -EBUSY;
+       }
+       if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+               test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
+               ch->next_skb = skb;
+               return 0;
+       } else {
+               /* write to fifo */
+               ch->tx_skb = skb;
+               ch->tx_idx = 0;
+               return 1;
+       }
+}
+EXPORT_SYMBOL(bchannel_senddata);
diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h
new file mode 100644 (file)
index 0000000..a23d575
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * see notice in l1oip.c
+ */
+
+/* debugging */
+#define DEBUG_L1OIP_INIT       0x00010000
+#define DEBUG_L1OIP_SOCKET     0x00020000
+#define DEBUG_L1OIP_MGR                0x00040000
+#define DEBUG_L1OIP_MSG                0x00080000
+
+/* enable to disorder received bchannels by sequence 2143658798... */
+/*
+#define REORDER_DEBUG
+*/
+
+/* frames */
+#define L1OIP_MAX_LEN          2048            /* max packet size form l2 */
+#define L1OIP_MAX_PERFRAME     1400            /* max data size in one frame */
+
+
+/* timers */
+#define L1OIP_KEEPALIVE                15
+#define L1OIP_TIMEOUT          65
+
+
+/* socket */
+#define L1OIP_DEFAULTPORT      931
+
+
+/* channel structure */
+struct l1oip_chan {
+       struct dchannel         *dch;
+       struct bchannel         *bch;
+       u32                     tx_counter;     /* counts xmit bytes/packets */
+       u32                     rx_counter;     /* counts recv bytes/packets */
+       u32                     codecstate;     /* used by codec to save data */
+#ifdef REORDER_DEBUG
+       int                     disorder_flag;
+       struct sk_buff          *disorder_skb;
+       u32                     disorder_cnt;
+#endif
+};
+
+
+/* card structure */
+struct l1oip {
+       struct list_head        list;
+
+       /* card */
+       int                     registered;     /* if registered with mISDN */
+       char                    name[MISDN_MAX_IDLEN];
+       int                     idx;            /* card index */
+       int                     pri;            /* 1=pri, 0=bri */
+       int                     d_idx;          /* current dchannel number */
+       int                     b_num;          /* number of bchannels */
+       u32                     id;             /* id of connection */
+       int                     ondemand;       /* if transmis. is on demand */
+       int                     bundle;         /* bundle channels in one frm */
+       int                     codec;          /* codec to use for transmis. */
+       int                     limit;          /* limit number of bchannels */
+
+       /* timer */
+       struct timer_list       keep_tl;
+       struct timer_list       timeout_tl;
+       int                     timeout_on;
+       struct work_struct      workq;
+
+       /* socket */
+       struct socket           *socket;        /* if set, socket is created */
+       struct completion       socket_complete;/* completion of sock thread */
+       struct task_struct      *socket_thread;
+       spinlock_t              socket_lock;    /* access sock outside thread */
+       u32                     remoteip;       /* if all set, ip is assigned */
+       u16                     localport;      /* must always be set */
+       u16                     remoteport;     /* must always be set */
+       struct sockaddr_in      sin_local;      /* local socket name */
+       struct sockaddr_in      sin_remote;     /* remote socket name */
+       struct msghdr           sendmsg;        /* ip message to send */
+       struct iovec            sendiov;        /* iov for message */
+
+       /* frame */
+       struct l1oip_chan       chan[128];      /* channel instances */
+};
+
+extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state);
+extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result);
+extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result);
+extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result);
+extern void l1oip_4bit_free(void);
+extern int l1oip_4bit_alloc(int ulaw);
+
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
new file mode 100644 (file)
index 0000000..a2dc457
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+
+ * l1oip_codec.c  generic codec using lookup table
+ *  -> conversion from a-Law to u-Law
+ *  -> conversion from u-Law to a-Law
+ *  -> compression by reducing the number of sample resolution to 4
+ *
+ * NOTE: It is not compatible with any standard codec like ADPCM.
+ *
+ * Author      Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+/*
+
+How the codec works:
+--------------------
+
+The volume is increased to increase the dynamic range of the audio signal.
+Each sample is converted to a-LAW with only 16 steps of level resolution.
+A pair of two samples are stored in one byte.
+
+The first byte is stored in the upper bits, the second byte is stored in the
+lower bits.
+
+To speed up compression and decompression, two lookup tables are formed:
+
+- 16 bits index for two samples (law encoded) with 8 bit compressed result.
+- 8 bits index for one compressed data with 16 bits decompressed result.
+
+NOTE: The bytes are handled as they are law-encoded.
+
+*/
+
+#include <linux/vmalloc.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+/* definitions of codec. don't use calculations, code may run slower. */
+
+static u8 *table_com;
+static u16 *table_dec;
+
+
+/* alaw -> ulaw */
+static u8 alaw_to_ulaw[256] =
+{
+       0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+       0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+       0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+       0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+       0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+       0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+       0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+       0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+       0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+       0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+       0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+       0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+       0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+       0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+       0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+       0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+       0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+       0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+       0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+       0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+       0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+       0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+       0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+       0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+       0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+       0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+       0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+       0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+       0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+       0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+       0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+       0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+};
+
+/* ulaw -> alaw */
+static u8 ulaw_to_alaw[256] =
+{
+       0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+       0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+       0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+       0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+       0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+       0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+       0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+       0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+       0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+       0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+       0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+       0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+       0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+       0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+       0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+       0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+       0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+       0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+       0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+       0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+       0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+       0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+       0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+       0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+       0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+       0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+       0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+       0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+       0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+       0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+       0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+       0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+};
+
+/* alaw -> 4bit compression */
+static u8 alaw_to_4bit[256] = {
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02,
+       0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+};
+
+/* 4bit -> alaw decompression */
+static u8 _4bit_to_alaw[16] = {
+       0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b,
+       0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c,
+};
+
+/* ulaw -> 4bit compression */
+static u8 ulaw_to_4bit[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       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, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+       0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+       0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+       0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+       0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b,
+       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+       0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+       0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+};
+
+/* 4bit -> ulaw decompression */
+static u8 _4bit_to_ulaw[16] = {
+       0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71,
+       0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f,
+};
+
+
+/*
+ * Compresses data to the result buffer
+ * The result size must be at least half of the input buffer.
+ * The number of samples also must be even!
+ */
+int
+l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state)
+{
+       int ii, i = 0, o = 0;
+
+       if (!len)
+               return 0;
+
+       /* send saved byte and first input byte */
+       if (*state) {
+               *result++ = table_com[(((*state)<<8)&0xff00) | (*data++)];
+               len--;
+               o++;
+       }
+
+       ii = len >> 1;
+
+       while (i < ii) {
+               *result++ = table_com[(data[0]<<8) | (data[1])];
+               data += 2;
+               i++;
+               o++;
+       }
+
+       /* if len has an odd number, we save byte for next call */
+       if (len & 1)
+               *state = 0x100 + *data;
+       else
+               *state = 0;
+
+       return o;
+}
+
+/* Decompress data to the result buffer
+ * The result size must be the number of sample in packet. (2 * input data)
+ * The number of samples in the result are even!
+ */
+int
+l1oip_4bit_to_law(u8 *data, int len, u8 *result)
+{
+       int i = 0;
+       u16 r;
+
+       while (i < len) {
+               r = table_dec[*data++];
+               *result++ = r>>8;
+               *result++ = r;
+               i++;
+       }
+
+       return len << 1;
+}
+
+
+/*
+ * law conversion
+ */
+int
+l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result)
+{
+       int i = 0;
+
+       while (i < len) {
+               *result++ = alaw_to_ulaw[*data++];
+               i++;
+       }
+
+       return len;
+}
+
+int
+l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result)
+{
+       int i = 0;
+
+       while (i < len) {
+               *result++ = ulaw_to_alaw[*data++];
+               i++;
+       }
+
+       return len;
+}
+
+
+/*
+ * generate/free compression and decompression table
+ */
+void
+l1oip_4bit_free(void)
+{
+       if (table_dec)
+               vfree(table_dec);
+       if (table_com)
+               vfree(table_com);
+       table_com = NULL;
+       table_dec = NULL;
+}
+
+int
+l1oip_4bit_alloc(int ulaw)
+{
+       int i1, i2, c, sample;
+
+       /* in case, it is called again */
+       if (table_dec)
+               return 0;
+
+       /* alloc conversion tables */
+       table_com = vmalloc(65536);
+       table_dec = vmalloc(512);
+       if (!table_com | !table_dec) {
+               l1oip_4bit_free();
+               return -ENOMEM;
+       }
+       memset(table_com, 0, 65536);
+       memset(table_dec, 0, 512);
+       /* generate compression table */
+       i1 = 0;
+       while (i1 < 256) {
+               if (ulaw)
+                       c = ulaw_to_4bit[i1];
+               else
+                       c = alaw_to_4bit[i1];
+               i2 = 0;
+               while (i2 < 256) {
+                       table_com[(i1<<8) | i2] |= (c<<4);
+                       table_com[(i2<<8) | i1] |= c;
+                       i2++;
+               }
+               i1++;
+       }
+
+       /* generate decompression table */
+       i1 = 0;
+       while (i1 < 16) {
+               if (ulaw)
+                       sample = _4bit_to_ulaw[i1];
+               else
+                       sample = _4bit_to_alaw[i1];
+               i2 = 0;
+               while (i2 < 16) {
+                       table_dec[(i1<<4) | i2] |= (sample<<8);
+                       table_dec[(i2<<4) | i1] |= sample;
+                       i2++;
+               }
+               i1++;
+       }
+
+       return 0;
+}
+
+
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
new file mode 100644 (file)
index 0000000..155b997
--- /dev/null
@@ -0,0 +1,1518 @@
+/*
+
+ * l1oip.c  low level driver for tunneling layer 1 over IP
+ *
+ * NOTE: It is not compatible with TDMoIP nor "ISDN over IP".
+ *
+ * Author      Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* module parameters:
+ * type:
+       Value 1 = BRI
+       Value 2 = PRI
+       Value 3 = BRI (multi channel frame, not supported yet)
+       Value 4 = PRI (multi channel frame, not supported yet)
+       A multi channel frame reduces overhead to a single frame for all
+       b-channels, but increases delay.
+       (NOTE: Multi channel frames are not implemented yet.)
+
+ * codec:
+       Value 0 = transparent (default)
+       Value 1 = transfer ALAW
+       Value 2 = transfer ULAW
+       Value 3 = transfer generic 4 bit compression.
+
+ * ulaw:
+       0 = we use a-Law (default)
+       1 = we use u-Law
+
+ * limit:
+       limitation of B-channels to control bandwidth (1...126)
+       BRI: 1 or 2
+       PRI: 1-30, 31-126 (126, because dchannel ist not counted here)
+       Also limited ressources are used for stack, resulting in less channels.
+       It is possible to have more channels than 30 in PRI mode, this must
+       be supported by the application.
+
+ * ip:
+       byte representation of remote ip address (127.0.0.1 -> 127,0,0,1)
+       If not given or four 0, no remote address is set.
+       For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1)
+
+ * port:
+       port number (local interface)
+       If not given or 0, port 931 is used for fist instance, 932 for next...
+       For multiple interfaces, different ports must be given.
+
+ * remoteport:
+       port number (remote interface)
+       If not given or 0, remote port equals local port
+       For multiple interfaces on equal sites, different ports must be given.
+
+ * ondemand:
+       0 = fixed (always transmit packets, even when remote side timed out)
+       1 = on demand (only transmit packets, when remote side is detected)
+       the default is 0
+       NOTE: ID must also be set for on demand.
+
+ * id:
+       optional value to identify frames. This value must be equal on both
+       peers and should be random. If omitted or 0, no ID is transmitted.
+
+ * debug:
+       NOTE: only one debug value must be given for all cards
+       enable debugging (see l1oip.h for debug options)
+
+
+Special mISDN controls:
+
+ op = MISDN_CTRL_SETPEER*
+ p1 = bytes 0-3 : remote IP address in network order (left element first)
+ p2 = bytes 1-2 : remote port in network order (high byte first)
+ optional:
+ p2 = bytes 3-4 : local port in network order (high byte first)
+
+ op = MISDN_CTRL_UNSETPEER*
+
+ * Use l1oipctrl for comfortable setting or removing ip address.
+   (Layer 1 Over IP CTRL)
+
+
+L1oIP-Protocol
+--------------
+
+Frame Header:
+
+ 7 6 5 4 3 2 1 0
++---------------+
+|Ver|T|I|Coding |
++---------------+
+|  ID byte 3 *  |
++---------------+
+|  ID byte 2 *  |
++---------------+
+|  ID byte 1 *  |
++---------------+
+|  ID byte 0 *  |
++---------------+
+|M|   Channel   |
++---------------+
+|    Length *   |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data....     |
+
+...
+
+|               |
++---------------+
+|M|   Channel   |
++---------------+
+|    Length *   |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data....     |
+
+...
+
+
+* Only included in some cases.
+
+- Ver = Version
+If version is missmatch, the frame must be ignored.
+
+- T = Type of interface
+Must be 0 for S0 or 1 for E1.
+
+- I = Id present
+If bit is set, four ID bytes are included in frame.
+
+- ID = Connection ID
+Additional ID to prevent Denial of Service attacs. Also it prevents hijacking
+connections with dynamic IP. The ID should be random and must not be 0.
+
+- Coding = Type of codec
+Must be 0 for no transcoding. Also for D-channel and other HDLC frames.
+ 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec.
+ 3 is used for generic table compressor.
+
+- M = More channels to come. If this flag is 1, the following byte contains
+the length of the channel data. After the data block, the next channel will
+be defined. The flag for the last channel block (or if only one channel is
+transmitted), must be 0 and no length is given.
+
+- Channel = Channel number
+0 reserved
+1-3 channel data for S0 (3 is D-channel)
+1-31 channel data for E1 (16 is D-channel)
+32-127 channel data for extended E1 (16 is D-channel)
+
+- The length is used if the M-flag is 1. It is used to find the next channel
+inside frame.
+NOTE: A value of 0 equals 256 bytes of data.
+ -> For larger data blocks, a single frame must be used.
+ -> For larger streams, a single frame or multiple blocks with same channel ID
+   must be used.
+
+- Time Base = Timestamp of first sample in frame
+The "Time Base" is used to rearange packets and to detect packet loss.
+The 16 bits are sent in network order (MSB first) and count 1/8000 th of a
+second. This causes a wrap arround each 8,192 seconds. There is no requirement
+for the initial "Time Base", but 0 should be used for the first packet.
+In case of HDLC data, this timestamp counts the packet or byte number.
+
+
+Two Timers:
+
+After initialisation, a timer of 15 seconds is started. Whenever a packet is
+transmitted, the timer is reset to 15 seconds again. If the timer expires, an
+empty packet is transmitted. This keep the connection alive.
+
+When a valid packet is received, a timer 65 seconds is started. The interface
+become ACTIVE. If the timer expires, the interface becomes INACTIVE.
+
+
+Dynamic IP handling:
+
+To allow dynamic IP, the ID must be non 0. In this case, any packet with the
+correct port number and ID will be accepted. If the remote side changes its IP
+the new IP is used for all transmitted packets until it changes again.
+
+
+On Demand:
+
+If the ondemand parameter is given, the remote IP is set to 0 on timeout.
+This will stop keepalive traffic to remote. If the remote is online again,
+traffic will continue to the remote address. This is usefull for road warriors.
+This feature only works with ID set, otherwhise it is highly unsecure.
+
+
+Socket and Thread
+-----------------
+
+The complete socket opening and closing is done by a thread.
+When the thread opened a socket, the hc->socket descriptor is set. Whenever a
+packet shall be sent to the socket, the hc->socket must be checked wheter not
+NULL. To prevent change in socket descriptor, the hc->socket_lock must be used.
+To change the socket, a recall of l1oip_socket_open() will safely kill the
+socket process and create a new one.
+
+*/
+
+#define L1OIP_VERSION  0       /* 0...3 */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+#include <linux/init.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+#include "core.h"
+#include "l1oip.h"
+
+static const char *l1oip_revision = "2.00";
+
+static int l1oip_cnt;
+static spinlock_t l1oip_lock;
+static struct list_head l1oip_ilist;
+
+#define MAX_CARDS      16
+static u_int type[MAX_CARDS];
+static u_int codec[MAX_CARDS];
+static u_int ip[MAX_CARDS*4];
+static u_int port[MAX_CARDS];
+static u_int remoteport[MAX_CARDS];
+static u_int ondemand[MAX_CARDS];
+static u_int limit[MAX_CARDS];
+static u_int id[MAX_CARDS];
+static int debug;
+static int ulaw;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR);
+module_param(ulaw, uint, S_IRUGO | S_IWUSR);
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * send a frame via socket, if open and restart timer
+ */
+static int
+l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
+       u16 timebase, u8 *buf, int len)
+{
+       u8 *p;
+       int multi = 0;
+       u8 frame[len+32];
+       struct socket *socket = NULL;
+       mm_segment_t oldfs;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n",
+                       __func__, len);
+
+       p = frame;
+
+       /* restart timer */
+       if ((int)(hc->keep_tl.expires-jiffies) < 5*HZ) {
+               del_timer(&hc->keep_tl);
+               hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+               add_timer(&hc->keep_tl);
+       } else
+               hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: resetting timer\n", __func__);
+
+       /* drop if we have no remote ip or port */
+       if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) {
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: dropping frame, because remote "
+                               "IP is not set.\n", __func__);
+               return len;
+       }
+
+       /* assemble frame */
+       *p++ = (L1OIP_VERSION<<6) /* version and coding */
+            | (hc->pri?0x20:0x00) /* type */
+            | (hc->id?0x10:0x00) /* id */
+            | localcodec;
+       if (hc->id) {
+               *p++ = hc->id>>24; /* id */
+               *p++ = hc->id>>16;
+               *p++ = hc->id>>8;
+               *p++ = hc->id;
+       }
+       *p++ = (multi == 1)?0x80:0x00 + channel; /* m-flag, channel */
+       if (multi == 1)
+               *p++ = len; /* length */
+       *p++ = timebase>>8; /* time base */
+       *p++ = timebase;
+
+       if (buf && len) { /* add data to frame */
+               if (localcodec == 1 && ulaw)
+                       l1oip_ulaw_to_alaw(buf, len, p);
+               else if (localcodec == 2 && !ulaw)
+                       l1oip_alaw_to_ulaw(buf, len, p);
+               else if (localcodec == 3)
+                       len = l1oip_law_to_4bit(buf, len, p,
+                               &hc->chan[channel].codecstate);
+               else
+                       memcpy(p, buf, len);
+       }
+       len += p - frame;
+
+       /* check for socket in safe condition */
+       spin_lock(&hc->socket_lock);
+       if (!hc->socket) {
+               spin_unlock(&hc->socket_lock);
+               return 0;
+       }
+       /* seize socket */
+       socket = hc->socket;
+       hc->socket = NULL;
+       spin_unlock(&hc->socket_lock);
+       /* send packet */
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: sending packet to socket (len "
+                       "= %d)\n", __func__, len);
+       hc->sendiov.iov_base = frame;
+       hc->sendiov.iov_len  = len;
+       oldfs = get_fs();
+       set_fs(KERNEL_DS);
+       len = sock_sendmsg(socket, &hc->sendmsg, len);
+       set_fs(oldfs);
+       /* give socket back */
+       hc->socket = socket; /* no locking required */
+
+       return len;
+}
+
+
+/*
+ * receive channel data from socket
+ */
+static void
+l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase,
+       u8 *buf, int len)
+{
+       struct sk_buff *nskb;
+       struct bchannel *bch;
+       struct dchannel *dch;
+       u8 *p;
+       u32 rx_counter;
+
+       if (len == 0) {
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: received empty keepalive data, "
+                               "ignoring\n", __func__);
+               return;
+       }
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n",
+                       __func__, len);
+
+       if (channel < 1 || channel > 127) {
+               printk(KERN_WARNING "%s: packet error - channel %d out of "
+                       "range\n", __func__, channel);
+               return;
+       }
+       dch = hc->chan[channel].dch;
+       bch = hc->chan[channel].bch;
+       if (!dch && !bch) {
+               printk(KERN_WARNING "%s: packet error - channel %d not in "
+                       "stack\n", __func__, channel);
+               return;
+       }
+
+       /* prepare message */
+       nskb = mI_alloc_skb((remotecodec == 3)?(len<<1):len, GFP_ATOMIC);
+       if (!nskb) {
+               printk(KERN_ERR "%s: No mem for skb.\n", __func__);
+               return;
+       }
+       p = skb_put(nskb, (remotecodec == 3)?(len<<1):len);
+
+       if (remotecodec == 1 && ulaw)
+               l1oip_alaw_to_ulaw(buf, len, p);
+       else if (remotecodec == 2 && !ulaw)
+               l1oip_ulaw_to_alaw(buf, len, p);
+       else if (remotecodec == 3)
+               len = l1oip_4bit_to_law(buf, len, p);
+       else
+               memcpy(p, buf, len);
+
+       /* send message up */
+       if (dch && len >= 2) {
+               dch->rx_skb = nskb;
+               recv_Dchannel(dch);
+       }
+       if (bch) {
+               /* expand 16 bit sequence number to 32 bit sequence number */
+               rx_counter = hc->chan[channel].rx_counter;
+               if (((s16)(timebase - rx_counter)) >= 0) {
+                       /* time has changed forward */
+                       if (timebase >= (rx_counter & 0xffff))
+                               rx_counter =
+                                       (rx_counter & 0xffff0000) | timebase;
+                       else
+                               rx_counter = ((rx_counter & 0xffff0000)+0x10000)
+                                       | timebase;
+               } else {
+                       /* time has changed backwards */
+                       if (timebase < (rx_counter & 0xffff))
+                               rx_counter =
+                                       (rx_counter & 0xffff0000) | timebase;
+                       else
+                               rx_counter = ((rx_counter & 0xffff0000)-0x10000)
+                                       | timebase;
+               }
+               hc->chan[channel].rx_counter = rx_counter;
+
+#ifdef REORDER_DEBUG
+               if (hc->chan[channel].disorder_flag) {
+                       struct sk_buff *skb;
+                       int cnt;
+                       skb = hc->chan[channel].disorder_skb;
+                       hc->chan[channel].disorder_skb = nskb;
+                       nskb = skb;
+                       cnt = hc->chan[channel].disorder_cnt;
+                       hc->chan[channel].disorder_cnt = rx_counter;
+                       rx_counter = cnt;
+               }
+               hc->chan[channel].disorder_flag ^= 1;
+               if (nskb)
+#endif
+               queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb);
+       }
+}
+
+
+/*
+ * parse frame and extract channel data
+ */
+static void
+l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
+{
+       u32                     id;
+       u8                      channel;
+       u8                      remotecodec;
+       u16                     timebase;
+       int                     m, mlen;
+       int                     len_start = len; /* initial frame length */
+       struct dchannel         *dch = hc->chan[hc->d_idx].dch;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n",
+                       __func__, len);
+
+       /* check lenght */
+       if (len < 1+1+2) {
+               printk(KERN_WARNING "%s: packet error - length %d below "
+                       "4 bytes\n", __func__, len);
+               return;
+       }
+
+       /* check version */
+       if (((*buf)>>6) != L1OIP_VERSION) {
+               printk(KERN_WARNING "%s: packet error - unknown version %d\n",
+                       __func__, buf[0]>>6);
+               return;
+       }
+
+       /* check type */
+       if (((*buf)&0x20) && !hc->pri) {
+               printk(KERN_WARNING "%s: packet error - received E1 packet "
+                       "on S0 interface\n", __func__);
+               return;
+       }
+       if (!((*buf)&0x20) && hc->pri) {
+               printk(KERN_WARNING "%s: packet error - received S0 packet "
+                       "on E1 interface\n", __func__);
+               return;
+       }
+
+       /* get id flag */
+       id = (*buf>>4)&1;
+
+       /* check coding */
+       remotecodec = (*buf) & 0x0f;
+       if (remotecodec > 3) {
+               printk(KERN_WARNING "%s: packet error - remotecodec %d "
+                       "unsupported\n", __func__, remotecodec);
+               return;
+       }
+       buf++;
+       len--;
+
+       /* check id */
+       if (id) {
+               if (!hc->id) {
+                       printk(KERN_WARNING "%s: packet error - packet has id "
+                               "0x%x, but we have not\n", __func__, id);
+                       return;
+               }
+               if (len < 4) {
+                       printk(KERN_WARNING "%s: packet error - packet too "
+                               "short for ID value\n", __func__);
+                       return;
+               }
+               id = (*buf++) << 24;
+               id += (*buf++) << 16;
+               id += (*buf++) << 8;
+               id += (*buf++);
+               len -= 4;
+
+               if (id != hc->id) {
+                       printk(KERN_WARNING "%s: packet error - ID mismatch, "
+                               "got 0x%x, we 0x%x\n",
+                               __func__, id, hc->id);
+                       return;
+               }
+       } else {
+               if (hc->id) {
+                       printk(KERN_WARNING "%s: packet error - packet has no "
+                               "ID, but we have\n", __func__);
+                       return;
+               }
+       }
+
+multiframe:
+       if (len < 1) {
+               printk(KERN_WARNING "%s: packet error - packet too short, "
+                       "channel expected at position %d.\n",
+                       __func__, len-len_start+1);
+               return;
+       }
+
+       /* get channel and multiframe flag */
+       channel = *buf&0x7f;
+       m = *buf >> 7;
+       buf++;
+       len--;
+
+       /* check length on multiframe */
+       if (m) {
+               if (len < 1) {
+                       printk(KERN_WARNING "%s: packet error - packet too "
+                               "short, length expected at position %d.\n",
+                               __func__, len_start-len-1);
+                       return;
+               }
+
+               mlen = *buf++;
+               len--;
+               if (mlen == 0)
+                       mlen = 256;
+               if (len < mlen+3) {
+                       printk(KERN_WARNING "%s: packet error - length %d at "
+                               "position %d exceeds total length %d.\n",
+                               __func__, mlen, len_start-len-1, len_start);
+                       return;
+               }
+               if (len == mlen+3) {
+                       printk(KERN_WARNING "%s: packet error - length %d at "
+                               "position %d will not allow additional "
+                               "packet.\n",
+                               __func__, mlen, len_start-len+1);
+                       return;
+               }
+       } else
+               mlen = len-2; /* single frame, substract timebase */
+
+       if (len < 2) {
+               printk(KERN_WARNING "%s: packet error - packet too short, time "
+                       "base expected at position %d.\n",
+                       __func__, len-len_start+1);
+               return;
+       }
+
+       /* get time base */
+       timebase = (*buf++) << 8;
+       timebase |= (*buf++);
+       len -= 2;
+
+       /* if inactive, we send up a PH_ACTIVATE and activate */
+       if (!test_bit(FLG_ACTIVE, &dch->Flags)) {
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: interface become active due to "
+                               "received packet\n", __func__);
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_ATOMIC);
+       }
+
+       /* distribute packet */
+       l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen);
+       buf += mlen;
+       len -= mlen;
+
+       /* multiframe */
+       if (m)
+               goto multiframe;
+
+       /* restart timer */
+       if ((int)(hc->timeout_tl.expires-jiffies) < 5*HZ || !hc->timeout_on) {
+               hc->timeout_on = 1;
+               del_timer(&hc->timeout_tl);
+               hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+               add_timer(&hc->timeout_tl);
+       } else /* only adjust timer */
+               hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+
+       /* if ip or source port changes */
+       if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr)
+        || (hc->sin_remote.sin_port != sin->sin_port)) {
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: remote address changes from "
+                               "0x%08x to 0x%08x (port %d to %d)\n", __func__,
+                               ntohl(hc->sin_remote.sin_addr.s_addr),
+                               ntohl(sin->sin_addr.s_addr),
+                               ntohs(hc->sin_remote.sin_port),
+                               ntohs(sin->sin_port));
+               hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr;
+               hc->sin_remote.sin_port = sin->sin_port;
+       }
+}
+
+
+/*
+ * socket stuff
+ */
+static int
+l1oip_socket_thread(void *data)
+{
+       struct l1oip *hc = (struct l1oip *)data;
+       int ret = 0;
+       struct msghdr msg;
+       struct iovec iov;
+       mm_segment_t oldfs;
+       struct sockaddr_in sin_rx;
+       unsigned char recvbuf[1500];
+       int recvlen;
+       struct socket *socket = NULL;
+       DECLARE_COMPLETION(wait);
+
+       /* make daemon */
+       allow_signal(SIGTERM);
+
+       /* create socket */
+       if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) {
+               printk(KERN_ERR "%s: Failed to create socket.\n", __func__);
+               return -EIO;
+       }
+
+       /* set incoming address */
+       hc->sin_local.sin_family = AF_INET;
+       hc->sin_local.sin_addr.s_addr = INADDR_ANY;
+       hc->sin_local.sin_port = htons((unsigned short)hc->localport);
+
+       /* set outgoing address */
+       hc->sin_remote.sin_family = AF_INET;
+       hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip);
+       hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport);
+
+       /* bind to incomming port */
+       if (socket->ops->bind(socket, (struct sockaddr *)&hc->sin_local,
+           sizeof(hc->sin_local))) {
+               printk(KERN_ERR "%s: Failed to bind socket to port %d.\n",
+                       __func__, hc->localport);
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       /* check sk */
+       if (socket->sk == NULL) {
+               printk(KERN_ERR "%s: socket->sk == NULL\n", __func__);
+               ret = -EIO;
+               goto fail;
+       }
+
+       /* build receive message */
+       msg.msg_name = &sin_rx;
+       msg.msg_namelen = sizeof(sin_rx);
+       msg.msg_control = NULL;
+       msg.msg_controllen = 0;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       /* build send message */
+       hc->sendmsg.msg_name = &hc->sin_remote;
+       hc->sendmsg.msg_namelen = sizeof(hc->sin_remote);
+       hc->sendmsg.msg_control = NULL;
+       hc->sendmsg.msg_controllen = 0;
+       hc->sendmsg.msg_iov    = &hc->sendiov;
+       hc->sendmsg.msg_iovlen = 1;
+
+       /* give away socket */
+       spin_lock(&hc->socket_lock);
+       hc->socket = socket;
+       spin_unlock(&hc->socket_lock);
+
+       /* read loop */
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket created and open\n",
+                       __func__);
+       while (!signal_pending(current)) {
+               iov.iov_base = recvbuf;
+               iov.iov_len = sizeof(recvbuf);
+               oldfs = get_fs();
+               set_fs(KERNEL_DS);
+               recvlen = sock_recvmsg(socket, &msg, sizeof(recvbuf), 0);
+               set_fs(oldfs);
+               if (recvlen > 0) {
+                       l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen);
+               } else {
+                       if (debug & DEBUG_L1OIP_SOCKET)
+                           printk(KERN_WARNING "%s: broken pipe on socket\n",
+                               __func__);
+               }
+       }
+
+       /* get socket back, check first if in use, maybe by send function */
+       spin_lock(&hc->socket_lock);
+       /* if hc->socket is NULL, it is in use until it is given back */
+       while (!hc->socket) {
+               spin_unlock(&hc->socket_lock);
+               schedule_timeout(HZ/10);
+               spin_lock(&hc->socket_lock);
+       }
+       hc->socket = NULL;
+       spin_unlock(&hc->socket_lock);
+
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket thread terminating\n",
+                       __func__);
+
+fail:
+       /* close socket */
+       if (socket)
+               sock_release(socket);
+
+       /* if we got killed, signal completion */
+       complete(&hc->socket_complete);
+       hc->socket_thread = NULL; /* show termination of thread */
+
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket thread terminated\n",
+                       __func__);
+       return ret;
+}
+
+static void
+l1oip_socket_close(struct l1oip *hc)
+{
+       /* kill thread */
+       if (hc->socket_thread) {
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: socket thread exists, "
+                               "killing...\n", __func__);
+               send_sig(SIGTERM, hc->socket_thread, 0);
+               wait_for_completion(&hc->socket_complete);
+       }
+}
+
+static int
+l1oip_socket_open(struct l1oip *hc)
+{
+       /* in case of reopen, we need to close first */
+       l1oip_socket_close(hc);
+
+       init_completion(&hc->socket_complete);
+
+       /* create receive process */
+       hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s",
+               hc->name);
+       if (IS_ERR(hc->socket_thread)) {
+               int err = PTR_ERR(hc->socket_thread);
+               printk(KERN_ERR "%s: Failed (%d) to create socket process.\n",
+                       __func__, err);
+               hc->socket_thread = NULL;
+               sock_release(hc->socket);
+               return err;
+       }
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket thread created\n", __func__);
+
+       return 0;
+}
+
+
+static void
+l1oip_send_bh(struct work_struct *work)
+{
+       struct l1oip *hc = container_of(work, struct l1oip, workq);
+
+       if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+               printk(KERN_DEBUG "%s: keepalive timer expired, sending empty "
+                       "frame on dchannel\n", __func__);
+
+       /* send an empty l1oip frame at D-channel */
+       l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0);
+}
+
+
+/*
+ * timer stuff
+ */
+static void
+l1oip_keepalive(void *data)
+{
+       struct l1oip *hc = (struct l1oip *)data;
+
+       schedule_work(&hc->workq);
+}
+
+static void
+l1oip_timeout(void *data)
+{
+       struct l1oip                    *hc = (struct l1oip *)data;
+       struct dchannel         *dch = hc->chan[hc->d_idx].dch;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: timeout timer expired, turn layer one "
+                       "down.\n", __func__);
+
+       hc->timeout_on = 0; /* state that timer must be initialized next time */
+
+       /* if timeout, we send up a PH_DEACTIVATE and deactivate */
+       if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: interface become deactivated "
+                               "due to timeout\n", __func__);
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_ATOMIC);
+       }
+
+       /* if we have ondemand set, we remove ip address */
+       if (hc->ondemand) {
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: on demand causes ip address to "
+                               "be removed\n", __func__);
+               hc->sin_remote.sin_addr.s_addr = 0;
+       }
+}
+
+
+/*
+ * message handling
+ */
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct l1oip                    *hc = dch->hw;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+       int                     l, ll;
+       unsigned char           *p;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (skb->len < 1) {
+                       printk(KERN_WARNING "%s: skb too small\n",
+                               __func__);
+                       break;
+               }
+               if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+                       printk(KERN_WARNING "%s: skb too large\n",
+                               __func__);
+                       break;
+               }
+               /* send frame */
+               p = skb->data;
+               l = skb->len;
+               while (l) {
+                       ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+                       l1oip_socket_send(hc, 0, dch->slot, 0,
+                               hc->chan[dch->slot].tx_counter++, p, ll);
+                       p += ll;
+                       l -= ll;
+               }
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+               return 0;
+       case PH_ACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+                               , __func__, dch->slot, hc->b_num+1);
+               skb_trim(skb, 0);
+               if (test_bit(FLG_ACTIVE, &dch->Flags))
+                       queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+               else
+                       queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+               return 0;
+       case PH_DEACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+                               "(1..%d)\n", __func__, dch->slot,
+                               hc->b_num+1);
+               skb_trim(skb, 0);
+               if (test_bit(FLG_ACTIVE, &dch->Flags))
+                       queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+               else
+                       queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+               return 0;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+       struct l1oip    *hc = dch->hw;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER;
+               break;
+       case MISDN_CTRL_SETPEER:
+               hc->remoteip = (u32)cq->p1;
+               hc->remoteport = cq->p2 & 0xffff;
+               hc->localport = cq->p2 >> 16;
+               if (!hc->remoteport)
+                       hc->remoteport = hc->localport;
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: got new ip address from user "
+                               "space.\n", __func__);
+                       l1oip_socket_open(hc);
+               break;
+       case MISDN_CTRL_UNSETPEER:
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: removing ip address.\n",
+                               __func__);
+               hc->remoteip = 0;
+               l1oip_socket_open(hc);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+       if (debug & DEBUG_HW_OPEN)
+               printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+                   dch->dev.id, __builtin_return_address(0));
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+           (dch->dev.D.protocol != rq->protocol)) {
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_WARNING "%s: change protocol %x to %x\n",
+                       __func__, dch->dev.D.protocol, rq->protocol);
+       }
+       if (dch->dev.D.protocol != rq->protocol)
+               dch->dev.D.protocol = rq->protocol;
+
+       if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+               _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       }
+       rq->ch = &dch->dev.D;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+       struct bchannel *bch;
+       int             ch;
+
+       if (!test_bit(rq->adr.channel & 0x1f,
+               &dch->dev.channelmap[rq->adr.channel >> 5]))
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       ch = rq->adr.channel; /* BRI: 1=B1 2=B2  PRI: 1..15,17.. */
+       bch = hc->chan[ch].bch;
+       if (!bch) {
+               printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+                   __func__, ch);
+               return -EINVAL;
+       }
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct l1oip                    *hc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       if (dch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               switch (rq->protocol) {
+               case ISDN_P_TE_S0:
+               case ISDN_P_NT_S0:
+                       if (hc->pri) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq);
+                       break;
+               case ISDN_P_TE_E1:
+               case ISDN_P_NT_E1:
+                       if (!hc->pri) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq);
+                       break;
+               default:
+                       err = open_bchannel(hc, dch, rq);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+                           __func__, dch->dev.id,
+                           __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_dctrl(dch, arg);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct l1oip                    *hc = bch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       int                     l, ll, i;
+       unsigned char           *p;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (skb->len <= 0) {
+                       printk(KERN_WARNING "%s: skb too small\n",
+                               __func__);
+                       break;
+               }
+               if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+                       printk(KERN_WARNING "%s: skb too large\n",
+                               __func__);
+                       break;
+               }
+               /* check for AIS / ulaw-silence */
+               p = skb->data;
+               l = skb->len;
+               for (i = 0; i < l; i++) {
+                       if (*p++ != 0xff)
+                               break;
+               }
+               if (i == l) {
+                       if (debug & DEBUG_L1OIP_MSG)
+                               printk(KERN_DEBUG "%s: got AIS, not sending, "
+                                       "but counting\n", __func__);
+                       hc->chan[bch->slot].tx_counter += l;
+                       skb_trim(skb, 0);
+                       queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+                       return 0;
+               }
+               /* check for silence */
+               p = skb->data;
+               l = skb->len;
+               for (i = 0; i < l; i++) {
+                       if (*p++ != 0x2a)
+                               break;
+               }
+               if (i == l) {
+                       if (debug & DEBUG_L1OIP_MSG)
+                               printk(KERN_DEBUG "%s: got silence, not sending"
+                                       ", but counting\n", __func__);
+                       hc->chan[bch->slot].tx_counter += l;
+                       skb_trim(skb, 0);
+                       queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+                       return 0;
+               }
+
+               /* send frame */
+               p = skb->data;
+               l = skb->len;
+               while (l) {
+                       ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+                       l1oip_socket_send(hc, hc->codec, bch->slot, 0,
+                               hc->chan[bch->slot].tx_counter, p, ll);
+                       hc->chan[bch->slot].tx_counter += ll;
+                       p += ll;
+                       l -= ll;
+               }
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+               return 0;
+       case PH_ACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+                               , __func__, bch->slot, hc->b_num+1);
+               hc->chan[bch->slot].codecstate = 0;
+               test_and_set_bit(FLG_ACTIVE, &bch->Flags);
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+               return 0;
+       case PH_DEACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+                               "(1..%d)\n", __func__, bch->slot,
+                               hc->b_num+1);
+               test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+               return 0;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int                     ret = 0;
+       struct dsp_features     *features =
+               (struct dsp_features *)(*((u_long *)&cq->p1));
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_HW_FEATURES_OP;
+               break;
+       case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+                           __func__);
+               /* create confirm */
+               features->unclocked = 1;
+               features->unordered = 1;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       int             err = -EINVAL;
+
+       if (bch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               err = 0;
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_bctrl(bch, arg);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown prim(%x)\n",
+                       __func__, cmd);
+       }
+       return err;
+}
+
+
+/*
+ * cleanup module and stack
+ */
+static void
+release_card(struct l1oip *hc)
+{
+       int     ch;
+
+       if (timer_pending(&hc->keep_tl))
+               del_timer(&hc->keep_tl);
+
+       if (timer_pending(&hc->timeout_tl))
+               del_timer(&hc->timeout_tl);
+
+       if (hc->socket_thread)
+               l1oip_socket_close(hc);
+
+       if (hc->registered && hc->chan[hc->d_idx].dch)
+               mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev);
+       for (ch = 0; ch < 128; ch++) {
+               if (hc->chan[ch].dch) {
+                       mISDN_freedchannel(hc->chan[ch].dch);
+                       kfree(hc->chan[ch].dch);
+               }
+               if (hc->chan[ch].bch) {
+                       mISDN_freebchannel(hc->chan[ch].bch);
+                       kfree(hc->chan[ch].bch);
+#ifdef REORDER_DEBUG
+                       if (hc->chan[ch].disorder_skb)
+                               dev_kfree_skb(hc->chan[ch].disorder_skb);
+#endif
+               }
+       }
+
+       spin_lock(&l1oip_lock);
+       list_del(&hc->list);
+       spin_unlock(&l1oip_lock);
+
+       kfree(hc);
+}
+
+static void
+l1oip_cleanup(void)
+{
+       struct l1oip *hc, *next;
+
+       list_for_each_entry_safe(hc, next, &l1oip_ilist, list)
+               release_card(hc);
+
+       l1oip_4bit_free();
+}
+
+
+/*
+ * module and stack init
+ */
+static int
+init_card(struct l1oip *hc, int pri, int bundle)
+{
+       struct dchannel *dch;
+       struct bchannel *bch;
+       int             ret;
+       int             i, ch;
+
+       spin_lock_init(&hc->socket_lock);
+       hc->idx = l1oip_cnt;
+       hc->pri = pri;
+       hc->d_idx = pri?16:3;
+       hc->b_num = pri?30:2;
+       hc->bundle = bundle;
+       if (hc->pri)
+               sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1);
+       else
+               sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1);
+
+       switch (codec[l1oip_cnt]) {
+       case 0: /* as is */
+       case 1: /* alaw */
+       case 2: /* ulaw */
+       case 3: /* 4bit */
+               break;
+       default:
+               printk(KERN_ERR "Codec(%d) not supported.\n",
+                       codec[l1oip_cnt]);
+               return -EINVAL;
+       }
+       hc->codec = codec[l1oip_cnt];
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: using codec %d\n",
+                       __func__, hc->codec);
+
+       if (id[l1oip_cnt] == 0) {
+               printk(KERN_WARNING "Warning: No 'id' value given or "
+                       "0, this is highly unsecure. Please use 32 "
+                       "bit randmom number 0x...\n");
+       }
+       hc->id = id[l1oip_cnt];
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id);
+
+       hc->ondemand = ondemand[l1oip_cnt];
+       if (hc->ondemand && !hc->id) {
+               printk(KERN_ERR "%s: ondemand option only allowed in "
+                       "conjunction with non 0 ID\n", __func__);
+               return -EINVAL;
+       }
+
+       if (limit[l1oip_cnt])
+               hc->b_num = limit[l1oip_cnt];
+       if (!pri && hc->b_num > 2) {
+               printk(KERN_ERR "Maximum limit for BRI interface is 2 "
+                       "channels.\n");
+               return -EINVAL;
+       }
+       if (pri && hc->b_num > 126) {
+               printk(KERN_ERR "Maximum limit for PRI interface is 126 "
+                       "channels.\n");
+               return -EINVAL;
+       }
+       if (pri && hc->b_num > 30) {
+               printk(KERN_WARNING "Maximum limit for BRI interface is 30 "
+                       "channels.\n");
+               printk(KERN_WARNING "Your selection of %d channels must be "
+                       "supported by application.\n", hc->limit);
+       }
+
+       hc->remoteip = ip[l1oip_cnt<<2] << 24
+                    | ip[(l1oip_cnt<<2)+1] << 16
+                    | ip[(l1oip_cnt<<2)+2] << 8
+                    | ip[(l1oip_cnt<<2)+3];
+       hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT+l1oip_cnt);
+       if (remoteport[l1oip_cnt])
+               hc->remoteport = remoteport[l1oip_cnt];
+       else
+               hc->remoteport = hc->localport;
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: using local port %d remote ip "
+                       "%d.%d.%d.%d port %d ondemand %d\n", __func__,
+                       hc->localport, hc->remoteip >> 24,
+                       (hc->remoteip >> 16) & 0xff,
+                       (hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff,
+                       hc->remoteport, hc->ondemand);
+
+       dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+       if (!dch)
+               return -ENOMEM;
+       dch->debug = debug;
+       mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL);
+       dch->hw = hc;
+       if (pri)
+               dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+       else
+               dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+       dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       dch->dev.D.send = handle_dmsg;
+       dch->dev.D.ctrl = l1oip_dctrl;
+       dch->dev.nrbchan = hc->b_num;
+       dch->slot = hc->d_idx;
+       hc->chan[hc->d_idx].dch = dch;
+       i = 1;
+       for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+               if (ch == 15)
+                       i++;
+               bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+               if (!bch) {
+                       printk(KERN_ERR "%s: no memory for bchannel\n",
+                           __func__);
+                       return -ENOMEM;
+               }
+               bch->nr = i + ch;
+               bch->slot = i + ch;
+               bch->debug = debug;
+               mISDN_initbchannel(bch, MAX_DATA_MEM);
+               bch->hw = hc;
+               bch->ch.send = handle_bmsg;
+               bch->ch.ctrl = l1oip_bctrl;
+               bch->ch.nr = i + ch;
+               list_add(&bch->ch.list, &dch->dev.bchannels);
+               hc->chan[i + ch].bch = bch;
+               test_and_set_bit(bch->nr & 0x1f,
+                       &dch->dev.channelmap[bch->nr >> 5]);
+       }
+       ret = mISDN_register_device(&dch->dev, hc->name);
+       if (ret)
+               return ret;
+       hc->registered = 1;
+
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: Setting up network card(%d)\n",
+                       __func__, l1oip_cnt + 1);
+       ret = l1oip_socket_open(hc);
+       if (ret)
+               return ret;
+
+       hc->keep_tl.function = (void *)l1oip_keepalive;
+       hc->keep_tl.data = (ulong)hc;
+       init_timer(&hc->keep_tl);
+       hc->keep_tl.expires = jiffies + 2*HZ; /* two seconds first time */
+       add_timer(&hc->keep_tl);
+
+       hc->timeout_tl.function = (void *)l1oip_timeout;
+       hc->timeout_tl.data = (ulong)hc;
+       init_timer(&hc->timeout_tl);
+       hc->timeout_on = 0; /* state that we have timer off */
+
+       return 0;
+}
+
+static int __init
+l1oip_init(void)
+{
+       int             pri, bundle;
+       struct l1oip            *hc;
+       int             ret;
+
+       printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n",
+               l1oip_revision);
+
+       INIT_LIST_HEAD(&l1oip_ilist);
+       spin_lock_init(&l1oip_lock);
+
+       if (l1oip_4bit_alloc(ulaw))
+               return -ENOMEM;
+
+       l1oip_cnt = 0;
+       while (type[l1oip_cnt] && l1oip_cnt < MAX_CARDS) {
+               switch (type[l1oip_cnt] & 0xff) {
+               case 1:
+                       pri = 0;
+                       bundle = 0;
+                       break;
+               case 2:
+                       pri = 1;
+                       bundle = 0;
+                       break;
+               case 3:
+                       pri = 0;
+                       bundle = 1;
+                       break;
+               case 4:
+                       pri = 1;
+                       bundle = 1;
+                       break;
+               default:
+                       printk(KERN_ERR "Card type(%d) not supported.\n",
+                               type[l1oip_cnt] & 0xff);
+                       l1oip_cleanup();
+                       return -EINVAL;
+               }
+
+               if (debug & DEBUG_L1OIP_INIT)
+                       printk(KERN_DEBUG "%s: interface %d is %s with %s.\n",
+                               __func__, l1oip_cnt, pri?"PRI":"BRI",
+                               bundle?"bundled IP packet for all B-channels"
+                                :"seperate IP packets for every B-channel");
+
+               hc = kzalloc(sizeof(struct l1oip), GFP_ATOMIC);
+               if (!hc) {
+                       printk(KERN_ERR "No kmem for L1-over-IP driver.\n");
+                       l1oip_cleanup();
+                       return -ENOMEM;
+               }
+               INIT_WORK(&hc->workq, (void *)l1oip_send_bh);
+
+               spin_lock(&l1oip_lock);
+               list_add_tail(&hc->list, &l1oip_ilist);
+               spin_unlock(&l1oip_lock);
+
+               ret = init_card(hc, pri, bundle);
+               if (ret) {
+                       l1oip_cleanup();
+                       return ret;
+               }
+
+               l1oip_cnt++;
+       }
+       printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt);
+       return 0;
+}
+
+module_init(l1oip_init);
+module_exit(l1oip_cleanup);
+
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
new file mode 100644 (file)
index 0000000..fced1a2
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "layer1.h"
+#include "fsm.h"
+
+static int *debug;
+
+struct layer1 {
+       u_long                  Flags;
+       struct FsmInst          l1m;
+       struct FsmTimer         timer;
+       int                     delay;
+       struct dchannel         *dch;
+       dchannel_l1callback     *dcb;
+};
+
+#define TIMER3_VALUE 7000
+
+static
+struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL};
+
+enum {
+       ST_L1_F2,
+       ST_L1_F3,
+       ST_L1_F4,
+       ST_L1_F5,
+       ST_L1_F6,
+       ST_L1_F7,
+       ST_L1_F8,
+};
+
+#define L1S_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1SState[] =
+{
+       "ST_L1_F2",
+       "ST_L1_F3",
+       "ST_L1_F4",
+       "ST_L1_F5",
+       "ST_L1_F6",
+       "ST_L1_F7",
+       "ST_L1_F8",
+};
+
+enum {
+       EV_PH_ACTIVATE,
+       EV_PH_DEACTIVATE,
+       EV_RESET_IND,
+       EV_DEACT_CNF,
+       EV_DEACT_IND,
+       EV_POWER_UP,
+       EV_ANYSIG_IND,
+       EV_INFO2_IND,
+       EV_INFO4_IND,
+       EV_TIMER_DEACT,
+       EV_TIMER_ACT,
+       EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+       "EV_PH_ACTIVATE",
+       "EV_PH_DEACTIVATE",
+       "EV_RESET_IND",
+       "EV_DEACT_CNF",
+       "EV_DEACT_IND",
+       "EV_POWER_UP",
+       "EV_ANYSIG_IND",
+       "EV_INFO2_IND",
+       "EV_INFO4_IND",
+       "EV_TIMER_DEACT",
+       "EV_TIMER_ACT",
+       "EV_TIMER3",
+};
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct layer1 *l1 = fi->userdata;
+       va_list va;
+
+       va_start(va, fmt);
+       printk(KERN_DEBUG "%s: ", l1->dch->dev.name);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F3);
+       if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
+               l1->dcb(l1->dch, HW_POWERUP_REQ);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F3);
+       mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2);
+       test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags);
+}
+
+static void
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+               mISDN_FsmChangeState(fi, ST_L1_F4);
+               l1->dcb(l1->dch, INFO3_P8);
+       } else
+               mISDN_FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F6);
+       l1->dcb(l1->dch, INFO3_P8);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F7);
+       l1->dcb(l1->dch, INFO3_P8);
+       if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags))
+               mISDN_FsmDelTimer(&l1->timer, 4);
+       if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) {
+               if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags))
+                       mISDN_FsmDelTimer(&l1->timer, 3);
+               mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2);
+               test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags);
+       }
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
+       if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+               if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+                       l1->dcb(l1->dch, HW_D_NOBLOCKED);
+               l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+       }
+       if (l1->l1m.state != ST_L1_F6) {
+               mISDN_FsmChangeState(fi, ST_L1_F3);
+               l1->dcb(l1->dch, HW_POWERUP_REQ);
+       }
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
+       test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
+       l1->dcb(l1->dch, PH_ACTIVATE_IND);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags);
+       test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags);
+       if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+               l1->dcb(l1->dch, HW_D_NOBLOCKED);
+       l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+       l1->dcb(l1->dch, HW_DEACT_REQ);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+       test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
+       l1->dcb(l1->dch, HW_RESET_REQ);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) &&
+           (!test_bit(FLG_L1_T3RUN, &l1->Flags))) {
+               test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags);
+               if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+                       l1->dcb(l1->dch, HW_D_NOBLOCKED);
+               l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+       }
+}
+
+static struct FsmNode L1SFnList[] =
+{
+       {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
+       {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+       {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
+       {ST_L1_F3, EV_RESET_IND, l1_reset},
+       {ST_L1_F4, EV_RESET_IND, l1_reset},
+       {ST_L1_F5, EV_RESET_IND, l1_reset},
+       {ST_L1_F6, EV_RESET_IND, l1_reset},
+       {ST_L1_F7, EV_RESET_IND, l1_reset},
+       {ST_L1_F8, EV_RESET_IND, l1_reset},
+       {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
+       {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
+       {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
+       {ST_L1_F3, EV_POWER_UP,  l1_power_up_s},
+       {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5},
+       {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8},
+       {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8},
+       {ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F3, EV_TIMER3, l1_timer3},
+       {ST_L1_F4, EV_TIMER3, l1_timer3},
+       {ST_L1_F5, EV_TIMER3, l1_timer3},
+       {ST_L1_F6, EV_TIMER3, l1_timer3},
+       {ST_L1_F8, EV_TIMER3, l1_timer3},
+       {ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
+       {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+static void
+release_l1(struct layer1 *l1) {
+       mISDN_FsmDelTimer(&l1->timer, 0);
+       if (l1->dch)
+               l1->dch->l1 = NULL;
+       module_put(THIS_MODULE);
+       kfree(l1);
+}
+
+int
+l1_event(struct layer1 *l1, u_int event)
+{
+       int             err = 0;
+
+       if (!l1)
+               return -EINVAL;
+       switch (event) {
+       case HW_RESET_IND:
+               mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
+               break;
+       case HW_DEACT_IND:
+               mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
+               break;
+       case HW_POWERUP_IND:
+               mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
+               break;
+       case HW_DEACT_CNF:
+               mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
+               break;
+       case ANYSIGNAL:
+               mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+               break;
+       case LOSTFRAMING:
+               mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+               break;
+       case INFO2:
+               mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
+               break;
+       case INFO4_P8:
+               mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+               break;
+       case INFO4_P10:
+               mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+               break;
+       case PH_ACTIVATE_REQ:
+               if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
+                       l1->dcb(l1->dch, PH_ACTIVATE_IND);
+               else {
+                       test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
+                       mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               release_l1(l1);
+               break;
+       default:
+               if (*debug & DEBUG_L1)
+                       printk(KERN_DEBUG "%s %x unhandled\n",
+                           __func__, event);
+               err = -EINVAL;
+       }
+       return err;
+}
+EXPORT_SYMBOL(l1_event);
+
+int
+create_l1(struct dchannel *dch, dchannel_l1callback *dcb) {
+       struct layer1   *nl1;
+
+       nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC);
+       if (!nl1) {
+               printk(KERN_ERR "kmalloc struct layer1 failed\n");
+               return -ENOMEM;
+       }
+       nl1->l1m.fsm = &l1fsm_s;
+       nl1->l1m.state = ST_L1_F3;
+       nl1->Flags = 0;
+       nl1->l1m.debug = *debug & DEBUG_L1_FSM;
+       nl1->l1m.userdata = nl1;
+       nl1->l1m.userint = 0;
+       nl1->l1m.printdebug = l1m_debug;
+       nl1->dch = dch;
+       nl1->dcb = dcb;
+       mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer);
+       __module_get(THIS_MODULE);
+       dch->l1 = nl1;
+       return 0;
+}
+EXPORT_SYMBOL(create_l1);
+
+int
+l1_init(u_int *deb)
+{
+       debug = deb;
+       l1fsm_s.state_count = L1S_STATE_COUNT;
+       l1fsm_s.event_count = L1_EVENT_COUNT;
+       l1fsm_s.strEvent = strL1Event;
+       l1fsm_s.strState = strL1SState;
+       mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
+       return 0;
+}
+
+void
+l1_cleanup(void)
+{
+       mISDN_FsmFree(&l1fsm_s);
+}
diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h
new file mode 100644 (file)
index 0000000..9c8125f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ * Layer 1 defines
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define FLG_L1_ACTIVATING      1
+#define FLG_L1_ACTIVATED       2
+#define FLG_L1_DEACTTIMER      3
+#define FLG_L1_ACTTIMER                4
+#define FLG_L1_T3RUN           5
+#define FLG_L1_PULL_REQ                6
+#define FLG_L1_UINT            7
+#define FLG_L1_DBLOCKED                8
+
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
new file mode 100644 (file)
index 0000000..a7915a1
--- /dev/null
@@ -0,0 +1,2216 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "fsm.h"
+#include "layer2.h"
+
+static int *debug;
+
+static
+struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
+
+static char *strL2State[] =
+{
+       "ST_L2_1",
+       "ST_L2_2",
+       "ST_L2_3",
+       "ST_L2_4",
+       "ST_L2_5",
+       "ST_L2_6",
+       "ST_L2_7",
+       "ST_L2_8",
+};
+
+enum {
+       EV_L2_UI,
+       EV_L2_SABME,
+       EV_L2_DISC,
+       EV_L2_DM,
+       EV_L2_UA,
+       EV_L2_FRMR,
+       EV_L2_SUPER,
+       EV_L2_I,
+       EV_L2_DL_DATA,
+       EV_L2_ACK_PULL,
+       EV_L2_DL_UNITDATA,
+       EV_L2_DL_ESTABLISH_REQ,
+       EV_L2_DL_RELEASE_REQ,
+       EV_L2_MDL_ASSIGN,
+       EV_L2_MDL_REMOVE,
+       EV_L2_MDL_ERROR,
+       EV_L1_DEACTIVATE,
+       EV_L2_T200,
+       EV_L2_T203,
+       EV_L2_SET_OWN_BUSY,
+       EV_L2_CLEAR_OWN_BUSY,
+       EV_L2_FRAME_ERROR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1)
+
+static char *strL2Event[] =
+{
+       "EV_L2_UI",
+       "EV_L2_SABME",
+       "EV_L2_DISC",
+       "EV_L2_DM",
+       "EV_L2_UA",
+       "EV_L2_FRMR",
+       "EV_L2_SUPER",
+       "EV_L2_I",
+       "EV_L2_DL_DATA",
+       "EV_L2_ACK_PULL",
+       "EV_L2_DL_UNITDATA",
+       "EV_L2_DL_ESTABLISH_REQ",
+       "EV_L2_DL_RELEASE_REQ",
+       "EV_L2_MDL_ASSIGN",
+       "EV_L2_MDL_REMOVE",
+       "EV_L2_MDL_ERROR",
+       "EV_L1_DEACTIVATE",
+       "EV_L2_T200",
+       "EV_L2_T203",
+       "EV_L2_SET_OWN_BUSY",
+       "EV_L2_CLEAR_OWN_BUSY",
+       "EV_L2_FRAME_ERROR",
+};
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct layer2 *l2 = fi->userdata;
+       va_list va;
+
+       if (!(*debug & DEBUG_L2_FSM))
+               return;
+       va_start(va, fmt);
+       printk(KERN_DEBUG "l2 (tei %d): ", l2->tei);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+inline u_int
+l2headersize(struct layer2 *l2, int ui)
+{
+       return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+               (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
+}
+
+inline u_int
+l2addrsize(struct layer2 *l2)
+{
+       return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1;
+}
+
+static u_int
+l2_newid(struct layer2 *l2)
+{
+       u_int   id;
+
+       id = l2->next_id++;
+       if (id == 0x7fff)
+               l2->next_id = 1;
+       id <<= 16;
+       id |= l2->tei << 8;
+       id |= l2->sapi;
+       return id;
+}
+
+static void
+l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb)
+{
+       int     err;
+
+       if (!l2->up)
+               return;
+       mISDN_HEAD_PRIM(skb) = prim;
+       mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr;
+       err = l2->up->send(l2->up, skb);
+       if (err) {
+               printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+               dev_kfree_skb(skb);
+       }
+}
+
+static void
+l2up_create(struct layer2 *l2, u_int prim, int len, void *arg)
+{
+       struct sk_buff  *skb;
+       struct mISDNhead *hh;
+       int             err;
+
+       if (!l2->up)
+               return;
+       skb = mI_alloc_skb(len, GFP_ATOMIC);
+       if (!skb)
+               return;
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = (l2->ch.nr << 16) | l2->ch.addr;
+       if (len)
+               memcpy(skb_put(skb, len), arg, len);
+       err = l2->up->send(l2->up, skb);
+       if (err) {
+               printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+               dev_kfree_skb(skb);
+       }
+}
+
+static int
+l2down_skb(struct layer2 *l2, struct sk_buff *skb) {
+       int ret;
+
+       ret = l2->ch.recv(l2->ch.peer, skb);
+       if (ret && (*debug & DEBUG_L2_RECV))
+               printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret);
+       return ret;
+}
+
+static int
+l2down_raw(struct layer2 *l2, struct sk_buff *skb)
+{
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+       if (hh->prim == PH_DATA_REQ) {
+               if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+                       skb_queue_tail(&l2->down_queue, skb);
+                       return 0;
+               }
+               l2->down_id = mISDN_HEAD_ID(skb);
+       }
+       return l2down_skb(l2, skb);
+}
+
+static int
+l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb)
+{
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+       hh->prim = prim;
+       hh->id = id;
+       return l2down_raw(l2, skb);
+}
+
+static int
+l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg)
+{
+       struct sk_buff  *skb;
+       int             err;
+       struct mISDNhead *hh;
+
+       skb = mI_alloc_skb(len, GFP_ATOMIC);
+       if (!skb)
+               return -ENOMEM;
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = id;
+       if (len)
+               memcpy(skb_put(skb, len), arg, len);
+       err = l2down_raw(l2, skb);
+       if (err)
+               dev_kfree_skb(skb);
+       return err;
+}
+
+static int
+ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) {
+       struct sk_buff *nskb = skb;
+       int ret = -EAGAIN;
+
+       if (test_bit(FLG_L1_NOTREADY, &l2->flag)) {
+               if (hh->id == l2->down_id) {
+                       nskb = skb_dequeue(&l2->down_queue);
+                       if (nskb) {
+                               l2->down_id = mISDN_HEAD_ID(nskb);
+                               if (l2down_skb(l2, nskb)) {
+                                       dev_kfree_skb(nskb);
+                                       l2->down_id = MISDN_ID_NONE;
+                               }
+                       } else
+                               l2->down_id = MISDN_ID_NONE;
+                       if (ret) {
+                               dev_kfree_skb(skb);
+                               ret = 0;
+                       }
+                       if (l2->down_id == MISDN_ID_NONE) {
+                               test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+                               mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+                       }
+               }
+       }
+       if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+               nskb = skb_dequeue(&l2->down_queue);
+               if (nskb) {
+                       l2->down_id = mISDN_HEAD_ID(nskb);
+                       if (l2down_skb(l2, nskb)) {
+                               dev_kfree_skb(nskb);
+                               l2->down_id = MISDN_ID_NONE;
+                               test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+                       }
+               } else
+                       test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+       }
+       return ret;
+}
+
+static int
+l2mgr(struct layer2 *l2, u_int prim, void *arg) {
+       long c = (long)arg;
+
+       printk(KERN_WARNING
+           "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c);
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               !test_bit(FLG_FIXED_TEI, &l2->flag)) {
+               switch (c) {
+               case 'C':
+               case 'D':
+               case 'G':
+               case 'H':
+                       l2_tei(l2, prim, (u_long)arg);
+                       break;
+               }
+       }
+       return 0;
+}
+
+static void
+set_peer_busy(struct layer2 *l2) {
+       test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+       if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue))
+               test_and_set_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+clear_peer_busy(struct layer2 *l2) {
+       if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+               test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct layer2 *l2)
+{
+       int i;
+
+       for (i = 0; i < MAX_WINDOW; i++)
+               l2->windowar[i] = NULL;
+}
+
+static int
+freewin(struct layer2 *l2)
+{
+       int i, cnt = 0;
+
+       for (i = 0; i < MAX_WINDOW; i++) {
+               if (l2->windowar[i]) {
+                       cnt++;
+                       dev_kfree_skb(l2->windowar[i]);
+                       l2->windowar[i] = NULL;
+               }
+       }
+       return cnt;
+}
+
+static void
+ReleaseWin(struct layer2 *l2)
+{
+       int cnt = freewin(l2);
+
+       if (cnt)
+               printk(KERN_WARNING
+                   "isdnl2 freed %d skbuffs in release\n", cnt);
+}
+
+inline unsigned int
+cansend(struct layer2 *l2)
+{
+       unsigned int p1;
+
+       if (test_bit(FLG_MOD128, &l2->flag))
+               p1 = (l2->vs - l2->va) % 128;
+       else
+               p1 = (l2->vs - l2->va) % 8;
+       return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag);
+}
+
+inline void
+clear_exception(struct layer2 *l2)
+{
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       test_and_clear_bit(FLG_REJEXC, &l2->flag);
+       test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+       clear_peer_busy(l2);
+}
+
+static int
+sethdraddr(struct layer2 *l2, u_char *header, int rsp)
+{
+       u_char *ptr = header;
+       int crbit = rsp;
+
+       if (test_bit(FLG_LAPD, &l2->flag)) {
+               if (test_bit(FLG_LAPD_NET, &l2->flag))
+                       crbit = !crbit;
+               *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0);
+               *ptr++ = (l2->tei << 1) | 1;
+               return 2;
+       } else {
+               if (test_bit(FLG_ORIG, &l2->flag))
+                       crbit = !crbit;
+               if (crbit)
+                       *ptr++ = l2->addr.B;
+               else
+                       *ptr++ = l2->addr.A;
+               return 1;
+       }
+}
+
+static inline void
+enqueue_super(struct layer2 *l2, struct sk_buff *skb)
+{
+       if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+               dev_kfree_skb(skb);
+}
+
+static inline void
+enqueue_ui(struct layer2 *l2, struct sk_buff *skb)
+{
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UI_IND, 0);
+       if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+               dev_kfree_skb(skb);
+}
+
+inline int
+IsUI(u_char *data)
+{
+       return (data[0] & 0xef) == UI;
+}
+
+inline int
+IsUA(u_char *data)
+{
+       return (data[0] & 0xef) == UA;
+}
+
+inline int
+IsDM(u_char *data)
+{
+       return (data[0] & 0xef) == DM;
+}
+
+inline int
+IsDISC(u_char *data)
+{
+       return (data[0] & 0xef) == DISC;
+}
+
+inline int
+IsRR(u_char *data, struct layer2 *l2)
+{
+       if (test_bit(FLG_MOD128, &l2->flag))
+               return data[0] == RR;
+       else
+               return (data[0] & 0xf) == 1;
+}
+
+inline int
+IsSFrame(u_char *data, struct layer2 *l2)
+{
+       register u_char d = *data;
+
+       if (!test_bit(FLG_MOD128, &l2->flag))
+               d &= 0xf;
+       return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c);
+}
+
+inline int
+IsSABME(u_char *data, struct layer2 *l2)
+{
+       u_char d = data[0] & ~0x10;
+
+       return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM;
+}
+
+inline int
+IsREJ(u_char *data, struct layer2 *l2)
+{
+       return test_bit(FLG_MOD128, &l2->flag) ?
+               data[0] == REJ : (data[0] & 0xf) == REJ;
+}
+
+inline int
+IsFRMR(u_char *data)
+{
+       return (data[0] & 0xef) == FRMR;
+}
+
+inline int
+IsRNR(u_char *data, struct layer2 *l2)
+{
+       return test_bit(FLG_MOD128, &l2->flag) ?
+           data[0] == RNR : (data[0] & 0xf) == RNR;
+}
+
+int
+iframe_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       u_int   i;
+       int     rsp = *skb->data & 0x2;
+
+       i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1);
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (rsp)
+               return 'L';
+       if (skb->len < i)
+               return 'N';
+       if ((skb->len - i) > l2->maxlen)
+               return 'O';
+       return 0;
+}
+
+int
+super_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       if (skb->len != l2addrsize(l2) +
+           (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1))
+               return 'N';
+       return 0;
+}
+
+int
+unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp)
+{
+       int rsp = (*skb->data & 0x2) >> 1;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (rsp != wantrsp)
+               return 'L';
+       if (skb->len != l2addrsize(l2) + 1)
+               return 'N';
+       return 0;
+}
+
+int
+UI_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       int rsp = *skb->data & 0x2;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (rsp)
+               return 'L';
+       if (skb->len > l2->maxlen + l2addrsize(l2) + 1)
+               return 'O';
+       return 0;
+}
+
+int
+FRMR_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       u_int   headers = l2addrsize(l2) + 1;
+       u_char  *datap = skb->data + headers;
+       int     rsp = *skb->data & 0x2;
+
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (!rsp)
+               return 'L';
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               if (skb->len < headers + 5)
+                       return 'N';
+               else if (*debug & DEBUG_L2)
+                       l2m_debug(&l2->l2m,
+                           "FRMR information %2x %2x %2x %2x %2x",
+                           datap[0], datap[1], datap[2], datap[3], datap[4]);
+       } else {
+               if (skb->len < headers + 3)
+                       return 'N';
+               else if (*debug & DEBUG_L2)
+                       l2m_debug(&l2->l2m,
+                           "FRMR information %2x %2x %2x",
+                           datap[0], datap[1], datap[2]);
+       }
+       return 0;
+}
+
+static unsigned int
+legalnr(struct layer2 *l2, unsigned int nr)
+{
+       if (test_bit(FLG_MOD128, &l2->flag))
+               return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128);
+       else
+               return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8);
+}
+
+static void
+setva(struct layer2 *l2, unsigned int nr)
+{
+       struct sk_buff  *skb;
+
+       while (l2->va != nr) {
+               l2->va++;
+               if (test_bit(FLG_MOD128, &l2->flag))
+                       l2->va %= 128;
+               else
+                       l2->va %= 8;
+               if (l2->windowar[l2->sow]) {
+                       skb_trim(l2->windowar[l2->sow], 0);
+                       skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]);
+                       l2->windowar[l2->sow] = NULL;
+               }
+               l2->sow = (l2->sow + 1) % l2->window;
+       }
+       skb = skb_dequeue(&l2->tmp_queue);
+       while (skb) {
+               dev_kfree_skb(skb);
+               skb = skb_dequeue(&l2->tmp_queue);
+       }
+}
+
+static void
+send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr)
+{
+       u_char tmp[MAX_L2HEADER_LEN];
+       int i;
+
+       i = sethdraddr(l2, tmp, cr);
+       tmp[i++] = cmd;
+       if (skb)
+               skb_trim(skb, 0);
+       else {
+               skb = mI_alloc_skb(i, GFP_ATOMIC);
+               if (!skb) {
+                       printk(KERN_WARNING "%s: can't alloc skbuff\n",
+                               __func__);
+                       return;
+               }
+       }
+       memcpy(skb_put(skb, i), tmp, i);
+       enqueue_super(l2, skb);
+}
+
+
+inline u_char
+get_PollFlag(struct layer2 *l2, struct sk_buff *skb)
+{
+       return skb->data[l2addrsize(l2)] & 0x10;
+}
+
+inline u_char
+get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb)
+{
+       u_char PF;
+
+       PF = get_PollFlag(l2, skb);
+       dev_kfree_skb(skb);
+       return PF;
+}
+
+inline void
+start_t200(struct layer2 *l2, int i)
+{
+       mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i);
+       test_and_set_bit(FLG_T200_RUN, &l2->flag);
+}
+
+inline void
+restart_t200(struct layer2 *l2, int i)
+{
+       mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i);
+       test_and_set_bit(FLG_T200_RUN, &l2->flag);
+}
+
+inline void
+stop_t200(struct layer2 *l2, int i)
+{
+       if (test_and_clear_bit(FLG_T200_RUN, &l2->flag))
+               mISDN_FsmDelTimer(&l2->t200, i);
+}
+
+inline void
+st5_dl_release_l2l3(struct layer2 *l2)
+{
+       int pr;
+
+       if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+               pr = DL_RELEASE_CNF;
+       else
+               pr = DL_RELEASE_IND;
+       l2up_create(l2, pr, 0, NULL);
+}
+
+inline void
+lapb_dl_release_l2l3(struct layer2 *l2, int f)
+{
+       if (test_bit(FLG_LAPB, &l2->flag))
+               l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL);
+       l2up_create(l2, f, 0, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+       struct layer2 *l2 = fi->userdata;
+       u_char cmd;
+
+       clear_exception(l2);
+       l2->rc = 0;
+       cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10;
+       send_uframe(l2, NULL, cmd, CMD);
+       mISDN_FsmDelTimer(&l2->t203, 1);
+       restart_t200(l2, 1);
+       test_and_clear_bit(FLG_PEND_REL, &l2->flag);
+       freewin(l2);
+       mISDN_FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       if (get_PollFlagFree(l2, skb))
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'C');
+       else
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'D');
+
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       if (get_PollFlagFree(l2, skb))
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+       else {
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'E');
+               establishlink(fi);
+               test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+       }
+}
+
+static void
+l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       if (get_PollFlagFree(l2, skb))
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+       else
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'E');
+       establishlink(fi);
+       test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+l2_go_st3(struct FsmInst *fi, int event, void *arg)
+{
+       dev_kfree_skb((struct sk_buff *)arg);
+       mISDN_FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2_mdl_assign(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L2_3);
+       dev_kfree_skb((struct sk_buff *)arg);
+       l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->ui_queue, skb);
+       mISDN_FsmChangeState(fi, ST_L2_2);
+       l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->ui_queue, skb);
+}
+
+static void
+tx_ui(struct layer2 *l2)
+{
+       struct sk_buff *skb;
+       u_char header[MAX_L2HEADER_LEN];
+       int i;
+
+       i = sethdraddr(l2, header, CMD);
+       if (test_bit(FLG_LAPD_NET, &l2->flag))
+               header[1] = 0xff; /* tei 127 */
+       header[i++] = UI;
+       while ((skb = skb_dequeue(&l2->ui_queue))) {
+               memcpy(skb_push(skb, i), header, i);
+               enqueue_ui(l2, skb);
+       }
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->ui_queue, skb);
+       tx_ui(l2);
+}
+
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_pull(skb, l2headersize(l2, 1));
+/*
+ *             in states 1-3 for broadcast
+ */
+
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UI_IND, 0);
+       l2up(l2, DL_UNITDATA_IND, skb);
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       establishlink(fi);
+       test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       test_and_clear_bit(FLG_PEND_REL, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       establishlink(fi);
+       test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_release(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_trim(skb, 0);
+       l2up(l2, DL_RELEASE_CNF, skb);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       test_and_set_bit(FLG_PEND_REL, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_disconnect(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       freewin(l2);
+       mISDN_FsmChangeState(fi, ST_L2_6);
+       l2->rc = 0;
+       send_uframe(l2, NULL, DISC | 0x10, CMD);
+       mISDN_FsmDelTimer(&l2->t203, 1);
+       restart_t200(l2, 2);
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_start_multi(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+
+       l2->vs = 0;
+       l2->va = 0;
+       l2->vr = 0;
+       l2->sow = 0;
+       clear_exception(l2);
+       send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP);
+       mISDN_FsmChangeState(fi, ST_L2_7);
+       mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
+       skb_trim(skb, 0);
+       l2up(l2, DL_ESTABLISH_IND, skb);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+}
+
+static void
+l2_send_DM(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP);
+}
+
+static void
+l2_restart_multi(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+       int             est = 0;
+
+       send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+
+       l2mgr(l2, MDL_ERROR_IND, (void *) 'F');
+
+       if (l2->vs != l2->va) {
+               skb_queue_purge(&l2->i_queue);
+               est = 1;
+       }
+
+       clear_exception(l2);
+       l2->vs = 0;
+       l2->va = 0;
+       l2->vr = 0;
+       l2->sow = 0;
+       mISDN_FsmChangeState(fi, ST_L2_7);
+       stop_t200(l2, 3);
+       mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
+
+       if (est)
+               l2up_create(l2, DL_ESTABLISH_IND, 0, NULL);
+/*             mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ *                 MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
+ *                 0, NULL, 0);
+ */
+       if (skb_queue_len(&l2->i_queue) && cansend(l2))
+               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+}
+
+static void
+l2_stop_multi(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       mISDN_FsmDelTimer(&l2->t203, 3);
+       stop_t200(l2, 4);
+
+       send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+       skb_queue_purge(&l2->i_queue);
+       freewin(l2);
+       lapb_dl_release_l2l3(l2, DL_RELEASE_IND);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+       int pr = -1;
+
+       if (!get_PollFlag(l2, skb)) {
+               l2_mdl_error_ua(fi, event, arg);
+               return;
+       }
+       dev_kfree_skb(skb);
+       if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+               l2_disconnect(fi, event, NULL);
+       if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) {
+               pr = DL_ESTABLISH_CNF;
+       } else if (l2->vs != l2->va) {
+               skb_queue_purge(&l2->i_queue);
+               pr = DL_ESTABLISH_IND;
+       }
+       stop_t200(l2, 5);
+       l2->vr = 0;
+       l2->vs = 0;
+       l2->va = 0;
+       l2->sow = 0;
+       mISDN_FsmChangeState(fi, ST_L2_7);
+       mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4);
+       if (pr != -1)
+               l2up_create(l2, pr, 0, NULL);
+
+       if (skb_queue_len(&l2->i_queue) && cansend(l2))
+               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!get_PollFlag(l2, skb)) {
+               l2_mdl_error_ua(fi, event, arg);
+               return;
+       }
+       dev_kfree_skb(skb);
+       stop_t200(l2, 6);
+       lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!get_PollFlagFree(l2, skb)) {
+               establishlink(fi);
+               test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       }
+}
+
+static void
+l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (get_PollFlagFree(l2, skb)) {
+               stop_t200(l2, 7);
+               if (!test_bit(FLG_L3_INIT, &l2->flag))
+                       skb_queue_purge(&l2->i_queue);
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       l2down_create(l2, PH_DEACTIVATE_REQ,
+                               l2_newid(l2), 0, NULL);
+               st5_dl_release_l2l3(l2);
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       }
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (get_PollFlagFree(l2, skb)) {
+               stop_t200(l2, 8);
+               lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       }
+}
+
+void
+enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf)
+{
+       struct sk_buff *skb;
+       u_char tmp[MAX_L2HEADER_LEN];
+       int i;
+
+       i = sethdraddr(l2, tmp, cr);
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               tmp[i++] = typ;
+               tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
+       } else
+               tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0);
+       skb = mI_alloc_skb(i, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_WARNING
+                   "isdnl2 can't alloc sbbuff for enquiry_cr\n");
+               return;
+       }
+       memcpy(skb_put(skb, i), tmp, i);
+       enqueue_super(l2, skb);
+}
+
+inline void
+enquiry_response(struct layer2 *l2)
+{
+       if (test_bit(FLG_OWN_BUSY, &l2->flag))
+               enquiry_cr(l2, RNR, RSP, 1);
+       else
+               enquiry_cr(l2, RR, RSP, 1);
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+}
+
+inline void
+transmit_enquiry(struct layer2 *l2)
+{
+       if (test_bit(FLG_OWN_BUSY, &l2->flag))
+               enquiry_cr(l2, RNR, CMD, 1);
+       else
+               enquiry_cr(l2, RR, CMD, 1);
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       start_t200(l2, 9);
+}
+
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       l2mgr(l2, MDL_ERROR_IND, (void *) 'J');
+       establishlink(fi);
+       test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+invoke_retransmission(struct layer2 *l2, unsigned int nr)
+{
+       u_int   p1;
+
+       if (l2->vs != nr) {
+               while (l2->vs != nr) {
+                       (l2->vs)--;
+                       if (test_bit(FLG_MOD128, &l2->flag)) {
+                               l2->vs %= 128;
+                               p1 = (l2->vs - l2->va) % 128;
+                       } else {
+                               l2->vs %= 8;
+                               p1 = (l2->vs - l2->va) % 8;
+                       }
+                       p1 = (p1 + l2->sow) % l2->window;
+                       if (l2->windowar[p1])
+                               skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+                       else
+                               printk(KERN_WARNING
+                                   "%s: windowar[%d] is NULL\n",
+                                   __func__, p1);
+                       l2->windowar[p1] = NULL;
+               }
+               mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+       }
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+       int PollFlag, rsp, typ = RR;
+       unsigned int nr;
+
+       rsp = *skb->data & 0x2;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+
+       skb_pull(skb, l2addrsize(l2));
+       if (IsRNR(skb->data, l2)) {
+               set_peer_busy(l2);
+               typ = RNR;
+       } else
+               clear_peer_busy(l2);
+       if (IsREJ(skb->data, l2))
+               typ = REJ;
+
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               PollFlag = (skb->data[1] & 0x1) == 0x1;
+               nr = skb->data[1] >> 1;
+       } else {
+               PollFlag = (skb->data[0] & 0x10);
+               nr = (skb->data[0] >> 5) & 0x7;
+       }
+       dev_kfree_skb(skb);
+
+       if (PollFlag) {
+               if (rsp)
+                       l2mgr(l2, MDL_ERROR_IND, (void *) 'A');
+               else
+                       enquiry_response(l2);
+       }
+       if (legalnr(l2, nr)) {
+               if (typ == REJ) {
+                       setva(l2, nr);
+                       invoke_retransmission(l2, nr);
+                       stop_t200(l2, 10);
+                       if (mISDN_FsmAddTimer(&l2->t203, l2->T203,
+                                       EV_L2_T203, NULL, 6))
+                               l2m_debug(&l2->l2m, "Restart T203 ST7 REJ");
+               } else if ((nr == l2->vs) && (typ == RR)) {
+                       setva(l2, nr);
+                       stop_t200(l2, 11);
+                       mISDN_FsmRestartTimer(&l2->t203, l2->T203,
+                                       EV_L2_T203, NULL, 7);
+               } else if ((l2->va != nr) || (typ == RNR)) {
+                       setva(l2, nr);
+                       if (typ != RR)
+                               mISDN_FsmDelTimer(&l2->t203, 9);
+                       restart_t200(l2, 12);
+               }
+               if (skb_queue_len(&l2->i_queue) && (typ == RR))
+                       mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+       } else
+               nrerrorrecovery(fi);
+}
+
+static void
+l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!test_bit(FLG_L3_INIT, &l2->flag))
+               skb_queue_tail(&l2->i_queue, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->i_queue, skb);
+       mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->i_queue, skb);
+}
+
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+       int             PollFlag, i;
+       u_int           ns, nr;
+
+       i = l2addrsize(l2);
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+               ns = skb->data[i] >> 1;
+               nr = (skb->data[i + 1] >> 1) & 0x7f;
+       } else {
+               PollFlag = (skb->data[i] & 0x10);
+               ns = (skb->data[i] >> 1) & 0x7;
+               nr = (skb->data[i] >> 5) & 0x7;
+       }
+       if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+               dev_kfree_skb(skb);
+               if (PollFlag)
+                       enquiry_response(l2);
+       } else {
+               if (l2->vr == ns) {
+                       l2->vr++;
+                       if (test_bit(FLG_MOD128, &l2->flag))
+                               l2->vr %= 128;
+                       else
+                               l2->vr %= 8;
+                       test_and_clear_bit(FLG_REJEXC, &l2->flag);
+                       if (PollFlag)
+                               enquiry_response(l2);
+                       else
+                               test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+                       skb_pull(skb, l2headersize(l2, 0));
+                       l2up(l2, DL_DATA_IND, skb);
+               } else {
+                       /* n(s)!=v(r) */
+                       dev_kfree_skb(skb);
+                       if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+                               if (PollFlag)
+                                       enquiry_response(l2);
+                       } else {
+                               enquiry_cr(l2, REJ, RSP, PollFlag);
+                               test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+                       }
+               }
+       }
+       if (legalnr(l2, nr)) {
+               if (!test_bit(FLG_PEER_BUSY, &l2->flag) &&
+                   (fi->state == ST_L2_7)) {
+                       if (nr == l2->vs) {
+                               stop_t200(l2, 13);
+                               mISDN_FsmRestartTimer(&l2->t203, l2->T203,
+                                               EV_L2_T203, NULL, 7);
+                       } else if (nr != l2->va)
+                               restart_t200(l2, 14);
+               }
+               setva(l2, nr);
+       } else {
+               nrerrorrecovery(fi);
+               return;
+       }
+       if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7))
+               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+       if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag))
+               enquiry_cr(l2, RR, RSP, 0);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       u_int           info;
+
+       l2->tei = (signed char)(long)arg;
+       set_channel_address(&l2->ch, l2->sapi, l2->tei);
+       info = DL_INFO_L2_CONNECT;
+       l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info);
+       if (fi->state == ST_L2_3) {
+               establishlink(fi);
+               test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       } else
+               mISDN_FsmChangeState(fi, ST_L2_4);
+       if (skb_queue_len(&l2->ui_queue))
+               tx_ui(l2);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+       } else if (l2->rc == l2->N200) {
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+               skb_queue_purge(&l2->i_queue);
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'G');
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       l2down_create(l2, PH_DEACTIVATE_REQ,
+                               l2_newid(l2), 0, NULL);
+               st5_dl_release_l2l3(l2);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       } else {
+               l2->rc++;
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+               send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ?
+                       SABME : SABM) | 0x10, CMD);
+       }
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+       } else if (l2->rc == l2->N200) {
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'H');
+               lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       } else {
+               l2->rc++;
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200,
+                           NULL, 9);
+               send_uframe(l2, NULL, DISC | 0x10, CMD);
+       }
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+               return;
+       }
+       test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+       l2->rc = 0;
+       mISDN_FsmChangeState(fi, ST_L2_8);
+       transmit_enquiry(l2);
+       l2->rc++;
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+               return;
+       }
+       test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+       if (l2->rc == l2->N200) {
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'I');
+               establishlink(fi);
+               test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+       } else {
+               transmit_enquiry(l2);
+               l2->rc++;
+       }
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9);
+               return;
+       }
+       mISDN_FsmChangeState(fi, ST_L2_8);
+       transmit_enquiry(l2);
+       l2->rc = 0;
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb, *nskb, *oskb;
+       u_char          header[MAX_L2HEADER_LEN];
+       u_int           i, p1;
+
+       if (!cansend(l2))
+               return;
+
+       skb = skb_dequeue(&l2->i_queue);
+       if (!skb)
+               return;
+
+       if (test_bit(FLG_MOD128, &l2->flag))
+               p1 = (l2->vs - l2->va) % 128;
+       else
+               p1 = (l2->vs - l2->va) % 8;
+       p1 = (p1 + l2->sow) % l2->window;
+       if (l2->windowar[p1]) {
+               printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
+                   p1);
+               dev_kfree_skb(l2->windowar[p1]);
+       }
+       l2->windowar[p1] = skb;
+       i = sethdraddr(l2, header, CMD);
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               header[i++] = l2->vs << 1;
+               header[i++] = l2->vr << 1;
+               l2->vs = (l2->vs + 1) % 128;
+       } else {
+               header[i++] = (l2->vr << 5) | (l2->vs << 1);
+               l2->vs = (l2->vs + 1) % 8;
+       }
+
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       p1 = skb_headroom(nskb);
+       if (p1 >= i)
+               memcpy(skb_push(nskb, i), header, i);
+       else {
+               printk(KERN_WARNING
+                   "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+               oskb = nskb;
+               nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC);
+               if (!nskb) {
+                       dev_kfree_skb(oskb);
+                       printk(KERN_WARNING "%s: no skb mem\n", __func__);
+                       return;
+               }
+               memcpy(skb_put(nskb, i), header, i);
+               memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len);
+               dev_kfree_skb(oskb);
+       }
+       l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb);
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) {
+               mISDN_FsmDelTimer(&l2->t203, 13);
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11);
+       }
+}
+
+static void
+l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+       int PollFlag, rsp, rnr = 0;
+       unsigned int nr;
+
+       rsp = *skb->data & 0x2;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+
+       skb_pull(skb, l2addrsize(l2));
+
+       if (IsRNR(skb->data, l2)) {
+               set_peer_busy(l2);
+               rnr = 1;
+       } else
+               clear_peer_busy(l2);
+
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               PollFlag = (skb->data[1] & 0x1) == 0x1;
+               nr = skb->data[1] >> 1;
+       } else {
+               PollFlag = (skb->data[0] & 0x10);
+               nr = (skb->data[0] >> 5) & 0x7;
+       }
+       dev_kfree_skb(skb);
+       if (rsp && PollFlag) {
+               if (legalnr(l2, nr)) {
+                       if (rnr) {
+                               restart_t200(l2, 15);
+                       } else {
+                               stop_t200(l2, 16);
+                               mISDN_FsmAddTimer(&l2->t203, l2->T203,
+                                           EV_L2_T203, NULL, 5);
+                               setva(l2, nr);
+                       }
+                       invoke_retransmission(l2, nr);
+                       mISDN_FsmChangeState(fi, ST_L2_7);
+                       if (skb_queue_len(&l2->i_queue) && cansend(l2))
+                               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+               } else
+                       nrerrorrecovery(fi);
+       } else {
+               if (!rsp && PollFlag)
+                       enquiry_response(l2);
+               if (legalnr(l2, nr))
+                       setva(l2, nr);
+               else
+                       nrerrorrecovery(fi);
+       }
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_pull(skb, l2addrsize(l2) + 1);
+
+       if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */
+           (IsUA(skb->data) && (fi->state == ST_L2_7))) {
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'K');
+               establishlink(fi);
+               test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+       }
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->ui_queue);
+       l2->tei = GROUP_TEI;
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->ui_queue);
+       l2->tei = GROUP_TEI;
+       l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       l2->tei = GROUP_TEI;
+       stop_t200(l2, 17);
+       st5_dl_release_l2l3(l2);
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->ui_queue);
+       l2->tei = GROUP_TEI;
+       stop_t200(l2, 18);
+       l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       l2->tei = GROUP_TEI;
+       stop_t200(l2, 17);
+       mISDN_FsmDelTimer(&l2->t203, 19);
+       l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+/*     mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ *             MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED,
+ *             0, NULL, 0);
+ */
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+               l2up(l2, DL_RELEASE_IND, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       stop_t200(l2, 19);
+       st5_dl_release_l2l3(l2);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->ui_queue);
+       stop_t200(l2, 20);
+       l2up(l2, DL_RELEASE_CNF, skb);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       stop_t200(l2, 19);
+       mISDN_FsmDelTimer(&l2->t203, 19);
+       l2up(l2, DL_RELEASE_IND, skb);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) {
+               enquiry_cr(l2, RNR, RSP, 0);
+               test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_clear_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) {
+               enquiry_cr(l2, RR, RSP, 0);
+               test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_frame_error(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       l2mgr(l2, MDL_ERROR_IND, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       l2mgr(l2, MDL_ERROR_IND, arg);
+       establishlink(fi);
+       test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static struct FsmNode L2FnList[] =
+{
+       {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign},
+       {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3},
+       {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish},
+       {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3},
+       {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+       {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+       {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release},
+       {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel},
+       {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+       {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+       {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest},
+       {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull},
+       {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue},
+       {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign},
+       {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui},
+       {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui},
+       {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei},
+       {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei},
+       {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+       {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove},
+       {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove},
+       {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove},
+       {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove},
+       {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove},
+       {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
+       {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
+       {ST_L2_4, EV_L2_SABME, l2_start_multi},
+       {ST_L2_5, EV_L2_SABME, l2_send_UA},
+       {ST_L2_6, EV_L2_SABME, l2_send_DM},
+       {ST_L2_7, EV_L2_SABME, l2_restart_multi},
+       {ST_L2_8, EV_L2_SABME, l2_restart_multi},
+       {ST_L2_4, EV_L2_DISC, l2_send_DM},
+       {ST_L2_5, EV_L2_DISC, l2_send_DM},
+       {ST_L2_6, EV_L2_DISC, l2_send_UA},
+       {ST_L2_7, EV_L2_DISC, l2_stop_multi},
+       {ST_L2_8, EV_L2_DISC, l2_stop_multi},
+       {ST_L2_4, EV_L2_UA, l2_mdl_error_ua},
+       {ST_L2_5, EV_L2_UA, l2_connected},
+       {ST_L2_6, EV_L2_UA, l2_released},
+       {ST_L2_7, EV_L2_UA, l2_mdl_error_ua},
+       {ST_L2_8, EV_L2_UA, l2_mdl_error_ua},
+       {ST_L2_4, EV_L2_DM, l2_reestablish},
+       {ST_L2_5, EV_L2_DM, l2_st5_dm_release},
+       {ST_L2_6, EV_L2_DM, l2_st6_dm_release},
+       {ST_L2_7, EV_L2_DM, l2_mdl_error_dm},
+       {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm},
+       {ST_L2_1, EV_L2_UI, l2_got_ui},
+       {ST_L2_2, EV_L2_UI, l2_got_ui},
+       {ST_L2_3, EV_L2_UI, l2_got_ui},
+       {ST_L2_4, EV_L2_UI, l2_got_ui},
+       {ST_L2_5, EV_L2_UI, l2_got_ui},
+       {ST_L2_6, EV_L2_UI, l2_got_ui},
+       {ST_L2_7, EV_L2_UI, l2_got_ui},
+       {ST_L2_8, EV_L2_UI, l2_got_ui},
+       {ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
+       {ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
+       {ST_L2_7, EV_L2_SUPER, l2_st7_got_super},
+       {ST_L2_8, EV_L2_SUPER, l2_st8_got_super},
+       {ST_L2_7, EV_L2_I, l2_got_iframe},
+       {ST_L2_8, EV_L2_I, l2_got_iframe},
+       {ST_L2_5, EV_L2_T200, l2_st5_tout_200},
+       {ST_L2_6, EV_L2_T200, l2_st6_tout_200},
+       {ST_L2_7, EV_L2_T200, l2_st7_tout_200},
+       {ST_L2_8, EV_L2_T200, l2_st8_tout_200},
+       {ST_L2_7, EV_L2_T203, l2_st7_tout_203},
+       {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+       {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+       {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+       {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+       {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+       {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error},
+       {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error},
+       {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error},
+       {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+       {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+       {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+       {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove},
+       {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove},
+       {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+       {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da},
+       {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da},
+       {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
+       {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static int
+ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb)
+{
+       u_char  *datap = skb->data;
+       int     ret = -EINVAL;
+       int     psapi, ptei;
+       u_int   l;
+       int     c = 0;
+
+       l = l2addrsize(l2);
+       if (skb->len <= l) {
+               mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N');
+               return ret;
+       }
+       if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */
+               psapi = *datap++;
+               ptei = *datap++;
+               if ((psapi & 1) || !(ptei & 1)) {
+                       printk(KERN_WARNING
+                           "l2 D-channel frame wrong EA0/EA1\n");
+                       return ret;
+               }
+               psapi >>= 2;
+               ptei >>= 1;
+               if (psapi != l2->sapi) {
+                       /* not our bussiness
+                        * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n",
+                        *  __func__,
+                        *      psapi, l2->sapi);
+                        */
+                       dev_kfree_skb(skb);
+                       return 0;
+               }
+               if ((ptei != l2->tei) && (ptei != GROUP_TEI)) {
+                       /* not our bussiness
+                        * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n",
+                        *  __func__,
+                        *      ptei, l2->tei, psapi);
+                        */
+                       dev_kfree_skb(skb);
+                       return 0;
+               }
+       } else
+               datap += l;
+       if (!(*datap & 1)) {    /* I-Frame */
+               c = iframe_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb);
+       } else if (IsSFrame(datap, l2)) {       /* S-Frame */
+               c = super_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb);
+       } else if (IsUI(datap)) {
+               c = UI_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb);
+       } else if (IsSABME(datap, l2)) {
+               c = unnum_error(l2, skb, CMD);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb);
+       } else if (IsUA(datap)) {
+               c = unnum_error(l2, skb, RSP);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb);
+       } else if (IsDISC(datap)) {
+               c = unnum_error(l2, skb, CMD);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb);
+       } else if (IsDM(datap)) {
+               c = unnum_error(l2, skb, RSP);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb);
+       } else if (IsFRMR(datap)) {
+               c = FRMR_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb);
+       } else
+               c = 'L';
+       if (c) {
+               printk(KERN_WARNING "l2 D-channel frame error %c\n", c);
+               mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c);
+       }
+       return ret;
+}
+
+static int
+l2_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct layer2           *l2 = container_of(ch, struct layer2, ch);
+       struct mISDNhead        *hh =  mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+
+       if (*debug & DEBUG_L2_RECV)
+               printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n",
+                   __func__, hh->prim, hh->id, l2->tei);
+       switch (hh->prim) {
+       case PH_DATA_IND:
+               ret = ph_data_indication(l2, hh, skb);
+               break;
+       case PH_DATA_CNF:
+               ret = ph_data_confirm(l2, hh, skb);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_L1_ACTIV, &l2->flag);
+               l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL);
+               if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+                       ret = mISDN_FsmEvent(&l2->l2m,
+                               EV_L2_DL_ESTABLISH_REQ, skb);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_L1_ACTIV, &l2->flag);
+               l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL);
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb);
+               break;
+       case MPH_INFORMATION_IND:
+               if (!l2->up)
+                       break;
+               ret = l2->up->send(l2->up, skb);
+               break;
+       case DL_DATA_REQ:
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb);
+               break;
+       case DL_UNITDATA_REQ:
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb);
+               break;
+       case DL_ESTABLISH_REQ:
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       test_and_set_bit(FLG_ORIG, &l2->flag);
+               if (test_bit(FLG_L1_ACTIV, &l2->flag)) {
+                       if (test_bit(FLG_LAPD, &l2->flag) ||
+                               test_bit(FLG_ORIG, &l2->flag))
+                               ret = mISDN_FsmEvent(&l2->l2m,
+                                       EV_L2_DL_ESTABLISH_REQ, skb);
+               } else {
+                       if (test_bit(FLG_LAPD, &l2->flag) ||
+                               test_bit(FLG_ORIG, &l2->flag)) {
+                               test_and_set_bit(FLG_ESTAB_PEND,
+                                       &l2->flag);
+                       }
+                       ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2),
+                           skb);
+               }
+               break;
+       case DL_RELEASE_REQ:
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       l2down_create(l2, PH_DEACTIVATE_REQ,
+                               l2_newid(l2), 0, NULL);
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ,
+                   skb);
+               break;
+       default:
+               if (*debug & DEBUG_L2)
+                       l2m_debug(&l2->l2m, "l2 unknown pr %04x",
+                           hh->prim);
+       }
+       if (ret) {
+               dev_kfree_skb(skb);
+               ret = 0;
+       }
+       return ret;
+}
+
+int
+tei_l2(struct layer2 *l2, u_int cmd, u_long arg)
+{
+       int             ret = -EINVAL;
+
+       if (*debug & DEBUG_L2_TEI)
+               printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+       switch (cmd) {
+       case (MDL_ASSIGN_REQ):
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg);
+               break;
+       case (MDL_REMOVE_REQ):
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL);
+               break;
+       case (MDL_ERROR_IND):
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+               break;
+       case (MDL_ERROR_RSP):
+               /* ETS 300-125 5.3.2.1 Test: TC13010 */
+               printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n");
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+               break;
+       }
+       return ret;
+}
+
+static void
+release_l2(struct layer2 *l2)
+{
+       mISDN_FsmDelTimer(&l2->t200, 21);
+       mISDN_FsmDelTimer(&l2->t203, 16);
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       skb_queue_purge(&l2->down_queue);
+       ReleaseWin(l2);
+       if (test_bit(FLG_LAPD, &l2->flag)) {
+               TEIrelease(l2);
+               if (l2->ch.st)
+                       l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D,
+                           CLOSE_CHANNEL, NULL);
+       }
+       kfree(l2);
+}
+
+static int
+l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct layer2           *l2 = container_of(ch, struct layer2, ch);
+       u_int                   info;
+
+       if (*debug & DEBUG_L2_CTRL)
+               printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               if (test_bit(FLG_LAPD, &l2->flag)) {
+                       set_channel_address(&l2->ch, l2->sapi, l2->tei);
+                       info = DL_INFO_L2_CONNECT;
+                       l2up_create(l2, DL_INFORMATION_IND,
+                           sizeof(info), &info);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               if (l2->ch.peer)
+                       l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL);
+               release_l2(l2);
+               break;
+       }
+       return 0;
+}
+
+struct layer2 *
+create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg)
+{
+       struct layer2           *l2;
+       struct channel_req      rq;
+
+       l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL);
+       if (!l2) {
+               printk(KERN_ERR "kzalloc layer2 failed\n");
+               return NULL;
+       }
+       l2->next_id = 1;
+       l2->down_id = MISDN_ID_NONE;
+       l2->up = ch;
+       l2->ch.st = ch->st;
+       l2->ch.send = l2_send;
+       l2->ch.ctrl = l2_ctrl;
+       switch (protocol) {
+       case ISDN_P_LAPD_NT:
+               test_and_set_bit(FLG_LAPD, &l2->flag);
+               test_and_set_bit(FLG_LAPD_NET, &l2->flag);
+               test_and_set_bit(FLG_MOD128, &l2->flag);
+               l2->sapi = 0;
+               l2->maxlen = MAX_DFRAME_LEN;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       l2->window = 7;
+               else
+                       l2->window = 1;
+               if (test_bit(OPTION_L2_PTP, &options))
+                       test_and_set_bit(FLG_PTP, &l2->flag);
+               if (test_bit(OPTION_L2_FIXEDTEI, &options))
+                       test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+               l2->tei = (u_int)arg;
+               l2->T200 = 1000;
+               l2->N200 = 3;
+               l2->T203 = 10000;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       rq.protocol = ISDN_P_NT_E1;
+               else
+                       rq.protocol = ISDN_P_NT_S0;
+               rq.adr.channel = 0;
+               l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+               break;
+       case ISDN_P_LAPD_TE:
+               test_and_set_bit(FLG_LAPD, &l2->flag);
+               test_and_set_bit(FLG_MOD128, &l2->flag);
+               test_and_set_bit(FLG_ORIG, &l2->flag);
+               l2->sapi = 0;
+               l2->maxlen = MAX_DFRAME_LEN;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       l2->window = 7;
+               else
+                       l2->window = 1;
+               if (test_bit(OPTION_L2_PTP, &options))
+                       test_and_set_bit(FLG_PTP, &l2->flag);
+               if (test_bit(OPTION_L2_FIXEDTEI, &options))
+                       test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+               l2->tei = (u_int)arg;
+               l2->T200 = 1000;
+               l2->N200 = 3;
+               l2->T203 = 10000;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       rq.protocol = ISDN_P_TE_E1;
+               else
+                       rq.protocol = ISDN_P_TE_S0;
+               rq.adr.channel = 0;
+               l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+               break;
+       case ISDN_P_B_X75SLP:
+               test_and_set_bit(FLG_LAPB, &l2->flag);
+               l2->window = 7;
+               l2->maxlen = MAX_DATA_SIZE;
+               l2->T200 = 1000;
+               l2->N200 = 4;
+               l2->T203 = 5000;
+               l2->addr.A = 3;
+               l2->addr.B = 1;
+               break;
+       default:
+               printk(KERN_ERR "layer2 create failed prt %x\n",
+                       protocol);
+               kfree(l2);
+               return NULL;
+       }
+       skb_queue_head_init(&l2->i_queue);
+       skb_queue_head_init(&l2->ui_queue);
+       skb_queue_head_init(&l2->down_queue);
+       skb_queue_head_init(&l2->tmp_queue);
+       InitWin(l2);
+       l2->l2m.fsm = &l2fsm;
+       if (test_bit(FLG_LAPB, &l2->flag) ||
+               test_bit(FLG_PTP, &l2->flag) ||
+               test_bit(FLG_LAPD_NET, &l2->flag))
+               l2->l2m.state = ST_L2_4;
+       else
+               l2->l2m.state = ST_L2_1;
+       l2->l2m.debug = *debug;
+       l2->l2m.userdata = l2;
+       l2->l2m.userint = 0;
+       l2->l2m.printdebug = l2m_debug;
+
+       mISDN_FsmInitTimer(&l2->l2m, &l2->t200);
+       mISDN_FsmInitTimer(&l2->l2m, &l2->t203);
+       return l2;
+}
+
+static int
+x75create(struct channel_req *crq)
+{
+       struct layer2   *l2;
+
+       if (crq->protocol != ISDN_P_B_X75SLP)
+               return -EPROTONOSUPPORT;
+       l2 = create_l2(crq->ch, crq->protocol, 0, 0);
+       if (!l2)
+               return -ENOMEM;
+       crq->ch = &l2->ch;
+       crq->protocol = ISDN_P_B_HDLC;
+       return 0;
+}
+
+static struct Bprotocol X75SLP = {
+       .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)),
+       .name = "X75SLP",
+       .create = x75create
+};
+
+int
+Isdnl2_Init(u_int *deb)
+{
+       debug = deb;
+       mISDN_register_Bprotocol(&X75SLP);
+       l2fsm.state_count = L2_STATE_COUNT;
+       l2fsm.event_count = L2_EVENT_COUNT;
+       l2fsm.strEvent = strL2Event;
+       l2fsm.strState = strL2State;
+       mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList));
+       TEIInit(deb);
+       return 0;
+}
+
+void
+Isdnl2_cleanup(void)
+{
+       mISDN_unregister_Bprotocol(&X75SLP);
+       TEIFree();
+       mISDN_FsmFree(&l2fsm);
+}
+
diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h
new file mode 100644 (file)
index 0000000..6293f80
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Layer 2 defines
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/skbuff.h>
+#include "fsm.h"
+
+#define MAX_WINDOW     8
+
+struct manager {
+       struct mISDNchannel     ch;
+       struct mISDNchannel     bcast;
+       u_long                  options;
+       struct list_head        layer2;
+       rwlock_t                lock;
+       struct FsmInst          deact;
+       struct FsmTimer         datimer;
+       struct sk_buff_head     sendq;
+       struct mISDNchannel     *up;
+       u_int                   nextid;
+       u_int                   lastid;
+};
+
+struct teimgr {
+       int                     ri;
+       int                     rcnt;
+       struct FsmInst          tei_m;
+       struct FsmTimer         timer;
+       int                     tval, nval;
+       struct layer2           *l2;
+       struct manager          *mgr;
+};
+
+struct laddr {
+       u_char  A;
+       u_char  B;
+};
+
+struct layer2 {
+       struct list_head        list;
+       struct mISDNchannel     ch;
+       u_long                  flag;
+       int                     id;
+       struct mISDNchannel     *up;
+       signed char             sapi;
+       signed char             tei;
+       struct laddr            addr;
+       u_int                   maxlen;
+       struct teimgr           *tm;
+       u_int                   vs, va, vr;
+       int                     rc;
+       u_int                   window;
+       u_int                   sow;
+       struct FsmInst          l2m;
+       struct FsmTimer         t200, t203;
+       int                     T200, N200, T203;
+       u_int                   next_id;
+       u_int                   down_id;
+       struct sk_buff          *windowar[MAX_WINDOW];
+       struct sk_buff_head     i_queue;
+       struct sk_buff_head     ui_queue;
+       struct sk_buff_head     down_queue;
+       struct sk_buff_head     tmp_queue;
+};
+
+enum {
+       ST_L2_1,
+       ST_L2_2,
+       ST_L2_3,
+       ST_L2_4,
+       ST_L2_5,
+       ST_L2_6,
+       ST_L2_7,
+       ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+extern struct layer2   *create_l2(struct mISDNchannel *, u_int,
+                               u_long, u_long);
+extern int             tei_l2(struct layer2 *, u_int, u_long arg);
+
+
+/* from tei.c */
+extern int             l2_tei(struct layer2 *, u_int, u_long arg);
+extern void            TEIrelease(struct layer2 *);
+extern int             TEIInit(u_int *);
+extern void            TEIFree(void);
+
+#define MAX_L2HEADER_LEN 4
+
+#define RR     0x01
+#define RNR    0x05
+#define REJ    0x09
+#define SABME  0x6f
+#define SABM   0x2f
+#define DM     0x0f
+#define UI     0x03
+#define DISC   0x43
+#define UA     0x63
+#define FRMR   0x87
+#define XID    0xaf
+
+#define CMD    0
+#define RSP    1
+
+#define LC_FLUSH_WAIT 1
+
+#define FLG_LAPB       0
+#define FLG_LAPD       1
+#define FLG_ORIG       2
+#define FLG_MOD128     3
+#define FLG_PEND_REL   4
+#define FLG_L3_INIT    5
+#define FLG_T200_RUN   6
+#define FLG_ACK_PEND   7
+#define FLG_REJEXC     8
+#define FLG_OWN_BUSY   9
+#define FLG_PEER_BUSY  10
+#define FLG_DCHAN_BUSY 11
+#define FLG_L1_ACTIV   12
+#define FLG_ESTAB_PEND 13
+#define FLG_PTP                14
+#define FLG_FIXED_TEI  15
+#define FLG_L2BLOCK    16
+#define FLG_L1_NOTREADY        17
+#define FLG_LAPD_NET   18
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
new file mode 100644 (file)
index 0000000..4ba4cc3
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static int     *debug;
+
+static struct proto mISDN_proto = {
+       .name           = "misdn",
+       .owner          = THIS_MODULE,
+       .obj_size       = sizeof(struct mISDN_sock)
+};
+
+#define _pms(sk)       ((struct mISDN_sock *)sk)
+
+static struct mISDN_sock_list  data_sockets = {
+       .lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
+};
+
+static struct mISDN_sock_list  base_sockets = {
+       .lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
+};
+
+#define L2_HEADER_LEN  4
+
+static inline struct sk_buff *
+_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+       struct sk_buff  *skb;
+
+       skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
+       if (likely(skb))
+               skb_reserve(skb, L2_HEADER_LEN);
+       return skb;
+}
+
+static void
+mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
+{
+       write_lock_bh(&l->lock);
+       sk_add_node(sk, &l->head);
+       write_unlock_bh(&l->lock);
+}
+
+static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
+{
+       write_lock_bh(&l->lock);
+       sk_del_node_init(sk);
+       write_unlock_bh(&l->lock);
+}
+
+static int
+mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDN_sock *msk;
+       int     err;
+
+       msk = container_of(ch, struct mISDN_sock, ch);
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
+       if (msk->sk.sk_state == MISDN_CLOSED)
+               return -EUNATCH;
+       __net_timestamp(skb);
+       err = sock_queue_rcv_skb(&msk->sk, skb);
+       if (err)
+               printk(KERN_WARNING "%s: error %d\n", __func__, err);
+       return err;
+}
+
+static int
+mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDN_sock *msk;
+
+       msk = container_of(ch, struct mISDN_sock, ch);
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               msk->sk.sk_state = MISDN_CLOSED;
+               break;
+       }
+       return 0;
+}
+
+static inline void
+mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+{
+       struct timeval  tv;
+
+       if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
+               skb_get_timestamp(skb, &tv);
+               put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
+       }
+}
+
+static int
+mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+    struct msghdr *msg, size_t len, int flags)
+{
+       struct sk_buff          *skb;
+       struct sock             *sk = sock->sk;
+       struct sockaddr_mISDN   *maddr;
+
+       int             copied, err;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
+                       __func__, (int)len, flags, _pms(sk)->ch.nr,
+                       sk->sk_protocol);
+       if (flags & (MSG_OOB))
+               return -EOPNOTSUPP;
+
+       if (sk->sk_state == MISDN_CLOSED)
+               return 0;
+
+       skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
+       if (!skb)
+               return err;
+
+       if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+               msg->msg_namelen = sizeof(struct sockaddr_mISDN);
+               maddr = (struct sockaddr_mISDN *)msg->msg_name;
+               maddr->family = AF_ISDN;
+               maddr->dev = _pms(sk)->dev->id;
+               if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+                   (sk->sk_protocol == ISDN_P_LAPD_NT)) {
+                       maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
+                       maddr->tei =  (mISDN_HEAD_ID(skb) >> 8) & 0xff;
+                       maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
+               } else {
+                       maddr->channel = _pms(sk)->ch.nr;
+                       maddr->sapi = _pms(sk)->ch.addr & 0xFF;
+                       maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
+               }
+       } else {
+               if (msg->msg_namelen)
+                       printk(KERN_WARNING "%s: too small namelen %d\n",
+                           __func__, msg->msg_namelen);
+               msg->msg_namelen = 0;
+       }
+
+       copied = skb->len + MISDN_HEADER_LEN;
+       if (len < copied) {
+               if (flags & MSG_PEEK)
+                       atomic_dec(&skb->users);
+               else
+                       skb_queue_head(&sk->sk_receive_queue, skb);
+               return -ENOSPC;
+       }
+       memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
+           MISDN_HEADER_LEN);
+
+       err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+       mISDN_sock_cmsg(sk, msg, skb);
+
+       skb_free_datagram(sk, skb);
+
+       return err ? : copied;
+}
+
+static int
+mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+    struct msghdr *msg, size_t len)
+{
+       struct sock             *sk = sock->sk;
+       struct sk_buff          *skb;
+       int                     err = -ENOMEM;
+       struct sockaddr_mISDN   *maddr;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
+                    __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
+                    sk->sk_protocol);
+
+       if (msg->msg_flags & MSG_OOB)
+               return -EOPNOTSUPP;
+
+       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+               return -EINVAL;
+
+       if (len < MISDN_HEADER_LEN)
+               return -EINVAL;
+
+       if (sk->sk_state != MISDN_BOUND)
+               return -EBADFD;
+
+       lock_sock(sk);
+
+       skb = _l2_alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               goto done;
+
+       if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+               err = -EFAULT;
+               goto drop;
+       }
+
+       memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
+       skb_pull(skb, MISDN_HEADER_LEN);
+
+       if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+               /* if we have a address, we use it */
+               maddr = (struct sockaddr_mISDN *)msg->msg_name;
+               mISDN_HEAD_ID(skb) = maddr->channel;
+       } else { /* use default for L2 messages */
+               if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+                   (sk->sk_protocol == ISDN_P_LAPD_NT))
+                   mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
+       }
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s: ID:%x\n",
+                    __func__, mISDN_HEAD_ID(skb));
+
+       err = -ENODEV;
+       if (!_pms(sk)->ch.peer ||
+           (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb)))
+               goto drop;
+
+       err = len;
+
+done:
+       release_sock(sk);
+       return err;
+
+drop:
+       kfree_skb(skb);
+       goto done;
+}
+
+static int
+data_sock_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+       if (!sk)
+               return 0;
+       switch (sk->sk_protocol) {
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_E1:
+       case ISDN_P_NT_E1:
+               if (sk->sk_state == MISDN_BOUND)
+                       delete_channel(&_pms(sk)->ch);
+               else
+                       mISDN_sock_unlink(&data_sockets, sk);
+               break;
+       case ISDN_P_LAPD_TE:
+       case ISDN_P_LAPD_NT:
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_X75SLP:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_L2DSP:
+       case ISDN_P_B_L2DSPHDLC:
+               delete_channel(&_pms(sk)->ch);
+               mISDN_sock_unlink(&data_sockets, sk);
+               break;
+       }
+
+       lock_sock(sk);
+
+       sock_orphan(sk);
+       skb_queue_purge(&sk->sk_receive_queue);
+
+       release_sock(sk);
+       sock_put(sk);
+
+       return 0;
+}
+
+static int
+data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
+{
+       struct mISDN_ctrl_req   cq;
+       int                     err = -EINVAL, val;
+       struct mISDNchannel     *bchan, *next;
+
+       lock_sock(sk);
+       if (!_pms(sk)->dev) {
+               err = -ENODEV;
+               goto done;
+       }
+       switch (cmd) {
+       case IMCTRLREQ:
+               if (copy_from_user(&cq, p, sizeof(cq))) {
+                       err = -EFAULT;
+                       break;
+               }
+               if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
+                       list_for_each_entry_safe(bchan, next,
+                               &_pms(sk)->dev->bchannels, list) {
+                               if (bchan->nr == cq.channel) {
+                                       err = bchan->ctrl(bchan,
+                                               CONTROL_CHANNEL, &cq);
+                                       break;
+                               }
+                       }
+               } else
+                       err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
+                               CONTROL_CHANNEL, &cq);
+               if (err)
+                       break;
+               if (copy_to_user(p, &cq, sizeof(cq)))
+                       err = -EFAULT;
+               break;
+       case IMCLEAR_L2:
+               if (sk->sk_protocol != ISDN_P_LAPD_NT) {
+                       err = -EINVAL;
+                       break;
+               }
+               if (get_user(val, (int __user *)p)) {
+                       err = -EFAULT;
+                       break;
+               }
+               err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
+                   CONTROL_CHANNEL, &val);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+done:
+       release_sock(sk);
+       return err;
+}
+
+static int
+data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       int                     err = 0, id;
+       struct sock             *sk = sock->sk;
+       struct mISDNdevice      *dev;
+       struct mISDNversion     ver;
+
+       switch (cmd) {
+       case IMGETVERSION:
+               ver.major = MISDN_MAJOR_VERSION;
+               ver.minor = MISDN_MINOR_VERSION;
+               ver.release = MISDN_RELEASE;
+               if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+                       err = -EFAULT;
+               break;
+       case IMGETCOUNT:
+               id = get_mdevice_count();
+               if (put_user(id, (int __user *)arg))
+                       err = -EFAULT;
+               break;
+       case IMGETDEVINFO:
+               if (get_user(id, (int __user *)arg)) {
+                       err = -EFAULT;
+                       break;
+               }
+               dev = get_mdevice(id);
+               if (dev) {
+                       struct mISDN_devinfo di;
+
+                       di.id = dev->id;
+                       di.Dprotocols = dev->Dprotocols;
+                       di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+                       di.protocol = dev->D.protocol;
+                       memcpy(di.channelmap, dev->channelmap,
+                               MISDN_CHMAP_SIZE * 4);
+                       di.nrbchan = dev->nrbchan;
+                       strcpy(di.name, dev->name);
+                       if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+                               err = -EFAULT;
+               } else
+                       err = -ENODEV;
+               break;
+       default:
+               if (sk->sk_state == MISDN_BOUND)
+                       err = data_sock_ioctl_bound(sk, cmd,
+                               (void __user *)arg);
+               else
+                       err = -ENOTCONN;
+       }
+       return err;
+}
+
+static int data_sock_setsockopt(struct socket *sock, int level, int optname,
+       char __user *optval, int len)
+{
+       struct sock *sk = sock->sk;
+       int err = 0, opt = 0;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock,
+                   level, optname, optval, len);
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case MISDN_TIME_STAMP:
+               if (get_user(opt, (int __user *)optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt)
+                       _pms(sk)->cmask |= MISDN_TIME_STAMP;
+               else
+                       _pms(sk)->cmask &= ~MISDN_TIME_STAMP;
+               break;
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+       release_sock(sk);
+       return err;
+}
+
+static int data_sock_getsockopt(struct socket *sock, int level, int optname,
+       char __user *optval, int __user *optlen)
+{
+       struct sock *sk = sock->sk;
+       int len, opt;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       switch (optname) {
+       case MISDN_TIME_STAMP:
+               if (_pms(sk)->cmask & MISDN_TIME_STAMP)
+                       opt = 1;
+               else
+                       opt = 0;
+
+               if (put_user(opt, optval))
+                       return -EFAULT;
+               break;
+       default:
+               return -ENOPROTOOPT;
+       }
+
+       return 0;
+}
+
+static int
+data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+       struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+       struct sock *sk = sock->sk;
+       int err = 0;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+       if (addr_len != sizeof(struct sockaddr_mISDN))
+               return -EINVAL;
+       if (!maddr || maddr->family != AF_ISDN)
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       if (_pms(sk)->dev) {
+               err = -EALREADY;
+               goto done;
+       }
+       _pms(sk)->dev = get_mdevice(maddr->dev);
+       if (!_pms(sk)->dev) {
+               err = -ENODEV;
+               goto done;
+       }
+       _pms(sk)->ch.send = mISDN_send;
+       _pms(sk)->ch.ctrl = mISDN_ctrl;
+
+       switch (sk->sk_protocol) {
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_E1:
+       case ISDN_P_NT_E1:
+               mISDN_sock_unlink(&data_sockets, sk);
+               err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
+                   sk->sk_protocol, maddr);
+               if (err)
+                       mISDN_sock_link(&data_sockets, sk);
+               break;
+       case ISDN_P_LAPD_TE:
+       case ISDN_P_LAPD_NT:
+               err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
+                   sk->sk_protocol, maddr);
+               break;
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_X75SLP:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_L2DSP:
+       case ISDN_P_B_L2DSPHDLC:
+               err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
+                   sk->sk_protocol, maddr);
+               break;
+       default:
+               err = -EPROTONOSUPPORT;
+       }
+       if (err)
+               goto done;
+       sk->sk_state = MISDN_BOUND;
+       _pms(sk)->ch.protocol = sk->sk_protocol;
+
+done:
+       release_sock(sk);
+       return err;
+}
+
+static int
+data_sock_getname(struct socket *sock, struct sockaddr *addr,
+    int *addr_len, int peer)
+{
+       struct sockaddr_mISDN   *maddr = (struct sockaddr_mISDN *) addr;
+       struct sock             *sk = sock->sk;
+
+       if (!_pms(sk)->dev)
+               return -EBADFD;
+
+       lock_sock(sk);
+
+       *addr_len = sizeof(*maddr);
+       maddr->dev = _pms(sk)->dev->id;
+       maddr->channel = _pms(sk)->ch.nr;
+       maddr->sapi = _pms(sk)->ch.addr & 0xff;
+       maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
+       release_sock(sk);
+       return 0;
+}
+
+static const struct proto_ops data_sock_ops = {
+       .family         = PF_ISDN,
+       .owner          = THIS_MODULE,
+       .release        = data_sock_release,
+       .ioctl          = data_sock_ioctl,
+       .bind           = data_sock_bind,
+       .getname        = data_sock_getname,
+       .sendmsg        = mISDN_sock_sendmsg,
+       .recvmsg        = mISDN_sock_recvmsg,
+       .poll           = datagram_poll,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = data_sock_setsockopt,
+       .getsockopt     = data_sock_getsockopt,
+       .connect        = sock_no_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .mmap           = sock_no_mmap
+};
+
+static int
+data_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+       struct sock *sk;
+
+       if (sock->type != SOCK_DGRAM)
+               return -ESOCKTNOSUPPORT;
+
+       sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+       if (!sk)
+               return -ENOMEM;
+
+       sock_init_data(sock, sk);
+
+       sock->ops = &data_sock_ops;
+       sock->state = SS_UNCONNECTED;
+       sock_reset_flag(sk, SOCK_ZAPPED);
+
+       sk->sk_protocol = protocol;
+       sk->sk_state    = MISDN_OPEN;
+       mISDN_sock_link(&data_sockets, sk);
+
+       return 0;
+}
+
+static int
+base_sock_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+
+       printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+       if (!sk)
+               return 0;
+
+       mISDN_sock_unlink(&base_sockets, sk);
+       sock_orphan(sk);
+       sock_put(sk);
+
+       return 0;
+}
+
+static int
+base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       int                     err = 0, id;
+       struct mISDNdevice      *dev;
+       struct mISDNversion     ver;
+
+       switch (cmd) {
+       case IMGETVERSION:
+               ver.major = MISDN_MAJOR_VERSION;
+               ver.minor = MISDN_MINOR_VERSION;
+               ver.release = MISDN_RELEASE;
+               if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+                       err = -EFAULT;
+               break;
+       case IMGETCOUNT:
+               id = get_mdevice_count();
+               if (put_user(id, (int __user *)arg))
+                       err = -EFAULT;
+               break;
+       case IMGETDEVINFO:
+               if (get_user(id, (int __user *)arg)) {
+                       err = -EFAULT;
+                       break;
+               }
+               dev = get_mdevice(id);
+               if (dev) {
+                       struct mISDN_devinfo di;
+
+                       di.id = dev->id;
+                       di.Dprotocols = dev->Dprotocols;
+                       di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+                       di.protocol = dev->D.protocol;
+                       memcpy(di.channelmap, dev->channelmap,
+                               MISDN_CHMAP_SIZE * 4);
+                       di.nrbchan = dev->nrbchan;
+                       strcpy(di.name, dev->name);
+                       if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+                               err = -EFAULT;
+               } else
+                       err = -ENODEV;
+               break;
+       default:
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static int
+base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+       struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+       struct sock *sk = sock->sk;
+       int err = 0;
+
+       if (!maddr || maddr->family != AF_ISDN)
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       if (_pms(sk)->dev) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       _pms(sk)->dev = get_mdevice(maddr->dev);
+       if (!_pms(sk)->dev) {
+               err = -ENODEV;
+               goto done;
+       }
+       sk->sk_state = MISDN_BOUND;
+
+done:
+       release_sock(sk);
+       return err;
+}
+
+static const struct proto_ops base_sock_ops = {
+       .family         = PF_ISDN,
+       .owner          = THIS_MODULE,
+       .release        = base_sock_release,
+       .ioctl          = base_sock_ioctl,
+       .bind           = base_sock_bind,
+       .getname        = sock_no_getname,
+       .sendmsg        = sock_no_sendmsg,
+       .recvmsg        = sock_no_recvmsg,
+       .poll           = sock_no_poll,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .connect        = sock_no_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .mmap           = sock_no_mmap
+};
+
+
+static int
+base_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+       struct sock *sk;
+
+       if (sock->type != SOCK_RAW)
+               return -ESOCKTNOSUPPORT;
+
+       sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+       if (!sk)
+               return -ENOMEM;
+
+       sock_init_data(sock, sk);
+       sock->ops = &base_sock_ops;
+       sock->state = SS_UNCONNECTED;
+       sock_reset_flag(sk, SOCK_ZAPPED);
+       sk->sk_protocol = protocol;
+       sk->sk_state    = MISDN_OPEN;
+       mISDN_sock_link(&base_sockets, sk);
+
+       return 0;
+}
+
+static int
+mISDN_sock_create(struct net *net, struct socket *sock, int proto)
+{
+       int err = -EPROTONOSUPPORT;
+
+       switch  (proto) {
+       case ISDN_P_BASE:
+               err = base_sock_create(net, sock, proto);
+               break;
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_E1:
+       case ISDN_P_NT_E1:
+       case ISDN_P_LAPD_TE:
+       case ISDN_P_LAPD_NT:
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_X75SLP:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_L2DSP:
+       case ISDN_P_B_L2DSPHDLC:
+               err = data_sock_create(net, sock, proto);
+               break;
+       default:
+               return err;
+       }
+
+       return err;
+}
+
+static struct
+net_proto_family mISDN_sock_family_ops = {
+       .owner  = THIS_MODULE,
+       .family = PF_ISDN,
+       .create = mISDN_sock_create,
+};
+
+int
+misdn_sock_init(u_int *deb)
+{
+       int err;
+
+       debug = deb;
+       err = sock_register(&mISDN_sock_family_ops);
+       if (err)
+               printk(KERN_ERR "%s: error(%d)\n", __func__, err);
+       return err;
+}
+
+void
+misdn_sock_cleanup(void)
+{
+       sock_unregister(PF_ISDN);
+}
+
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
new file mode 100644 (file)
index 0000000..54cfddc
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/kthread.h>
+#include "core.h"
+
+static u_int   *debug;
+
+static inline void
+_queue_message(struct mISDNstack *st, struct sk_buff *skb)
+{
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+
+       if (*debug & DEBUG_QUEUE_FUNC)
+               printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+                   __func__, hh->prim, hh->id, skb);
+       skb_queue_tail(&st->msgq, skb);
+       if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
+               test_and_set_bit(mISDN_STACK_WORK, &st->status);
+               wake_up_interruptible(&st->workq);
+       }
+}
+
+int
+mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       _queue_message(ch->st, skb);
+       return 0;
+}
+
+static struct mISDNchannel *
+get_channel4id(struct mISDNstack *st, u_int id)
+{
+       struct mISDNchannel     *ch;
+
+       mutex_lock(&st->lmutex);
+       list_for_each_entry(ch, &st->layer2, list) {
+               if (id == ch->nr)
+                       goto unlock;
+       }
+       ch = NULL;
+unlock:
+       mutex_unlock(&st->lmutex);
+       return ch;
+}
+
+static void
+send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
+{
+       struct hlist_node       *node;
+       struct sock             *sk;
+       struct sk_buff          *cskb = NULL;
+
+       read_lock(&sl->lock);
+       sk_for_each(sk, node, &sl->head) {
+               if (sk->sk_state != MISDN_BOUND)
+                       continue;
+               if (!cskb)
+                       cskb = skb_copy(skb, GFP_KERNEL);
+               if (!cskb) {
+                       printk(KERN_WARNING "%s no skb\n", __func__);
+                       break;
+               }
+               if (!sock_queue_rcv_skb(sk, cskb))
+                       cskb = NULL;
+       }
+       read_unlock(&sl->lock);
+       if (cskb)
+               dev_kfree_skb(cskb);
+}
+
+static void
+send_layer2(struct mISDNstack *st, struct sk_buff *skb)
+{
+       struct sk_buff          *cskb;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       struct mISDNchannel     *ch;
+       int                     ret;
+
+       if (!st)
+               return;
+       mutex_lock(&st->lmutex);
+       if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
+               list_for_each_entry(ch, &st->layer2, list) {
+                       if (list_is_last(&ch->list, &st->layer2)) {
+                               cskb = skb;
+                               skb = NULL;
+                       } else {
+                               cskb = skb_copy(skb, GFP_KERNEL);
+                       }
+                       if (cskb) {
+                               ret = ch->send(ch, cskb);
+                               if (ret) {
+                                       if (*debug & DEBUG_SEND_ERR)
+                                               printk(KERN_DEBUG
+                                                   "%s ch%d prim(%x) addr(%x)"
+                                                   " err %d\n",
+                                                   __func__, ch->nr,
+                                                   hh->prim, ch->addr, ret);
+                                       dev_kfree_skb(cskb);
+                               }
+                       } else {
+                               printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+                                   __func__, ch->nr, ch->addr);
+                               goto out;
+                       }
+               }
+       } else {
+               list_for_each_entry(ch, &st->layer2, list) {
+                       if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
+                               ret = ch->send(ch, skb);
+                               if (!ret)
+                                       skb = NULL;
+                               goto out;
+                       }
+               }
+               ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
+               if (!ret)
+                       skb = NULL;
+               else if (*debug & DEBUG_SEND_ERR)
+                       printk(KERN_DEBUG
+                           "%s ch%d mgr prim(%x) addr(%x) err %d\n",
+                           __func__, ch->nr, hh->prim, ch->addr, ret);
+       }
+out:
+       mutex_unlock(&st->lmutex);
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static inline int
+send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
+{
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       struct mISDNchannel     *ch;
+       int     lm;
+
+       lm = hh->prim & MISDN_LAYERMASK;
+       if (*debug & DEBUG_QUEUE_FUNC)
+               printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+                   __func__, hh->prim, hh->id, skb);
+       if (lm == 0x1) {
+               if (!hlist_empty(&st->l1sock.head)) {
+                       __net_timestamp(skb);
+                       send_socklist(&st->l1sock, skb);
+               }
+               return st->layer1->send(st->layer1, skb);
+       } else if (lm == 0x2) {
+               if (!hlist_empty(&st->l1sock.head))
+                       send_socklist(&st->l1sock, skb);
+               send_layer2(st, skb);
+               return 0;
+       } else if (lm == 0x4) {
+               ch = get_channel4id(st, hh->id);
+               if (ch)
+                       return ch->send(ch, skb);
+               else
+                       printk(KERN_WARNING
+                           "%s: dev(%s) prim(%x) id(%x) no channel\n",
+                           __func__, st->dev->name, hh->prim, hh->id);
+       } else if (lm == 0x8) {
+               WARN_ON(lm == 0x8);
+               ch = get_channel4id(st, hh->id);
+               if (ch)
+                       return ch->send(ch, skb);
+               else
+                       printk(KERN_WARNING
+                           "%s: dev(%s) prim(%x) id(%x) no channel\n",
+                           __func__, st->dev->name, hh->prim, hh->id);
+       } else {
+               /* broadcast not handled yet */
+               printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
+                   __func__, st->dev->name, hh->prim);
+       }
+       return -ESRCH;
+}
+
+static void
+do_clear_stack(struct mISDNstack *st)
+{
+}
+
+static int
+mISDNStackd(void *data)
+{
+       struct mISDNstack *st = data;
+       int err = 0;
+
+#ifdef CONFIG_SMP
+       lock_kernel();
+#endif
+       sigfillset(&current->blocked);
+#ifdef CONFIG_SMP
+       unlock_kernel();
+#endif
+       if (*debug & DEBUG_MSG_THREAD)
+               printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name);
+
+       if (st->notify != NULL) {
+               complete(st->notify);
+               st->notify = NULL;
+       }
+
+       for (;;) {
+               struct sk_buff  *skb;
+
+               if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
+                       test_and_clear_bit(mISDN_STACK_WORK, &st->status);
+                       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+               } else
+                       test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+               while (test_bit(mISDN_STACK_WORK, &st->status)) {
+                       skb = skb_dequeue(&st->msgq);
+                       if (!skb) {
+                               test_and_clear_bit(mISDN_STACK_WORK,
+                                       &st->status);
+                               /* test if a race happens */
+                               skb = skb_dequeue(&st->msgq);
+                               if (!skb)
+                                       continue;
+                               test_and_set_bit(mISDN_STACK_WORK,
+                                   &st->status);
+                       }
+#ifdef MISDN_MSG_STATS
+                       st->msg_cnt++;
+#endif
+                       err = send_msg_to_layer(st, skb);
+                       if (unlikely(err)) {
+                               if (*debug & DEBUG_SEND_ERR)
+                                       printk(KERN_DEBUG
+                                           "%s: %s prim(%x) id(%x) "
+                                           "send call(%d)\n",
+                                           __func__, st->dev->name,
+                                           mISDN_HEAD_PRIM(skb),
+                                           mISDN_HEAD_ID(skb), err);
+                               dev_kfree_skb(skb);
+                               continue;
+                       }
+                       if (unlikely(test_bit(mISDN_STACK_STOPPED,
+                           &st->status))) {
+                               test_and_clear_bit(mISDN_STACK_WORK,
+                                   &st->status);
+                               test_and_clear_bit(mISDN_STACK_RUNNING,
+                                   &st->status);
+                               break;
+                       }
+               }
+               if (test_bit(mISDN_STACK_CLEARING, &st->status)) {
+                       test_and_set_bit(mISDN_STACK_STOPPED, &st->status);
+                       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+                       do_clear_stack(st);
+                       test_and_clear_bit(mISDN_STACK_CLEARING, &st->status);
+                       test_and_set_bit(mISDN_STACK_RESTART, &st->status);
+               }
+               if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) {
+                       test_and_clear_bit(mISDN_STACK_STOPPED, &st->status);
+                       test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+                       if (!skb_queue_empty(&st->msgq))
+                               test_and_set_bit(mISDN_STACK_WORK,
+                                   &st->status);
+               }
+               if (test_bit(mISDN_STACK_ABORT, &st->status))
+                       break;
+               if (st->notify != NULL) {
+                       complete(st->notify);
+                       st->notify = NULL;
+               }
+#ifdef MISDN_MSG_STATS
+               st->sleep_cnt++;
+#endif
+               test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+               wait_event_interruptible(st->workq, (st->status &
+                   mISDN_STACK_ACTION_MASK));
+               if (*debug & DEBUG_MSG_THREAD)
+                       printk(KERN_DEBUG "%s: %s wake status %08lx\n",
+                           __func__, st->dev->name, st->status);
+               test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
+
+               test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
+
+               if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
+                       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+#ifdef MISDN_MSG_STATS
+                       st->stopped_cnt++;
+#endif
+               }
+       }
+#ifdef MISDN_MSG_STATS
+       printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
+           "msg %d sleep %d stopped\n",
+           st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt);
+       printk(KERN_DEBUG
+           "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
+           st->dev->name, st->thread->utime, st->thread->stime);
+       printk(KERN_DEBUG
+           "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
+           st->dev->name, st->thread->nvcsw, st->thread->nivcsw);
+       printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
+           st->dev->name);
+#endif
+       test_and_set_bit(mISDN_STACK_KILLED, &st->status);
+       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+       test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+       test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
+       skb_queue_purge(&st->msgq);
+       st->thread = NULL;
+       if (st->notify != NULL) {
+               complete(st->notify);
+               st->notify = NULL;
+       }
+       return 0;
+}
+
+static int
+l1_receive(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       if (!ch->st)
+               return -ENODEV;
+       __net_timestamp(skb);
+       _queue_message(ch->st, skb);
+       return 0;
+}
+
+void
+set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei)
+{
+       ch->addr = sapi | (tei << 8);
+}
+
+void
+__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+       list_add_tail(&ch->list, &st->layer2);
+}
+
+void
+add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+       mutex_lock(&st->lmutex);
+       __add_layer2(ch, st);
+       mutex_unlock(&st->lmutex);
+}
+
+static int
+st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       if (!ch->st || ch->st->layer1)
+               return -EINVAL;
+       return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg);
+}
+
+int
+create_stack(struct mISDNdevice *dev)
+{
+       struct mISDNstack       *newst;
+       int                     err;
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL);
+       if (!newst) {
+               printk(KERN_ERR "kmalloc mISDN_stack failed\n");
+               return -ENOMEM;
+       }
+       newst->dev = dev;
+       INIT_LIST_HEAD(&newst->layer2);
+       INIT_HLIST_HEAD(&newst->l1sock.head);
+       rwlock_init(&newst->l1sock.lock);
+       init_waitqueue_head(&newst->workq);
+       skb_queue_head_init(&newst->msgq);
+       mutex_init(&newst->lmutex);
+       dev->D.st = newst;
+       err = create_teimanager(dev);
+       if (err) {
+               printk(KERN_ERR "kmalloc teimanager failed\n");
+               kfree(newst);
+               return err;
+       }
+       dev->teimgr->peer = &newst->own;
+       dev->teimgr->recv = mISDN_queue_message;
+       dev->teimgr->st = newst;
+       newst->layer1 = &dev->D;
+       dev->D.recv = l1_receive;
+       dev->D.peer = &newst->own;
+       newst->own.st = newst;
+       newst->own.ctrl = st_own_ctrl;
+       newst->own.send = mISDN_queue_message;
+       newst->own.recv = mISDN_queue_message;
+       if (*debug & DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name);
+       newst->notify = &done;
+       newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
+               newst->dev->name);
+       if (IS_ERR(newst->thread)) {
+               err = PTR_ERR(newst->thread);
+               printk(KERN_ERR
+                       "mISDN:cannot create kernel thread for %s (%d)\n",
+                       newst->dev->name, err);
+               delete_teimanager(dev->teimgr);
+               kfree(newst);
+       } else
+               wait_for_completion(&done);
+       return err;
+}
+
+int
+connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
+               u_int protocol, struct sockaddr_mISDN *adr)
+{
+       struct mISDN_sock       *msk = container_of(ch, struct mISDN_sock, ch);
+       struct channel_req      rq;
+       int                     err;
+
+
+       if (*debug &  DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, dev->name, protocol, adr->dev, adr->channel,
+                        adr->sapi, adr->tei);
+       switch (protocol) {
+       case ISDN_P_NT_S0:
+       case ISDN_P_NT_E1:
+       case ISDN_P_TE_S0:
+       case ISDN_P_TE_E1:
+#ifdef PROTOCOL_CHECK
+               /* this should be enhanced */
+               if (!list_empty(&dev->D.st->layer2)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+               if (!hlist_empty(&dev->D.st->l1sock.head)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+#endif
+               ch->recv = mISDN_queue_message;
+               ch->peer = &dev->D.st->own;
+               ch->st = dev->D.st;
+               rq.protocol = protocol;
+               rq.adr.channel = 0;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+               if (err)
+                       return err;
+               write_lock_bh(&dev->D.st->l1sock.lock);
+               sk_add_node(&msk->sk, &dev->D.st->l1sock.head);
+               write_unlock_bh(&dev->D.st->l1sock.lock);
+               break;
+       default:
+               return -ENOPROTOOPT;
+       }
+       return 0;
+}
+
+int
+connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
+    u_int protocol, struct sockaddr_mISDN *adr)
+{
+       struct channel_req      rq, rq2;
+       int                     pmask, err;
+       struct Bprotocol        *bp;
+
+       if (*debug &  DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, dev->name, protocol,
+                       adr->dev, adr->channel, adr->sapi,
+                       adr->tei);
+       ch->st = dev->D.st;
+       pmask = 1 << (protocol & ISDN_P_B_MASK);
+       if (pmask & dev->Bprotocols) {
+               rq.protocol = protocol;
+               rq.adr = *adr;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               if (err)
+                       return err;
+               ch->recv = rq.ch->send;
+               ch->peer = rq.ch;
+               rq.ch->recv = ch->send;
+               rq.ch->peer = ch;
+               rq.ch->st = dev->D.st;
+       } else {
+               bp = get_Bprotocol4mask(pmask);
+               if (!bp)
+                       return -ENOPROTOOPT;
+               rq2.protocol = protocol;
+               rq2.adr = *adr;
+               rq2.ch = ch;
+               err = bp->create(&rq2);
+               if (err)
+                       return err;
+               ch->recv = rq2.ch->send;
+               ch->peer = rq2.ch;
+               rq2.ch->st = dev->D.st;
+               rq.protocol = rq2.protocol;
+               rq.adr = *adr;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               if (err) {
+                       rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
+                       return err;
+               }
+               rq2.ch->recv = rq.ch->send;
+               rq2.ch->peer = rq.ch;
+               rq.ch->recv = rq2.ch->send;
+               rq.ch->peer = rq2.ch;
+               rq.ch->st = dev->D.st;
+       }
+       ch->protocol = protocol;
+       ch->nr = rq.ch->nr;
+       return 0;
+}
+
+int
+create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
+    u_int protocol, struct sockaddr_mISDN *adr)
+{
+       struct channel_req      rq;
+       int                     err;
+
+       if (*debug &  DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, dev->name, protocol,
+                       adr->dev, adr->channel, adr->sapi,
+                       adr->tei);
+       rq.protocol = ISDN_P_TE_S0;
+       if (dev->Dprotocols & (1 << ISDN_P_TE_E1))
+               rq.protocol = ISDN_P_TE_E1;
+       switch (protocol) {
+       case ISDN_P_LAPD_NT:
+               rq.protocol = ISDN_P_NT_S0;
+               if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
+                       rq.protocol = ISDN_P_NT_E1;
+       case ISDN_P_LAPD_TE:
+#ifdef PROTOCOL_CHECK
+               /* this should be enhanced */
+               if (!list_empty(&dev->D.st->layer2)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+               if (!hlist_empty(&dev->D.st->l1sock.head)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+#endif
+               ch->recv = mISDN_queue_message;
+               ch->peer = &dev->D.st->own;
+               ch->st = dev->D.st;
+               rq.adr.channel = 0;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+               if (err)
+                       break;
+               rq.protocol = protocol;
+               rq.adr = *adr;
+               rq.ch = ch;
+               err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
+               printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
+               if (!err) {
+                       if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
+                               break;
+                       add_layer2(rq.ch, dev->D.st);
+                       rq.ch->recv = mISDN_queue_message;
+                       rq.ch->peer = &dev->D.st->own;
+                       rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
+               }
+               break;
+       default:
+               err = -EPROTONOSUPPORT;
+       }
+       return err;
+}
+
+void
+delete_channel(struct mISDNchannel *ch)
+{
+       struct mISDN_sock       *msk = container_of(ch, struct mISDN_sock, ch);
+       struct mISDNchannel     *pch;
+
+       if (!ch->st) {
+               printk(KERN_WARNING "%s: no stack\n", __func__);
+               return;
+       }
+       if (*debug & DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
+                   ch->st->dev->name, ch->protocol);
+       if (ch->protocol >= ISDN_P_B_START) {
+               if (ch->peer) {
+                       ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
+                       ch->peer = NULL;
+               }
+               return;
+       }
+       switch (ch->protocol) {
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_E1:
+       case ISDN_P_TE_E1:
+               write_lock_bh(&ch->st->l1sock.lock);
+               sk_del_node_init(&msk->sk);
+               write_unlock_bh(&ch->st->l1sock.lock);
+               ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
+               break;
+       case ISDN_P_LAPD_TE:
+               pch = get_channel4id(ch->st, ch->nr);
+               if (pch) {
+                       mutex_lock(&ch->st->lmutex);
+                       list_del(&pch->list);
+                       mutex_unlock(&ch->st->lmutex);
+                       pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+                       pch = ch->st->dev->teimgr;
+                       pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+               } else
+                       printk(KERN_WARNING "%s: no l2 channel\n",
+                           __func__);
+               break;
+       case ISDN_P_LAPD_NT:
+               pch = ch->st->dev->teimgr;
+               if (pch) {
+                       pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+               } else
+                       printk(KERN_WARNING "%s: no l2 channel\n",
+                           __func__);
+               break;
+       default:
+               break;
+       }
+       return;
+}
+
+void
+delete_stack(struct mISDNdevice *dev)
+{
+       struct mISDNstack       *st = dev->D.st;
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       if (*debug & DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: st(%s)\n", __func__,
+                   st->dev->name);
+       if (dev->teimgr)
+               delete_teimanager(dev->teimgr);
+       if (st->thread) {
+               if (st->notify) {
+                       printk(KERN_WARNING "%s: notifier in use\n",
+                           __func__);
+                               complete(st->notify);
+               }
+               st->notify = &done;
+               test_and_set_bit(mISDN_STACK_ABORT, &st->status);
+               test_and_set_bit(mISDN_STACK_WAKEUP, &st->status);
+               wake_up_interruptible(&st->workq);
+               wait_for_completion(&done);
+       }
+       if (!list_empty(&st->layer2))
+               printk(KERN_WARNING "%s: layer2 list not empty\n",
+                   __func__);
+       if (!hlist_empty(&st->l1sock.head))
+               printk(KERN_WARNING "%s: layer1 list not empty\n",
+                   __func__);
+       kfree(st);
+}
+
+void
+mISDN_initstack(u_int *dp)
+{
+       debug = dp;
+}
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
new file mode 100644 (file)
index 0000000..6fbae42
--- /dev/null
@@ -0,0 +1,1340 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "layer2.h"
+#include <linux/random.h>
+#include "core.h"
+
+#define ID_REQUEST     1
+#define ID_ASSIGNED    2
+#define ID_DENIED      3
+#define ID_CHK_REQ     4
+#define ID_CHK_RES     5
+#define ID_REMOVE      6
+#define ID_VERIFY      7
+
+#define TEI_ENTITY_ID  0xf
+
+#define MGR_PH_ACTIVE  16
+#define MGR_PH_NOTREADY        17
+
+#define DATIMER_VAL    10000
+
+static         u_int   *debug;
+
+static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL};
+
+enum {
+       ST_L1_DEACT,
+       ST_L1_DEACT_PENDING,
+       ST_L1_ACTIV,
+};
+#define DEACT_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strDeactState[] =
+{
+       "ST_L1_DEACT",
+       "ST_L1_DEACT_PENDING",
+       "ST_L1_ACTIV",
+};
+
+enum {
+       EV_ACTIVATE,
+       EV_ACTIVATE_IND,
+       EV_DEACTIVATE,
+       EV_DEACTIVATE_IND,
+       EV_UI,
+       EV_DATIMER,
+};
+
+#define DEACT_EVENT_COUNT (EV_DATIMER+1)
+
+static char *strDeactEvent[] =
+{
+       "EV_ACTIVATE",
+       "EV_ACTIVATE_IND",
+       "EV_DEACTIVATE",
+       "EV_DEACTIVATE_IND",
+       "EV_UI",
+       "EV_DATIMER",
+};
+
+static void
+da_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct manager  *mgr = fi->userdata;
+       va_list va;
+
+       if (!(*debug & DEBUG_L2_TEIFSM))
+               return;
+       va_start(va, fmt);
+       printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+static void
+da_activate(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+
+       if (fi->state == ST_L1_DEACT_PENDING)
+               mISDN_FsmDelTimer(&mgr->datimer, 1);
+       mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+}
+
+static void
+da_deactivate_ind(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+da_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+       struct layer2   *l2;
+       u_long          flags;
+
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->l2m.state > ST_L2_4) {
+                       /* have still activ TEI */
+                       read_unlock_irqrestore(&mgr->lock, flags);
+                       return;
+               }
+       }
+       read_unlock_irqrestore(&mgr->lock, flags);
+       /* All TEI are inactiv */
+       mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1);
+       mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING);
+}
+
+static void
+da_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+
+       /* restart da timer */
+       mISDN_FsmDelTimer(&mgr->datimer, 2);
+       mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2);
+
+}
+
+static void
+da_timer(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+       struct layer2   *l2;
+       u_long          flags;
+
+       /* check again */
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->l2m.state > ST_L2_4) {
+                       /* have still activ TEI */
+                       read_unlock_irqrestore(&mgr->lock, flags);
+                       mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+                       return;
+               }
+       }
+       read_unlock_irqrestore(&mgr->lock, flags);
+       /* All TEI are inactiv */
+       mISDN_FsmChangeState(fi, ST_L1_DEACT);
+       _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL,
+           GFP_ATOMIC);
+}
+
+static struct FsmNode DeactFnList[] =
+{
+       {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate},
+       {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind},
+       {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate},
+       {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate},
+       {ST_L1_DEACT_PENDING, EV_UI, da_ui},
+       {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer},
+};
+
+enum {
+       ST_TEI_NOP,
+       ST_TEI_IDREQ,
+       ST_TEI_IDVERIFY,
+};
+
+#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
+
+static char *strTeiState[] =
+{
+       "ST_TEI_NOP",
+       "ST_TEI_IDREQ",
+       "ST_TEI_IDVERIFY",
+};
+
+enum {
+       EV_IDREQ,
+       EV_ASSIGN,
+       EV_ASSIGN_REQ,
+       EV_DENIED,
+       EV_CHKREQ,
+       EV_CHKRESP,
+       EV_REMOVE,
+       EV_VERIFY,
+       EV_TIMER,
+};
+
+#define TEI_EVENT_COUNT (EV_TIMER+1)
+
+static char *strTeiEvent[] =
+{
+       "EV_IDREQ",
+       "EV_ASSIGN",
+       "EV_ASSIGN_REQ",
+       "EV_DENIED",
+       "EV_CHKREQ",
+       "EV_CHKRESP",
+       "EV_REMOVE",
+       "EV_VERIFY",
+       "EV_TIMER",
+};
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct teimgr   *tm = fi->userdata;
+       va_list va;
+
+       if (!(*debug & DEBUG_L2_TEIFSM))
+               return;
+       va_start(va, fmt);
+       printk(KERN_DEBUG "tei(%d): ", tm->l2->tei);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+
+
+static int
+get_free_id(struct manager *mgr)
+{
+       u64             ids = 0;
+       int             i;
+       struct layer2   *l2;
+
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->ch.nr > 63) {
+                       printk(KERN_WARNING
+                           "%s: more as 63 layer2 for one device\n",
+                           __func__);
+                       return -EBUSY;
+               }
+               test_and_set_bit(l2->ch.nr, (u_long *)&ids);
+       }
+       for (i = 1; i < 64; i++)
+               if (!test_bit(i, (u_long *)&ids))
+                       return i;
+       printk(KERN_WARNING "%s: more as 63 layer2 for one device\n",
+           __func__);
+       return -EBUSY;
+}
+
+static int
+get_free_tei(struct manager *mgr)
+{
+       u64             ids = 0;
+       int             i;
+       struct layer2   *l2;
+
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->ch.nr == 0)
+                       continue;
+               if ((l2->ch.addr & 0xff) != 0)
+                       continue;
+               i = l2->ch.addr >> 8;
+               if (i < 64)
+                       continue;
+               i -= 64;
+
+               test_and_set_bit(i, (u_long *)&ids);
+       }
+       for (i = 0; i < 64; i++)
+               if (!test_bit(i, (u_long *)&ids))
+                       return i + 64;
+       printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n",
+           __func__);
+       return -1;
+}
+
+static void
+teiup_create(struct manager *mgr, u_int prim, int len, void *arg)
+{
+       struct sk_buff  *skb;
+       struct mISDNhead *hh;
+       int             err;
+
+       skb = mI_alloc_skb(len, GFP_ATOMIC);
+       if (!skb)
+               return;
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = (mgr->ch.nr << 16) | mgr->ch.addr;
+       if (len)
+               memcpy(skb_put(skb, len), arg, len);
+       err = mgr->up->send(mgr->up, skb);
+       if (err) {
+               printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+               dev_kfree_skb(skb);
+       }
+}
+
+static u_int
+new_id(struct manager *mgr)
+{
+       u_int   id;
+
+       id = mgr->nextid++;
+       if (id == 0x7fff)
+               mgr->nextid = 1;
+       id <<= 16;
+       id |= GROUP_TEI << 8;
+       id |= TEI_SAPI;
+       return id;
+}
+
+static void
+do_send(struct manager *mgr)
+{
+       if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+               return;
+
+       if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) {
+               struct sk_buff  *skb = skb_dequeue(&mgr->sendq);
+
+               if (!skb) {
+                       test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+                       return;
+               }
+               mgr->lastid = mISDN_HEAD_ID(skb);
+               mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+               if (mgr->ch.recv(mgr->ch.peer, skb)) {
+                       dev_kfree_skb(skb);
+                       test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+                       mgr->lastid = MISDN_ID_NONE;
+               }
+       }
+}
+
+static void
+do_ack(struct manager *mgr, u_int id)
+{
+       if (test_bit(MGR_PH_NOTREADY, &mgr->options)) {
+               if (id == mgr->lastid) {
+                       if (test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+                               struct sk_buff  *skb;
+
+                               skb = skb_dequeue(&mgr->sendq);
+                               if (skb) {
+                                       mgr->lastid = mISDN_HEAD_ID(skb);
+                                       if (!mgr->ch.recv(mgr->ch.peer, skb))
+                                               return;
+                                       dev_kfree_skb(skb);
+                               }
+                       }
+                       mgr->lastid = MISDN_ID_NONE;
+                       test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+               }
+       }
+}
+
+static void
+mgr_send_down(struct manager *mgr, struct sk_buff *skb)
+{
+       skb_queue_tail(&mgr->sendq, skb);
+       if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+               _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+                   NULL, GFP_KERNEL);
+       } else {
+               do_send(mgr);
+       }
+}
+
+static int
+dl_unit_data(struct manager *mgr, struct sk_buff *skb)
+{
+       if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */
+               return -EINVAL;
+       if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+               _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+                   NULL, GFP_KERNEL);
+       skb_push(skb, 3);
+       skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */
+       skb->data[1] = 0xff; /* TEI 127 */
+       skb->data[2] = UI;   /* UI frame */
+       mISDN_HEAD_PRIM(skb) = PH_DATA_REQ;
+       mISDN_HEAD_ID(skb) = new_id(mgr);
+       skb_queue_tail(&mgr->sendq, skb);
+       do_send(mgr);
+       return 0;
+}
+
+unsigned int
+random_ri(void)
+{
+       u16 x;
+
+       get_random_bytes(&x, sizeof(x));
+       return x;
+}
+
+static struct layer2 *
+findtei(struct manager *mgr, int tei)
+{
+       struct layer2   *l2;
+       u_long          flags;
+
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if ((l2->sapi == 0) && (l2->tei > 0) &&
+                   (l2->tei != GROUP_TEI) && (l2->tei == tei))
+                       goto done;
+       }
+       l2 = NULL;
+done:
+       read_unlock_irqrestore(&mgr->lock, flags);
+       return l2;
+}
+
+static void
+put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, u_char tei)
+{
+       struct sk_buff *skb;
+       u_char bp[8];
+
+       bp[0] = (TEI_SAPI << 2);
+       if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+               bp[0] |= 2; /* CR:=1 for net command */
+       bp[1] = (GROUP_TEI << 1) | 0x1;
+       bp[2] = UI;
+       bp[3] = TEI_ENTITY_ID;
+       bp[4] = ri >> 8;
+       bp[5] = ri & 0xff;
+       bp[6] = m_id;
+       bp[7] = (tei << 1) | 1;
+       skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr),
+           8, bp, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_WARNING "%s: no skb for tei msg\n", __func__);
+               return;
+       }
+       mgr_send_down(mgr, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (tm->l2->tei != GROUP_TEI) {
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "assign request for allready assigned tei %d",
+                       tm->l2->tei);
+               return;
+       }
+       tm->ri = random_ri();
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "assign request ri %d", tm->ri);
+       put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+       mISDN_FsmChangeState(fi, ST_TEI_IDREQ);
+       mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1);
+       tm->nval = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr   *tm = fi->userdata;
+       struct layer2   *l2;
+       u_char *dp = arg;
+       int ri, tei;
+
+       ri = ((unsigned int) *dp++ << 8);
+       ri += *dp++;
+       dp++;
+       tei = *dp >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity assign ri %d tei %d",
+                       ri, tei);
+       l2 = findtei(tm->mgr, tei);
+       if (l2) {       /* same tei is in use */
+               if (ri != l2->tm->ri) {
+                       tm->tei_m.printdebug(fi,
+                               "possible duplicate assignment tei %d", tei);
+                       tei_l2(l2, MDL_ERROR_RSP, 0);
+               }
+       } else if (ri == tm->ri) {
+               mISDN_FsmDelTimer(&tm->timer, 1);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+               tei_l2(tm->l2, MDL_ASSIGN_REQ, tei);
+       }
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr   *tm = fi->userdata;
+       struct layer2   *l2;
+       u_char *dp = arg;
+       int tei, ri;
+
+       ri = ((unsigned int) *dp++ << 8);
+       ri += *dp++;
+       dp++;
+       tei = *dp >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d",
+                       ri, tei);
+       l2 = findtei(tm->mgr, tei);
+       if (l2) {       /* same tei is in use */
+               if (ri != l2->tm->ri) { /* and it wasn't our request */
+                       tm->tei_m.printdebug(fi,
+                               "possible duplicate assignment tei %d", tei);
+                       mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL);
+               }
+       }
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int ri, tei;
+
+       ri = ((unsigned int) *dp++ << 8);
+       ri += *dp++;
+       dp++;
+       tei = *dp >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity denied ri %d tei %d",
+                       ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = *(dp+3) >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity check req tei %d", tei);
+       if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) ||
+           (tei == tm->l2->tei))) {
+               mISDN_FsmDelTimer(&tm->timer, 4);
+               mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+               put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei);
+       }
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = *(dp+3) >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity remove tei %d", tei);
+       if ((tm->l2->tei != GROUP_TEI) &&
+           ((tei == GROUP_TEI) || (tei == tm->l2->tei))) {
+               mISDN_FsmDelTimer(&tm->timer, 5);
+               mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+               tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+       }
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "id verify request for tei %d",
+                       tm->l2->tei);
+       put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+       mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+       mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+       tm->nval = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (--tm->nval) {
+               tm->ri = random_ri();
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi, "assign req(%d) ri %d",
+                               4 - tm->nval, tm->ri);
+               put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+               mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3);
+       } else {
+               tm->tei_m.printdebug(fi, "assign req failed");
+               tei_l2(tm->l2, MDL_ERROR_RSP, 0);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+       }
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (--tm->nval) {
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi,
+                               "id verify req(%d) for tei %d",
+                               3 - tm->nval, tm->l2->tei);
+               put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+               mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+       } else {
+               tm->tei_m.printdebug(fi, "verify req for tei %d failed",
+                       tm->l2->tei);
+               tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+       }
+}
+
+static struct FsmNode TeiFnListUser[] =
+{
+       {ST_TEI_NOP, EV_IDREQ, tei_id_request},
+       {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+       {ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+       {ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+       {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+       {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout},
+       {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+       {ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+       {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout},
+       {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+       {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+static void
+tei_l2remove(struct layer2 *l2)
+{
+       put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei);
+       tei_l2(l2, MDL_REMOVE_REQ, 0);
+       list_del(&l2->ch.list);
+       l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+}
+
+static void
+tei_assign_req(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+
+       if (tm->l2->tei == GROUP_TEI) {
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "net tei assign request without tei");
+               return;
+       }
+       tm->ri = ((unsigned int) *dp++ << 8);
+       tm->ri += *dp++;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "net assign request ri %d teim %d", tm->ri, *dp);
+       put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei);
+       mISDN_FsmChangeState(fi, ST_TEI_NOP);
+}
+
+static void
+tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr   *tm = fi->userdata;
+
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "id check request for tei %d",
+                   tm->l2->tei);
+       tm->rcnt = 0;
+       put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+       mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+       mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+       tm->nval = 2;
+}
+
+static void
+tei_id_chk_resp(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = dp[3] >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity check resp tei %d", tei);
+       if (tei == tm->l2->tei)
+               tm->rcnt++;
+}
+
+static void
+tei_id_verify_net(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = dp[3] >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity verify req tei %d/%d",
+                   tei, tm->l2->tei);
+       if (tei == tm->l2->tei)
+               tei_id_chk_req_net(fi, event, arg);
+}
+
+static void
+tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (tm->rcnt == 1) {
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi,
+                           "check req for tei %d sucessful\n", tm->l2->tei);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+       } else if (tm->rcnt > 1) {
+               /* duplicate assignment; remove */
+               tei_l2remove(tm->l2);
+       } else if (--tm->nval) {
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi,
+                               "id check req(%d) for tei %d",
+                               3 - tm->nval, tm->l2->tei);
+               put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+               mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+       } else {
+               tm->tei_m.printdebug(fi, "check req for tei %d failed",
+                       tm->l2->tei);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+               tei_l2remove(tm->l2);
+       }
+}
+
+static struct FsmNode TeiFnListNet[] =
+{
+       {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req},
+       {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net},
+       {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net},
+       {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net},
+       {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp},
+};
+
+static void
+tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len)
+{
+       if (test_bit(FLG_FIXED_TEI, &tm->l2->flag))
+               return;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt);
+       if (mt == ID_ASSIGNED)
+               mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp);
+       else if (mt == ID_DENIED)
+               mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp);
+       else if (mt == ID_CHK_REQ)
+               mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp);
+       else if (mt == ID_REMOVE)
+               mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp);
+       else if (mt == ID_VERIFY)
+               mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp);
+       else if (mt == ID_CHK_RES)
+               mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp);
+}
+
+static struct layer2 *
+create_new_tei(struct manager *mgr, int tei)
+{
+       u_long          opt = 0;
+       u_long          flags;
+       int             id;
+       struct layer2   *l2;
+
+       if (!mgr->up)
+               return NULL;
+       if (tei < 64)
+               test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+       if (mgr->ch.st->dev->Dprotocols
+         & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+               test_and_set_bit(OPTION_L2_PMX, &opt);
+       l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei);
+       if (!l2) {
+               printk(KERN_WARNING "%s:no memory for layer2\n", __func__);
+               return NULL;
+       }
+       l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+       if (!l2->tm) {
+               kfree(l2);
+               printk(KERN_WARNING "%s:no memory for teimgr\n", __func__);
+               return NULL;
+       }
+       l2->tm->mgr = mgr;
+       l2->tm->l2 = l2;
+       l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+       l2->tm->tei_m.userdata = l2->tm;
+       l2->tm->tei_m.printdebug = tei_debug;
+       l2->tm->tei_m.fsm = &teifsmn;
+       l2->tm->tei_m.state = ST_TEI_NOP;
+       l2->tm->tval = 2000; /* T202  2 sec */
+       mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+       write_lock_irqsave(&mgr->lock, flags);
+       id = get_free_id(mgr);
+       list_add_tail(&l2->list, &mgr->layer2);
+       write_unlock_irqrestore(&mgr->lock, flags);
+       if (id < 0) {
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+               printk(KERN_WARNING "%s:no free id\n", __func__);
+               return NULL;
+       } else {
+               l2->ch.nr = id;
+               __add_layer2(&l2->ch, mgr->ch.st);
+               l2->ch.recv = mgr->ch.recv;
+               l2->ch.peer = mgr->ch.peer;
+               l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+       }
+       return l2;
+}
+
+static void
+new_tei_req(struct manager *mgr, u_char *dp)
+{
+       int             tei, ri;
+       struct layer2   *l2;
+
+       ri = dp[0] << 8;
+       ri += dp[1];
+       if (!mgr->up)
+               goto denied;
+       tei = get_free_tei(mgr);
+       if (tei < 0) {
+               printk(KERN_WARNING "%s:No free tei\n", __func__);
+               goto denied;
+       }
+       l2 = create_new_tei(mgr, tei);
+       if (!l2)
+               goto denied;
+       else
+               mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp);
+       return;
+denied:
+       put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI);
+}
+
+static int
+ph_data_ind(struct manager *mgr, struct sk_buff *skb)
+{
+       int             ret = -EINVAL;
+       struct layer2   *l2;
+       u_long          flags;
+       u_char          mt;
+
+       if (skb->len < 8) {
+               if (*debug  & DEBUG_L2_TEI)
+                       printk(KERN_DEBUG "%s: short mgr frame %d/8\n",
+                           __func__, skb->len);
+               goto done;
+       }
+       if (*debug  & DEBUG_L2_TEI)
+
+       if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */
+               goto done;
+       if (skb->data[0] & 1) /* EA0 formal error */
+               goto done;
+       if (!(skb->data[1] & 1)) /* EA1 formal error */
+               goto done;
+       if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */
+               goto done;
+       if ((skb->data[2] & 0xef) != UI) /* not UI */
+               goto done;
+       if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */
+               goto done;
+       mt = skb->data[6];
+       switch (mt) {
+       case ID_REQUEST:
+       case ID_CHK_RES:
+       case ID_VERIFY:
+               if (!test_bit(MGR_OPT_NETWORK, &mgr->options))
+                       goto done;
+               break;
+       case ID_ASSIGNED:
+       case ID_DENIED:
+       case ID_CHK_REQ:
+       case ID_REMOVE:
+               if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+                       goto done;
+               break;
+       default:
+               goto done;
+       }
+       ret = 0;
+       if (mt == ID_REQUEST) {
+               new_tei_req(mgr, &skb->data[4]);
+               goto done;
+       }
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4);
+       }
+       read_unlock_irqrestore(&mgr->lock, flags);
+done:
+       return ret;
+}
+
+int
+l2_tei(struct layer2 *l2, u_int cmd, u_long arg)
+{
+       struct teimgr   *tm = l2->tm;
+
+       if (test_bit(FLG_FIXED_TEI, &l2->flag))
+               return 0;
+       if (*debug & DEBUG_L2_TEI)
+               printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+       switch (cmd) {
+       case MDL_ASSIGN_IND:
+               mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL);
+               break;
+       case MDL_ERROR_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei);
+               if (test_bit(MGR_OPT_USER, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL);
+               break;
+       case MDL_STATUS_UP_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL);
+               break;
+       case MDL_STATUS_DOWN_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL);
+               break;
+       case MDL_STATUS_UI_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL);
+               break;
+       }
+       return 0;
+}
+
+void
+TEIrelease(struct layer2 *l2)
+{
+       struct teimgr   *tm = l2->tm;
+       u_long          flags;
+
+       mISDN_FsmDelTimer(&tm->timer, 1);
+       write_lock_irqsave(&tm->mgr->lock, flags);
+       list_del(&l2->list);
+       write_unlock_irqrestore(&tm->mgr->lock, flags);
+       l2->tm = NULL;
+       kfree(tm);
+}
+
+static int
+create_teimgr(struct manager *mgr, struct channel_req *crq)
+{
+       struct layer2   *l2;
+       u_long          opt = 0;
+       u_long          flags;
+       int             id;
+
+       if (*debug & DEBUG_L2_TEI)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, mgr->ch.st->dev->name, crq->protocol,
+                       crq->adr.dev, crq->adr.channel, crq->adr.sapi,
+                       crq->adr.tei);
+       if (crq->adr.sapi != 0) /* not supported yet */
+               return -EINVAL;
+       if (crq->adr.tei > GROUP_TEI)
+               return -EINVAL;
+       if (crq->adr.tei < 64)
+               test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+       if (crq->adr.tei == 0)
+               test_and_set_bit(OPTION_L2_PTP, &opt);
+       if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+               if (crq->protocol == ISDN_P_LAPD_TE)
+                       return -EPROTONOSUPPORT;
+               if ((crq->adr.tei != 0) && (crq->adr.tei != 127))
+                       return -EINVAL;
+               if (mgr->up) {
+                       printk(KERN_WARNING
+                           "%s: only one network manager is allowed\n",
+                           __func__);
+                       return -EBUSY;
+               }
+       } else if (test_bit(MGR_OPT_USER, &mgr->options)) {
+               if (crq->protocol == ISDN_P_LAPD_NT)
+                       return -EPROTONOSUPPORT;
+               if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI))
+                       return -EINVAL; /* dyn tei */
+       } else {
+               if (crq->protocol == ISDN_P_LAPD_NT)
+                       test_and_set_bit(MGR_OPT_NETWORK, &mgr->options);
+               if (crq->protocol == ISDN_P_LAPD_TE)
+                       test_and_set_bit(MGR_OPT_USER, &mgr->options);
+       }
+       if (mgr->ch.st->dev->Dprotocols
+         & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+               test_and_set_bit(OPTION_L2_PMX, &opt);
+       if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) {
+               mgr->up = crq->ch;
+               id = DL_INFO_L2_CONNECT;
+               teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id);
+               crq->ch = NULL;
+               if (!list_empty(&mgr->layer2)) {
+                       read_lock_irqsave(&mgr->lock, flags);
+                       list_for_each_entry(l2, &mgr->layer2, list) {
+                               l2->up = mgr->up;
+                               l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+                       }
+                       read_unlock_irqrestore(&mgr->lock, flags);
+               }
+               return 0;
+       }
+       l2 = create_l2(crq->ch, crq->protocol, (u_int)opt,
+               (u_long)crq->adr.tei);
+       if (!l2)
+               return -ENOMEM;
+       l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+       if (!l2->tm) {
+               kfree(l2);
+               printk(KERN_ERR "kmalloc teimgr failed\n");
+               return -ENOMEM;
+       }
+       l2->tm->mgr = mgr;
+       l2->tm->l2 = l2;
+       l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+       l2->tm->tei_m.userdata = l2->tm;
+       l2->tm->tei_m.printdebug = tei_debug;
+       if (crq->protocol == ISDN_P_LAPD_TE) {
+               l2->tm->tei_m.fsm = &teifsmu;
+               l2->tm->tei_m.state = ST_TEI_NOP;
+               l2->tm->tval = 1000; /* T201  1 sec */
+       } else {
+               l2->tm->tei_m.fsm = &teifsmn;
+               l2->tm->tei_m.state = ST_TEI_NOP;
+               l2->tm->tval = 2000; /* T202  2 sec */
+       }
+       mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+       write_lock_irqsave(&mgr->lock, flags);
+       id = get_free_id(mgr);
+       list_add_tail(&l2->list, &mgr->layer2);
+       write_unlock_irqrestore(&mgr->lock, flags);
+       if (id < 0) {
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+       } else {
+               l2->ch.nr = id;
+               l2->up->nr = id;
+               crq->ch = &l2->ch;
+               id = 0;
+       }
+       return id;
+}
+
+static int
+mgr_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct manager  *mgr;
+       struct mISDNhead        *hh =  mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+
+       mgr = container_of(ch, struct manager, ch);
+       if (*debug & DEBUG_L2_RECV)
+               printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+                   __func__, hh->prim, hh->id);
+       switch (hh->prim) {
+       case PH_DATA_IND:
+               mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+               ret = ph_data_ind(mgr, skb);
+               break;
+       case PH_DATA_CNF:
+               do_ack(mgr, hh->id);
+               ret = 0;
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(MGR_PH_ACTIVE, &mgr->options);
+               mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL);
+               do_send(mgr);
+               ret = 0;
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options);
+               mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL);
+               ret = 0;
+               break;
+       case DL_UNITDATA_REQ:
+               return dl_unit_data(mgr, skb);
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+free_teimanager(struct manager *mgr)
+{
+       struct layer2   *l2, *nl2;
+
+       if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+               /* not locked lock is taken in release tei */
+               mgr->up = NULL;
+               if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) {
+                       list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+                               put_tei_msg(mgr, ID_REMOVE, 0, l2->tei);
+                               mutex_lock(&mgr->ch.st->lmutex);
+                               list_del(&l2->ch.list);
+                               mutex_unlock(&mgr->ch.st->lmutex);
+                               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+                       }
+                       test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options);
+               } else {
+                       list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+                               l2->up = NULL;
+                       }
+               }
+       }
+       if (test_bit(MGR_OPT_USER, &mgr->options)) {
+               if (list_empty(&mgr->layer2))
+                       test_and_clear_bit(MGR_OPT_USER, &mgr->options);
+       }
+       mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL);
+       return 0;
+}
+
+static int
+ctrl_teimanager(struct manager *mgr, void *arg)
+{
+       /* currently we only have one option */
+       int     clean = *((int *)arg);
+
+       if (clean)
+               test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options);
+       else
+               test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options);
+       return 0;
+}
+
+/* This function does create a L2 for fixed TEI in NT Mode */
+static int
+check_data(struct manager *mgr, struct sk_buff *skb)
+{
+       struct mISDNhead        *hh =  mISDN_HEAD_P(skb);
+       int                     ret, tei;
+       struct layer2           *l2;
+
+       if (*debug & DEBUG_L2_CTRL)
+               printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+                   __func__, hh->prim, hh->id);
+       if (test_bit(MGR_OPT_USER, &mgr->options))
+               return -ENOTCONN;
+       if (hh->prim != PH_DATA_IND)
+               return -ENOTCONN;
+       if (skb->len != 3)
+               return -ENOTCONN;
+       if (skb->data[0] != 0)
+               /* only SAPI 0 command */
+               return -ENOTCONN;
+       if (!(skb->data[1] & 1)) /* invalid EA1 */
+               return -EINVAL;
+       tei = skb->data[1] >> 0;
+       if (tei > 63) /* not a fixed tei */
+               return -ENOTCONN;
+       if ((skb->data[2] & ~0x10) != SABME)
+               return -ENOTCONN;
+       /* We got a SABME for a fixed TEI */
+       l2 = create_new_tei(mgr, tei);
+       if (!l2)
+               return -ENOMEM;
+       ret = l2->ch.send(&l2->ch, skb);
+       return ret;
+}
+
+void
+delete_teimanager(struct mISDNchannel *ch)
+{
+       struct manager  *mgr;
+       struct layer2   *l2, *nl2;
+
+       mgr = container_of(ch, struct manager, ch);
+       /* not locked lock is taken in release tei */
+       list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+               mutex_lock(&mgr->ch.st->lmutex);
+               list_del(&l2->ch.list);
+               mutex_unlock(&mgr->ch.st->lmutex);
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+       }
+       list_del(&mgr->ch.list);
+       list_del(&mgr->bcast.list);
+       skb_queue_purge(&mgr->sendq);
+       kfree(mgr);
+}
+
+static int
+mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct manager  *mgr;
+       int             ret = -EINVAL;
+
+       mgr = container_of(ch, struct manager, ch);
+       if (*debug & DEBUG_L2_CTRL)
+               printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               ret = create_teimgr(mgr, arg);
+               break;
+       case CLOSE_CHANNEL:
+               ret = free_teimanager(mgr);
+               break;
+       case CONTROL_CHANNEL:
+               ret = ctrl_teimanager(mgr, arg);
+               break;
+       case CHECK_DATA:
+               ret = check_data(mgr, arg);
+               break;
+       }
+       return ret;
+}
+
+static int
+mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct manager          *mgr = container_of(ch, struct manager, bcast);
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       struct sk_buff          *cskb = NULL;
+       struct layer2           *l2;
+       u_long                  flags;
+       int                     ret;
+
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if ((hh->id & MISDN_ID_SAPI_MASK) ==
+                   (l2->ch.addr & MISDN_ID_SAPI_MASK)) {
+                       if (list_is_last(&l2->list, &mgr->layer2)) {
+                               cskb = skb;
+                               skb = NULL;
+                       } else {
+                               if (!cskb)
+                                       cskb = skb_copy(skb, GFP_KERNEL);
+                       }
+                       if (cskb) {
+                               ret = l2->ch.send(&l2->ch, cskb);
+                               if (ret) {
+                                       if (*debug & DEBUG_SEND_ERR)
+                                               printk(KERN_DEBUG
+                                                   "%s ch%d prim(%x) addr(%x)"
+                                                   " err %d\n",
+                                                   __func__, l2->ch.nr,
+                                                   hh->prim, l2->ch.addr, ret);
+                               } else
+                                       cskb = NULL;
+                       } else {
+                               printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+                                   __func__, ch->nr, ch->addr);
+                               goto out;
+                       }
+               }
+       }
+out:
+       read_unlock_irqrestore(&mgr->lock, flags);
+       if (cskb)
+               dev_kfree_skb(cskb);
+       if (skb)
+               dev_kfree_skb(skb);
+       return 0;
+}
+
+static int
+mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+
+       return -EINVAL;
+}
+
+int
+create_teimanager(struct mISDNdevice *dev)
+{
+       struct manager *mgr;
+
+       mgr = kzalloc(sizeof(struct manager), GFP_KERNEL);
+       if (!mgr)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&mgr->layer2);
+       mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock);
+       skb_queue_head_init(&mgr->sendq);
+       mgr->nextid = 1;
+       mgr->lastid = MISDN_ID_NONE;
+       mgr->ch.send = mgr_send;
+       mgr->ch.ctrl = mgr_ctrl;
+       mgr->ch.st = dev->D.st;
+       set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI);
+       add_layer2(&mgr->ch, dev->D.st);
+       mgr->bcast.send = mgr_bcast;
+       mgr->bcast.ctrl = mgr_bcast_ctrl;
+       mgr->bcast.st = dev->D.st;
+       set_channel_address(&mgr->bcast, 0, GROUP_TEI);
+       add_layer2(&mgr->bcast, dev->D.st);
+       mgr->deact.debug = *debug & DEBUG_MANAGER;
+       mgr->deact.userdata = mgr;
+       mgr->deact.printdebug = da_debug;
+       mgr->deact.fsm = &deactfsm;
+       mgr->deact.state = ST_L1_DEACT;
+       mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer);
+       dev->teimgr = &mgr->ch;
+       return 0;
+}
+
+int TEIInit(u_int *deb)
+{
+       debug = deb;
+       teifsmu.state_count = TEI_STATE_COUNT;
+       teifsmu.event_count = TEI_EVENT_COUNT;
+       teifsmu.strEvent = strTeiEvent;
+       teifsmu.strState = strTeiState;
+       mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser));
+       teifsmn.state_count = TEI_STATE_COUNT;
+       teifsmn.event_count = TEI_EVENT_COUNT;
+       teifsmn.strEvent = strTeiEvent;
+       teifsmn.strState = strTeiState;
+       mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet));
+       deactfsm.state_count =  DEACT_STATE_COUNT;
+       deactfsm.event_count = DEACT_EVENT_COUNT;
+       deactfsm.strEvent = strDeactEvent;
+       deactfsm.strState = strDeactState;
+       mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList));
+       return 0;
+}
+
+void TEIFree(void)
+{
+       mISDN_FsmFree(&teifsmu);
+       mISDN_FsmFree(&teifsmn);
+       mISDN_FsmFree(&deactfsm);
+}
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
new file mode 100644 (file)
index 0000000..b5fabc7
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ *
+ * general timer device for using in ISDN stacks
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mISDNif.h>
+
+static int     *debug;
+
+
+struct mISDNtimerdev {
+       int                     next_id;
+       struct list_head        pending;
+       struct list_head        expired;
+       wait_queue_head_t       wait;
+       u_int                   work;
+       spinlock_t              lock; /* protect lists */
+};
+
+struct mISDNtimer {
+       struct list_head        list;
+       struct  mISDNtimerdev   *dev;
+       struct timer_list       tl;
+       int                     id;
+};
+
+static int
+mISDN_open(struct inode *ino, struct file *filep)
+{
+       struct mISDNtimerdev    *dev;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+       dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+       dev->next_id = 1;
+       INIT_LIST_HEAD(&dev->pending);
+       INIT_LIST_HEAD(&dev->expired);
+       spin_lock_init(&dev->lock);
+       dev->work = 0;
+       init_waitqueue_head(&dev->wait);
+       filep->private_data = dev;
+       __module_get(THIS_MODULE);
+       return 0;
+}
+
+static int
+mISDN_close(struct inode *ino, struct file *filep)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       struct mISDNtimer       *timer, *next;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+       list_for_each_entry_safe(timer, next, &dev->pending, list) {
+               del_timer(&timer->tl);
+               kfree(timer);
+       }
+       list_for_each_entry_safe(timer, next, &dev->expired, list) {
+               kfree(timer);
+       }
+       kfree(dev);
+       module_put(THIS_MODULE);
+       return 0;
+}
+
+static ssize_t
+mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       struct mISDNtimer       *timer;
+       u_long  flags;
+       int     ret = 0;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
+                       filep, buf, (int)count, off);
+       if (*off != filep->f_pos)
+               return -ESPIPE;
+
+       if (list_empty(&dev->expired) && (dev->work == 0)) {
+               if (filep->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               wait_event_interruptible(dev->wait, (dev->work ||
+                   !list_empty(&dev->expired)));
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+       }
+       if (count < sizeof(int))
+               return -ENOSPC;
+       if (dev->work)
+               dev->work = 0;
+       if (!list_empty(&dev->expired)) {
+               spin_lock_irqsave(&dev->lock, flags);
+               timer = (struct mISDNtimer *)dev->expired.next;
+               list_del(&timer->list);
+               spin_unlock_irqrestore(&dev->lock, flags);
+               if (put_user(timer->id, (int *)buf))
+                       ret = -EFAULT;
+               else
+                       ret = sizeof(int);
+               kfree(timer);
+       }
+       return ret;
+}
+
+static loff_t
+mISDN_llseek(struct file *filep, loff_t offset, int orig)
+{
+       return -ESPIPE;
+}
+
+static ssize_t
+mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off)
+{
+       return -EOPNOTSUPP;
+}
+
+static unsigned int
+mISDN_poll(struct file *filep, poll_table *wait)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       unsigned int            mask = POLLERR;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
+       if (dev) {
+               poll_wait(filep, &dev->wait, wait);
+               mask = 0;
+               if (dev->work || !list_empty(&dev->expired))
+                       mask |= (POLLIN | POLLRDNORM);
+               if (*debug & DEBUG_TIMER)
+                       printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
+                               dev->work, list_empty(&dev->expired));
+       }
+       return mask;
+}
+
+static void
+dev_expire_timer(struct mISDNtimer *timer)
+{
+       u_long                  flags;
+
+       spin_lock_irqsave(&timer->dev->lock, flags);
+       list_del(&timer->list);
+       list_add_tail(&timer->list, &timer->dev->expired);
+       spin_unlock_irqrestore(&timer->dev->lock, flags);
+       wake_up_interruptible(&timer->dev->wait);
+}
+
+static int
+misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
+{
+       int                     id;
+       u_long                  flags;
+       struct mISDNtimer       *timer;
+
+       if (!timeout) {
+               dev->work = 1;
+               wake_up_interruptible(&dev->wait);
+               id = 0;
+       } else {
+               timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
+               if (!timer)
+                       return -ENOMEM;
+               spin_lock_irqsave(&dev->lock, flags);
+               timer->id = dev->next_id++;
+               if (dev->next_id < 0)
+                       dev->next_id = 1;
+               list_add_tail(&timer->list, &dev->pending);
+               spin_unlock_irqrestore(&dev->lock, flags);
+               timer->dev = dev;
+               timer->tl.data = (long)timer;
+               timer->tl.function = (void *) dev_expire_timer;
+               init_timer(&timer->tl);
+               timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
+               add_timer(&timer->tl);
+               id = timer->id;
+       }
+       return id;
+}
+
+static int
+misdn_del_timer(struct mISDNtimerdev *dev, int id)
+{
+       u_long                  flags;
+       struct mISDNtimer       *timer;
+       int                     ret = 0;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       list_for_each_entry(timer, &dev->pending, list) {
+               if (timer->id == id) {
+                       list_del_init(&timer->list);
+                       del_timer(&timer->tl);
+                       ret = timer->id;
+                       kfree(timer);
+                       goto unlock;
+               }
+       }
+unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int
+mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+    unsigned long arg)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       int                     id, tout, ret = 0;
+
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
+                   filep, cmd, arg);
+       switch (cmd) {
+       case IMADDTIMER:
+               if (get_user(tout, (int __user *)arg)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               id = misdn_add_timer(dev, tout);
+               if (*debug & DEBUG_TIMER)
+                       printk(KERN_DEBUG "%s add %d id %d\n", __func__,
+                           tout, id);
+               if (id < 0) {
+                       ret = id;
+                       break;
+               }
+               if (put_user(id, (int __user *)arg))
+                       ret = -EFAULT;
+               break;
+       case IMDELTIMER:
+               if (get_user(id, (int __user *)arg)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               if (*debug & DEBUG_TIMER)
+                       printk(KERN_DEBUG "%s del id %d\n", __func__, id);
+               id = misdn_del_timer(dev, id);
+               if (put_user(id, (int __user *)arg))
+                       ret = -EFAULT;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static struct file_operations mISDN_fops = {
+       .llseek         = mISDN_llseek,
+       .read           = mISDN_read,
+       .write          = mISDN_write,
+       .poll           = mISDN_poll,
+       .ioctl          = mISDN_ioctl,
+       .open           = mISDN_open,
+       .release        = mISDN_close,
+};
+
+static struct miscdevice mISDNtimer = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "mISDNtimer",
+       .fops   = &mISDN_fops,
+};
+
+int
+mISDN_inittimer(int *deb)
+{
+       int     err;
+
+       debug = deb;
+       err = misc_register(&mISDNtimer);
+       if (err)
+               printk(KERN_WARNING "mISDN: Could not register timer device\n");
+       return err;
+}
+
+void mISDN_timer_cleanup(void)
+{
+       misc_deregister(&mISDNtimer);
+}
index fea966d66f9838c140176a7dbd0ed8223e908f44..71dd65aa31b696aa37a894b0d7bd9cbdc2034123 100644 (file)
@@ -147,9 +147,12 @@ static struct priority_group *alloc_priority_group(void)
 static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 {
        struct pgpath *pgpath, *tmp;
+       struct multipath *m = ti->private;
 
        list_for_each_entry_safe(pgpath, tmp, pgpaths, list) {
                list_del(&pgpath->list);
+               if (m->hw_handler_name)
+                       scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
                dm_put_device(ti, pgpath->path.dev);
                free_pgpath(pgpath);
        }
@@ -548,6 +551,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
 {
        int r;
        struct pgpath *p;
+       struct multipath *m = ti->private;
 
        /* we need at least a path arg */
        if (as->argc < 1) {
@@ -566,6 +570,15 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
                goto bad;
        }
 
+       if (m->hw_handler_name) {
+               r = scsi_dh_attach(bdev_get_queue(p->path.dev->bdev),
+                                  m->hw_handler_name);
+               if (r < 0) {
+                       dm_put_device(ti, p->path.dev);
+                       goto bad;
+               }
+       }
+
        r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error);
        if (r) {
                dm_put_device(ti, p->path.dev);
index 34402c47027e3951ac1d7de9387ec81a61227a2d..d6a0074b9dc3b34c239fc8582fc3ef7a45dfc85c 100644 (file)
@@ -273,12 +273,12 @@ mpt_fault_reset_work(struct work_struct *work)
        ioc_raw_state = mpt_GetIocState(ioc, 0);
        if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
                printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
-                   ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
+                      ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
                printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
-                   ioc->name, __FUNCTION__);
+                      ioc->name, __func__);
                rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
                printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
-                   __FUNCTION__, (rc == 0) ? "success" : "failed");
+                      __func__, (rc == 0) ? "success" : "failed");
                ioc_raw_state = mpt_GetIocState(ioc, 0);
                if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
                        printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
@@ -356,7 +356,7 @@ mpt_turbo_reply(MPT_ADAPTER *ioc, u32 pa)
        if (!cb_idx || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS ||
                MptCallbacks[cb_idx] == NULL) {
                printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n",
-                               __FUNCTION__, ioc->name, cb_idx);
+                               __func__, ioc->name, cb_idx);
                goto out;
        }
 
@@ -420,7 +420,7 @@ mpt_reply(MPT_ADAPTER *ioc, u32 pa)
        if (!cb_idx || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS ||
                MptCallbacks[cb_idx] == NULL) {
                printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n",
-                               __FUNCTION__, ioc->name, cb_idx);
+                               __func__, ioc->name, cb_idx);
                freeme = 0;
                goto out;
        }
@@ -2434,7 +2434,7 @@ mpt_adapter_disable(MPT_ADAPTER *ioc)
 
        if (ioc->cached_fw != NULL) {
                ddlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: Pushing FW onto "
-                   "adapter\n", __FUNCTION__, ioc->name));
+                   "adapter\n", __func__, ioc->name));
                if ((ret = mpt_downloadboot(ioc, (MpiFwHeader_t *)
                    ioc->cached_fw, CAN_SLEEP)) < 0) {
                        printk(MYIOC_s_WARN_FMT
@@ -3693,7 +3693,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
 
        if (ioc->pcidev->device == MPI_MANUFACTPAGE_DEVID_SAS1078) {
                drsprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: Doorbell=%p; 1078 reset "
-                       "address=%p\n",  ioc->name, __FUNCTION__,
+                       "address=%p\n",  ioc->name, __func__,
                        &ioc->chip->Doorbell, &ioc->chip->Reset_1078));
                CHIPREG_WRITE32(&ioc->chip->Reset_1078, 0x07);
                if (sleepFlag == CAN_SLEEP)
@@ -4742,12 +4742,12 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
                break;
        }
 
-       printk("%s: persist_opcode=%x\n",__FUNCTION__, persist_opcode);
+       printk("%s: persist_opcode=%x\n",__func__, persist_opcode);
 
        /* Get a MF for this command.
         */
        if ((mf = mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
-               printk("%s: no msg frames!\n",__FUNCTION__);
+               printk("%s: no msg frames!\n",__func__);
                return -1;
         }
 
@@ -4771,13 +4771,13 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode)
            (SasIoUnitControlReply_t *)ioc->persist_reply_frame;
        if (le16_to_cpu(sasIoUnitCntrReply->IOCStatus) != MPI_IOCSTATUS_SUCCESS) {
                printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
-                   __FUNCTION__,
+                   __func__,
                    sasIoUnitCntrReply->IOCStatus,
                    sasIoUnitCntrReply->IOCLogInfo);
                return -1;
        }
 
-       printk("%s: success\n",__FUNCTION__);
+       printk("%s: success\n",__func__);
        return 0;
 }
 
@@ -5784,7 +5784,7 @@ SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp)
 
        if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc)) == NULL) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n",
-                   ioc->name,__FUNCTION__));
+                   ioc->name,__func__));
                return -1;
        }
 
index a5920423e2b23ac75ac4913ee9d142ebb755527c..f5233f3d9eff03399c13e3e0b7138ad620056797 100644 (file)
@@ -505,7 +505,7 @@ mptctl_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
        event = le32_to_cpu(pEvReply->Event) & 0xFF;
 
        dctlprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s() called\n",
-           ioc->name, __FUNCTION__));
+           ioc->name, __func__));
        if(async_queue == NULL)
                return 1;
 
@@ -2482,7 +2482,7 @@ mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
         */
        if ((mf = mpt_get_msg_frame(mptctl_id, ioc)) == NULL) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames!!\n",
-                   ioc->name,__FUNCTION__));
+                   ioc->name,__func__));
                goto out;
        }
 
index b36cae9ec6db6c81041f8dccd8b3ce59db466f2e..c3c24fdf9fb621c20acb8249496c6440cad8b8b6 100644 (file)
@@ -231,28 +231,28 @@ static int
 mptfc_abort(struct scsi_cmnd *SCpnt)
 {
        return
-           mptfc_block_error_handler(SCpnt, mptscsih_abort, __FUNCTION__);
+           mptfc_block_error_handler(SCpnt, mptscsih_abort, __func__);
 }
 
 static int
 mptfc_dev_reset(struct scsi_cmnd *SCpnt)
 {
        return
-           mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __FUNCTION__);
+           mptfc_block_error_handler(SCpnt, mptscsih_dev_reset, __func__);
 }
 
 static int
 mptfc_bus_reset(struct scsi_cmnd *SCpnt)
 {
        return
-           mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __FUNCTION__);
+           mptfc_block_error_handler(SCpnt, mptscsih_bus_reset, __func__);
 }
 
 static int
 mptfc_host_reset(struct scsi_cmnd *SCpnt)
 {
        return
-           mptfc_block_error_handler(SCpnt, mptscsih_host_reset, __FUNCTION__);
+           mptfc_block_error_handler(SCpnt, mptscsih_host_reset, __func__);
 }
 
 static void
index d709d92b7b30d0714ab2316f731ece1e8487e7fd..a1abf95cf75153d2f2f1abd74549d5ba446ae473 100644 (file)
@@ -610,7 +610,7 @@ mpt_lan_send_turbo(struct net_device *dev, u32 tmsg)
 
        dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n",
                        IOC_AND_NETDEV_NAMES_s_s(dev),
-                       __FUNCTION__, sent));
+                       __func__, sent));
 
        priv->SendCtl[ctx].skb = NULL;
        pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma,
@@ -676,7 +676,7 @@ mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep)
 
                dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n",
                                IOC_AND_NETDEV_NAMES_s_s(dev),
-                               __FUNCTION__, sent));
+                               __func__, sent));
 
                priv->SendCtl[ctx].skb = NULL;
                pci_unmap_single(mpt_dev->pcidev, priv->SendCtl[ctx].dma,
@@ -715,7 +715,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
        u16 cur_naa = 0x1000;
 
        dioprintk((KERN_INFO MYNAM ": %s called, skb_addr = %p\n",
-                       __FUNCTION__, skb));
+                       __func__, skb));
 
        spin_lock_irqsave(&priv->txfidx_lock, flags);
        if (priv->mpt_txfidx_tail < 0) {
@@ -723,7 +723,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
                spin_unlock_irqrestore(&priv->txfidx_lock, flags);
 
                printk (KERN_ERR "%s: no tx context available: %u\n",
-                       __FUNCTION__, priv->mpt_txfidx_tail);
+                       __func__, priv->mpt_txfidx_tail);
                return 1;
        }
 
@@ -733,7 +733,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev)
                spin_unlock_irqrestore(&priv->txfidx_lock, flags);
 
                printk (KERN_ERR "%s: Unable to alloc request frame\n",
-                       __FUNCTION__);
+                       __func__);
                return 1;
        }
 
@@ -1208,7 +1208,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
 
        dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, Start_buckets = %u, buckets_out = %u\n",
                        IOC_AND_NETDEV_NAMES_s_s(dev),
-                       __FUNCTION__, buckets, curr));
+                       __func__, buckets, curr));
 
        max = (mpt_dev->req_sz - MPT_LAN_RECEIVE_POST_REQUEST_SIZE) /
                        (MPT_LAN_TRANSACTION32_SIZE + sizeof(SGESimple64_t));
@@ -1217,9 +1217,9 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
                mf = mpt_get_msg_frame(LanCtx, mpt_dev);
                if (mf == NULL) {
                        printk (KERN_ERR "%s: Unable to alloc request frame\n",
-                               __FUNCTION__);
+                               __func__);
                        dioprintk((KERN_ERR "%s: %u buckets remaining\n",
-                                __FUNCTION__, buckets));
+                                __func__, buckets));
                        goto out;
                }
                pRecvReq = (LANReceivePostRequest_t *) mf;
@@ -1244,7 +1244,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
                        spin_lock_irqsave(&priv->rxfidx_lock, flags);
                        if (priv->mpt_rxfidx_tail < 0) {
                                printk (KERN_ERR "%s: Can't alloc context\n",
-                                       __FUNCTION__);
+                                       __func__);
                                spin_unlock_irqrestore(&priv->rxfidx_lock,
                                                       flags);
                                break;
@@ -1267,7 +1267,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
                                if (skb == NULL) {
                                        printk (KERN_WARNING
                                                MYNAM "/%s: Can't alloc skb\n",
-                                               __FUNCTION__);
+                                               __func__);
                                        priv->mpt_rxfidx[++priv->mpt_rxfidx_tail] = ctx;
                                        spin_unlock_irqrestore(&priv->rxfidx_lock, flags);
                                        break;
@@ -1305,7 +1305,7 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
 
                if (pSimple == NULL) {
 /**/                   printk (KERN_WARNING MYNAM "/%s: No buckets posted\n",
-/**/                           __FUNCTION__);
+/**/                           __func__);
                        mpt_free_msg_frame(mpt_dev, mf);
                        goto out;
                }
@@ -1329,9 +1329,9 @@ mpt_lan_post_receive_buckets(struct mpt_lan_priv *priv)
 
 out:
        dioprintk((KERN_INFO MYNAM "/%s: End_buckets = %u, priv->buckets_out = %u\n",
-                 __FUNCTION__, buckets, atomic_read(&priv->buckets_out)));
+                 __func__, buckets, atomic_read(&priv->buckets_out)));
        dioprintk((KERN_INFO MYNAM "/%s: Posted %u buckets and received %u back\n",
-       __FUNCTION__, priv->total_posted, priv->total_received));
+       __func__, priv->total_posted, priv->total_received));
 
        clear_bit(0, &priv->post_buckets_active);
 }
index b1147aa7afdebf594781c944faafb0414aadc8d8..12b732512e579049fe0152e1a570dd3594b8ad15 100644 (file)
@@ -300,7 +300,7 @@ mptsas_port_delete(MPT_ADAPTER *ioc, struct mptsas_portinfo_details * port_detai
        phy_info = port_info->phy_info;
 
        dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "%s: [%p]: num_phys=%02d "
-           "bitmask=0x%016llX\n", ioc->name, __FUNCTION__, port_details,
+           "bitmask=0x%016llX\n", ioc->name, __func__, port_details,
            port_details->num_phys, (unsigned long long)
            port_details->phy_bitmask));
 
@@ -411,7 +411,7 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
                 */
                dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
                    "%s: [%p]: deleting phy = %d\n",
-                   ioc->name, __FUNCTION__, port_details, i));
+                   ioc->name, __func__, port_details, i));
                port_details->num_phys--;
                port_details->phy_bitmask &= ~ (1 << phy_info->phy_id);
                memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
@@ -497,7 +497,7 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
                        continue;
                dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT
                    "%s: [%p]: phy_id=%02d num_phys=%02d "
-                   "bitmask=0x%016llX\n", ioc->name, __FUNCTION__,
+                   "bitmask=0x%016llX\n", ioc->name, __func__,
                    port_details, i, port_details->num_phys,
                    (unsigned long long)port_details->phy_bitmask));
                dsaswideprintk(ioc, printk(MYIOC_s_DEBUG_FMT "\t\tport = %p rphy=%p\n",
@@ -553,7 +553,7 @@ mptsas_target_reset(MPT_ADAPTER *ioc, u8 channel, u8 id)
 
        if ((mf = mpt_get_msg_frame(ioc->TaskCtx, ioc)) == NULL) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, no msg frames @%d!!\n",
-                   ioc->name,__FUNCTION__, __LINE__));
+                   ioc->name,__func__, __LINE__));
                return 0;
        }
 
@@ -606,7 +606,7 @@ mptsas_target_reset_queue(MPT_ADAPTER *ioc,
            GFP_ATOMIC);
        if (!target_reset_list) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
-                   ioc->name,__FUNCTION__, __LINE__));
+                   ioc->name,__func__, __LINE__));
                return;
        }
 
@@ -673,7 +673,7 @@ mptsas_dev_reset_complete(MPT_ADAPTER *ioc)
        ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
        if (!ev) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s, failed to allocate mem @%d..!!\n",
-                   ioc->name,__FUNCTION__, __LINE__));
+                   ioc->name,__func__, __LINE__));
                return;
        }
 
@@ -1183,7 +1183,7 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
        reply = (SasIoUnitControlReply_t *)ioc->sas_mgmt.reply;
        if (reply->IOCStatus != MPI_IOCSTATUS_SUCCESS) {
                printk(MYIOC_s_INFO_FMT "%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
-                   ioc->name, __FUNCTION__, reply->IOCStatus, reply->IOCLogInfo);
+                   ioc->name, __func__, reply->IOCStatus, reply->IOCLogInfo);
                error = -ENXIO;
                goto out_unlock;
        }
@@ -1270,14 +1270,14 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
 
        if (!rsp) {
                printk(MYIOC_s_ERR_FMT "%s: the smp response space is missing\n",
-                   ioc->name, __FUNCTION__);
+                   ioc->name, __func__);
                return -EINVAL;
        }
 
        /* do we need to support multiple segments? */
        if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
                printk(MYIOC_s_ERR_FMT "%s: multiple segments req %u %u, rsp %u %u\n",
-                   ioc->name, __FUNCTION__, req->bio->bi_vcnt, req->data_len,
+                   ioc->name, __func__, req->bio->bi_vcnt, req->data_len,
                    rsp->bio->bi_vcnt, rsp->data_len);
                return -EINVAL;
        }
@@ -1343,7 +1343,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
 
        timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ);
        if (!timeleft) {
-               printk(MYIOC_s_ERR_FMT "%s: smp timeout!\n", ioc->name, __FUNCTION__);
+               printk(MYIOC_s_ERR_FMT "%s: smp timeout!\n", ioc->name, __func__);
                /* On timeout reset the board */
                mpt_HardResetHandler(ioc, CAN_SLEEP);
                ret = -ETIMEDOUT;
@@ -1361,7 +1361,7 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
                rsp->data_len -= smprep->ResponseDataLength;
        } else {
                printk(MYIOC_s_ERR_FMT "%s: smp passthru reply failed to be returned\n",
-                   ioc->name, __FUNCTION__);
+                   ioc->name, __func__);
                ret = -ENXIO;
        }
 unmap:
@@ -2006,7 +2006,7 @@ static int mptsas_probe_one_phy(struct device *dev,
                        if (error) {
                                dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                        "%s: exit at line=%d\n", ioc->name,
-                                       __FUNCTION__, __LINE__));
+                                       __func__, __LINE__));
                                goto out;
                        }
                        mptsas_set_port(ioc, phy_info, port);
@@ -2076,7 +2076,7 @@ static int mptsas_probe_one_phy(struct device *dev,
                if (!rphy) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        goto out;
                }
 
@@ -2085,7 +2085,7 @@ static int mptsas_probe_one_phy(struct device *dev,
                if (error) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        sas_rphy_free(rphy);
                        goto out;
                }
@@ -2613,7 +2613,7 @@ mptsas_hotplug_work(struct work_struct *work)
                                    (ev->channel << 8) + ev->id)) {
                                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                        "%s: exit at line=%d\n", ioc->name,
-                                               __FUNCTION__, __LINE__));
+                                               __func__, __LINE__));
                                        break;
                                }
                                phy_info = mptsas_find_phyinfo_by_sas_address(
@@ -2633,20 +2633,20 @@ mptsas_hotplug_work(struct work_struct *work)
                if (!phy_info){
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break;
                }
                if (!phy_info->port_details) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break;
                }
                rphy = mptsas_get_rphy(phy_info);
                if (!rphy) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break;
                }
 
@@ -2654,7 +2654,7 @@ mptsas_hotplug_work(struct work_struct *work)
                if (!port) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break;
                }
 
@@ -2665,7 +2665,7 @@ mptsas_hotplug_work(struct work_struct *work)
                        if (!vtarget) {
                                dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                        "%s: exit at line=%d\n", ioc->name,
-                                       __FUNCTION__, __LINE__));
+                                       __func__, __LINE__));
                                break;
                        }
 
@@ -2720,7 +2720,7 @@ mptsas_hotplug_work(struct work_struct *work)
                        (ev->channel << 8) + ev->id)) {
                                dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                        "%s: exit at line=%d\n", ioc->name,
-                                       __FUNCTION__, __LINE__));
+                                       __func__, __LINE__));
                        break;
                }
 
@@ -2732,7 +2732,7 @@ mptsas_hotplug_work(struct work_struct *work)
                if (!phy_info || !phy_info->port_details) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break;
                }
 
@@ -2744,7 +2744,7 @@ mptsas_hotplug_work(struct work_struct *work)
                        if (!vtarget) {
                                dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                    "%s: exit at line=%d\n", ioc->name,
-                                   __FUNCTION__, __LINE__));
+                                   __func__, __LINE__));
                                break;
                        }
                        /*
@@ -2767,7 +2767,7 @@ mptsas_hotplug_work(struct work_struct *work)
                if (mptsas_get_rphy(phy_info)) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        if (ev->channel) printk("%d\n", __LINE__);
                        break;
                }
@@ -2776,7 +2776,7 @@ mptsas_hotplug_work(struct work_struct *work)
                if (!port) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break;
                }
                memcpy(&phy_info->attached, &sas_device,
@@ -2801,7 +2801,7 @@ mptsas_hotplug_work(struct work_struct *work)
                if (!rphy) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        break; /* non-fatal: an rphy can be added later */
                }
 
@@ -2809,7 +2809,7 @@ mptsas_hotplug_work(struct work_struct *work)
                if (sas_rphy_add(rphy)) {
                        dfailprintk(ioc, printk(MYIOC_s_ERR_FMT
                                "%s: exit at line=%d\n", ioc->name,
-                               __FUNCTION__, __LINE__));
+                               __func__, __LINE__));
                        sas_rphy_free(rphy);
                        break;
                }
index d142b6b4b9766d0591fb1d351ec7e924df2964c3..9f9354fd35160f8a55515437d703973cc5f7323d 100644 (file)
@@ -461,7 +461,7 @@ mptscsih_issue_sep_command(MPT_ADAPTER *ioc, VirtTarget *vtarget,
 
        if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) {
                dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: no msg frames!!\n",
-                   ioc->name,__FUNCTION__));
+                   ioc->name,__func__));
                return;
        }
 
@@ -2187,7 +2187,7 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *m
                                (ioc->debug_level & MPT_DEBUG_TM ))
                printk("%s: ha=%d [%d:%d:0] task_type=0x%02X "
                        "iocstatus=0x%04X\n\tloginfo=0x%08X response_code=0x%02X "
-                       "term_cmnds=%d\n", __FUNCTION__, ioc->id, pScsiTmReply->Bus,
+                       "term_cmnds=%d\n", __func__, ioc->id, pScsiTmReply->Bus,
                         pScsiTmReply->TargetID, pScsiTmReq->TaskType,
                        le16_to_cpu(pScsiTmReply->IOCStatus),
                        le32_to_cpu(pScsiTmReply->IOCLogInfo),pScsiTmReply->ResponseCode,
index 321eb913463504ed9e7699bdbc17f4b0fa6d297a..f5ade1904aadafefd641a90c36c63eacaf038dfd 100644 (file)
@@ -360,7 +360,7 @@ config THINKPAD_ACPI_VIDEO
          If you are not sure, say Y here.
 
 config THINKPAD_ACPI_HOTKEY_POLL
-       bool "Suport NVRAM polling for hot keys"
+       bool "Support NVRAM polling for hot keys"
        depends on THINKPAD_ACPI
        default y
        ---help---
index e171650766ce73bfc324c6799d4e3521949c72be..bf5e4d065436a2153c76ff13ef6adae2f2604a49 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/atmel-ssc.h>
 
index 19a1a254a0c5f81d8e0a36815caf4b870b6497cc..889e5f898f6f99485c6b031aee41f5a6ff86d0aa 100644 (file)
@@ -12,3 +12,4 @@ mmc_core-y                    := core.o bus.o host.o \
                                   sdio.o sdio_ops.o sdio_bus.o \
                                   sdio_cis.o sdio_io.o sdio_irq.o
 
+mmc_core-$(CONFIG_DEBUG_FS)    += debugfs.o
index fd95b18e988b3c33496c309f41dcc38356a82c47..0d9b2d6f9ebfba638b2bba41a0a0f6d28c922cc6 100644 (file)
@@ -252,6 +252,10 @@ int mmc_add_card(struct mmc_card *card)
        if (ret)
                return ret;
 
+#ifdef CONFIG_DEBUG_FS
+       mmc_add_card_debugfs(card);
+#endif
+
        mmc_card_set_present(card);
 
        return 0;
@@ -263,6 +267,10 @@ int mmc_add_card(struct mmc_card *card)
  */
 void mmc_remove_card(struct mmc_card *card)
 {
+#ifdef CONFIG_DEBUG_FS
+       mmc_remove_card_debugfs(card);
+#endif
+
        if (mmc_card_present(card)) {
                if (mmc_host_is_spi(card->host)) {
                        printk(KERN_INFO "%s: SPI card removed\n",
index cdb332b7dedc87340ef5ff0aacedc71f8b4fe931..c819effa1032c5ccebf896dff58d78f6e7ca13d5 100644 (file)
@@ -52,5 +52,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
 
 extern int use_spi_crc;
 
+/* Debugfs information for hosts and cards */
+void mmc_add_host_debugfs(struct mmc_host *host);
+void mmc_remove_host_debugfs(struct mmc_host *host);
+
+void mmc_add_card_debugfs(struct mmc_card *card);
+void mmc_remove_card_debugfs(struct mmc_card *card);
+
 #endif
 
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
new file mode 100644 (file)
index 0000000..1237bb4
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Debugfs support for hosts and cards
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "core.h"
+#include "mmc_ops.h"
+
+/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
+static int mmc_ios_show(struct seq_file *s, void *data)
+{
+       static const char *vdd_str[] = {
+               [8]     = "2.0",
+               [9]     = "2.1",
+               [10]    = "2.2",
+               [11]    = "2.3",
+               [12]    = "2.4",
+               [13]    = "2.5",
+               [14]    = "2.6",
+               [15]    = "2.7",
+               [16]    = "2.8",
+               [17]    = "2.9",
+               [18]    = "3.0",
+               [19]    = "3.1",
+               [20]    = "3.2",
+               [21]    = "3.3",
+               [22]    = "3.4",
+               [23]    = "3.5",
+               [24]    = "3.6",
+       };
+       struct mmc_host *host = s->private;
+       struct mmc_ios  *ios = &host->ios;
+       const char *str;
+
+       seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
+       seq_printf(s, "vdd:\t\t%u ", ios->vdd);
+       if ((1 << ios->vdd) & MMC_VDD_165_195)
+               seq_printf(s, "(1.65 - 1.95 V)\n");
+       else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
+                       && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
+               seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
+                               vdd_str[ios->vdd + 1]);
+       else
+               seq_printf(s, "(invalid)\n");
+
+       switch (ios->bus_mode) {
+       case MMC_BUSMODE_OPENDRAIN:
+               str = "open drain";
+               break;
+       case MMC_BUSMODE_PUSHPULL:
+               str = "push-pull";
+               break;
+       default:
+               str = "invalid";
+               break;
+       }
+       seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
+
+       switch (ios->chip_select) {
+       case MMC_CS_DONTCARE:
+               str = "don't care";
+               break;
+       case MMC_CS_HIGH:
+               str = "active high";
+               break;
+       case MMC_CS_LOW:
+               str = "active low";
+               break;
+       default:
+               str = "invalid";
+               break;
+       }
+       seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
+
+       switch (ios->power_mode) {
+       case MMC_POWER_OFF:
+               str = "off";
+               break;
+       case MMC_POWER_UP:
+               str = "up";
+               break;
+       case MMC_POWER_ON:
+               str = "on";
+               break;
+       default:
+               str = "invalid";
+               break;
+       }
+       seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
+       seq_printf(s, "bus width:\t%u (%u bits)\n",
+                       ios->bus_width, 1 << ios->bus_width);
+
+       switch (ios->timing) {
+       case MMC_TIMING_LEGACY:
+               str = "legacy";
+               break;
+       case MMC_TIMING_MMC_HS:
+               str = "mmc high-speed";
+               break;
+       case MMC_TIMING_SD_HS:
+               str = "sd high-speed";
+               break;
+       default:
+               str = "invalid";
+               break;
+       }
+       seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
+
+       return 0;
+}
+
+static int mmc_ios_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mmc_ios_show, inode->i_private);
+}
+
+static const struct file_operations mmc_ios_fops = {
+       .open           = mmc_ios_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+void mmc_add_host_debugfs(struct mmc_host *host)
+{
+       struct dentry *root;
+
+       root = debugfs_create_dir(mmc_hostname(host), NULL);
+       if (IS_ERR(root))
+               /* Don't complain -- debugfs just isn't enabled */
+               return;
+       if (!root)
+               /* Complain -- debugfs is enabled, but it failed to
+                * create the directory. */
+               goto err_root;
+
+       host->debugfs_root = root;
+
+       if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
+               goto err_ios;
+
+       return;
+
+err_ios:
+       debugfs_remove_recursive(root);
+       host->debugfs_root = NULL;
+err_root:
+       dev_err(&host->class_dev, "failed to initialize debugfs\n");
+}
+
+void mmc_remove_host_debugfs(struct mmc_host *host)
+{
+       debugfs_remove_recursive(host->debugfs_root);
+}
+
+static int mmc_dbg_card_status_get(void *data, u64 *val)
+{
+       struct mmc_card *card = data;
+       u32             status;
+       int             ret;
+
+       mmc_claim_host(card->host);
+
+       ret = mmc_send_status(data, &status);
+       if (!ret)
+               *val = status;
+
+       mmc_release_host(card->host);
+
+       return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
+               NULL, "%08llx\n");
+
+void mmc_add_card_debugfs(struct mmc_card *card)
+{
+       struct mmc_host *host = card->host;
+       struct dentry   *root;
+
+       if (!host->debugfs_root)
+               return;
+
+       root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
+       if (IS_ERR(root))
+               /* Don't complain -- debugfs just isn't enabled */
+               return;
+       if (!root)
+               /* Complain -- debugfs is enabled, but it failed to
+                * create the directory. */
+               goto err;
+
+       card->debugfs_root = root;
+
+       if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
+               goto err;
+
+       if (mmc_card_mmc(card) || mmc_card_sd(card))
+               if (!debugfs_create_file("status", S_IRUSR, root, card,
+                                       &mmc_dbg_card_status_fops))
+                       goto err;
+
+       return;
+
+err:
+       debugfs_remove_recursive(root);
+       card->debugfs_root = NULL;
+       dev_err(&card->dev, "failed to initialize debugfs\n");
+}
+
+void mmc_remove_card_debugfs(struct mmc_card *card)
+{
+       debugfs_remove_recursive(card->debugfs_root);
+}
index 1d795c5379b548f5869b83d7aabf05848a6d27b1..6da80fd4d974669223ac2caf54576f6521c52083 100644 (file)
@@ -127,6 +127,10 @@ int mmc_add_host(struct mmc_host *host)
        if (err)
                return err;
 
+#ifdef CONFIG_DEBUG_FS
+       mmc_add_host_debugfs(host);
+#endif
+
        mmc_start_host(host);
 
        return 0;
@@ -146,6 +150,10 @@ void mmc_remove_host(struct mmc_host *host)
 {
        mmc_stop_host(host);
 
+#ifdef CONFIG_DEBUG_FS
+       mmc_remove_host_debugfs(host);
+#endif
+
        device_del(&host->class_dev);
 
        led_trigger_unregister_simple(host->led);
index a9a5657706c6de2b22b3a92f0086eaa043df8b4b..26bd80e650317397545b4c62d0393f7d429c6e36 100644 (file)
@@ -82,6 +82,8 @@
 # define MCI_OVRE              (  1 <<  30)    /* RX Overrun Error */
 # define MCI_UNRE              (  1 <<  31)    /* TX Underrun Error */
 
+#define MCI_REGS_SIZE          0x100
+
 /* Register access macros */
 #define mci_readl(port,reg)                            \
        __raw_readl((port)->regs + MCI_##reg)
index cce873c5a149528f7d65fb3c5891a71bb5cdb502..992b4beb757c6593f4a790a0a749fcac5af8d165 100644 (file)
@@ -9,13 +9,18 @@
  */
 #include <linux/blkdev.h>
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
 
 #include <linux/mmc/host.h>
 
@@ -24,7 +29,6 @@
 #include <asm/unaligned.h>
 
 #include <asm/arch/board.h>
-#include <asm/arch/gpio.h>
 
 #include "atmel-mci-regs.h"
 
@@ -88,6 +92,188 @@ struct atmel_mci {
 #define atmci_clear_pending(host, event)                       \
        clear_bit(event, &host->pending_events)
 
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int atmci_req_show(struct seq_file *s, void *v)
+{
+       struct atmel_mci        *host = s->private;
+       struct mmc_request      *mrq = host->mrq;
+       struct mmc_command      *cmd;
+       struct mmc_command      *stop;
+       struct mmc_data         *data;
+
+       /* Make sure we get a consistent snapshot */
+       spin_lock_irq(&host->mmc->lock);
+
+       if (mrq) {
+               cmd = mrq->cmd;
+               data = mrq->data;
+               stop = mrq->stop;
+
+               if (cmd)
+                       seq_printf(s,
+                               "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+                               cmd->opcode, cmd->arg, cmd->flags,
+                               cmd->resp[0], cmd->resp[1], cmd->resp[2],
+                               cmd->resp[2], cmd->error);
+               if (data)
+                       seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+                               data->bytes_xfered, data->blocks,
+                               data->blksz, data->flags, data->error);
+               if (stop)
+                       seq_printf(s,
+                               "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+                               stop->opcode, stop->arg, stop->flags,
+                               stop->resp[0], stop->resp[1], stop->resp[2],
+                               stop->resp[2], stop->error);
+       }
+
+       spin_unlock_irq(&host->mmc->lock);
+
+       return 0;
+}
+
+static int atmci_req_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, atmci_req_show, inode->i_private);
+}
+
+static const struct file_operations atmci_req_fops = {
+       .owner          = THIS_MODULE,
+       .open           = atmci_req_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void atmci_show_status_reg(struct seq_file *s,
+               const char *regname, u32 value)
+{
+       static const char       *sr_bit[] = {
+               [0]     = "CMDRDY",
+               [1]     = "RXRDY",
+               [2]     = "TXRDY",
+               [3]     = "BLKE",
+               [4]     = "DTIP",
+               [5]     = "NOTBUSY",
+               [8]     = "SDIOIRQA",
+               [9]     = "SDIOIRQB",
+               [16]    = "RINDE",
+               [17]    = "RDIRE",
+               [18]    = "RCRCE",
+               [19]    = "RENDE",
+               [20]    = "RTOE",
+               [21]    = "DCRCE",
+               [22]    = "DTOE",
+               [30]    = "OVRE",
+               [31]    = "UNRE",
+       };
+       unsigned int            i;
+
+       seq_printf(s, "%s:\t0x%08x", regname, value);
+       for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
+               if (value & (1 << i)) {
+                       if (sr_bit[i])
+                               seq_printf(s, " %s", sr_bit[i]);
+                       else
+                               seq_puts(s, " UNKNOWN");
+               }
+       }
+       seq_putc(s, '\n');
+}
+
+static int atmci_regs_show(struct seq_file *s, void *v)
+{
+       struct atmel_mci        *host = s->private;
+       u32                     *buf;
+
+       buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* Grab a more or less consistent snapshot */
+       spin_lock_irq(&host->mmc->lock);
+       memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
+       spin_unlock_irq(&host->mmc->lock);
+
+       seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
+                       buf[MCI_MR / 4],
+                       buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
+                       buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
+                       buf[MCI_MR / 4] & 0xff);
+       seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
+       seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
+       seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
+       seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
+                       buf[MCI_BLKR / 4],
+                       buf[MCI_BLKR / 4] & 0xffff,
+                       (buf[MCI_BLKR / 4] >> 16) & 0xffff);
+
+       /* Don't read RSPR and RDR; it will consume the data there */
+
+       atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
+       atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
+
+       return 0;
+}
+
+static int atmci_regs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, atmci_regs_show, inode->i_private);
+}
+
+static const struct file_operations atmci_regs_fops = {
+       .owner          = THIS_MODULE,
+       .open           = atmci_regs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void atmci_init_debugfs(struct atmel_mci *host)
+{
+       struct mmc_host *mmc;
+       struct dentry   *root;
+       struct dentry   *node;
+       struct resource *res;
+
+       mmc = host->mmc;
+       root = mmc->debugfs_root;
+       if (!root)
+               return;
+
+       node = debugfs_create_file("regs", S_IRUSR, root, host,
+                       &atmci_regs_fops);
+       if (IS_ERR(node))
+               return;
+       if (!node)
+               goto err;
+
+       res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
+       node->d_inode->i_size = res->end - res->start + 1;
+
+       node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops);
+       if (!node)
+               goto err;
+
+       node = debugfs_create_x32("pending_events", S_IRUSR, root,
+                                    (u32 *)&host->pending_events);
+       if (!node)
+               goto err;
+
+       node = debugfs_create_x32("completed_events", S_IRUSR, root,
+                                    (u32 *)&host->completed_events);
+       if (!node)
+               goto err;
+
+       return;
+
+err:
+       dev_err(&host->pdev->dev,
+               "failed to initialize debugfs for controller\n");
+}
 
 static void atmci_enable(struct atmel_mci *host)
 {
@@ -388,7 +574,7 @@ static int atmci_get_ro(struct mmc_host *mmc)
        int                     read_only = 0;
        struct atmel_mci        *host = mmc_priv(mmc);
 
-       if (host->wp_pin >= 0) {
+       if (gpio_is_valid(host->wp_pin)) {
                read_only = gpio_get_value(host->wp_pin);
                dev_dbg(&mmc->class_dev, "card is %s\n",
                                read_only ? "read-only" : "read-write");
@@ -450,7 +636,7 @@ static void atmci_detect_change(unsigned long data)
         * been freed.
         */
        smp_rmb();
-       if (host->detect_pin < 0)
+       if (!gpio_is_valid(host->detect_pin))
                return;
 
        enable_irq(gpio_to_irq(host->detect_pin));
@@ -865,7 +1051,7 @@ static int __init atmci_probe(struct platform_device *pdev)
 
        /* Assume card is present if we don't have a detect pin */
        host->present = 1;
-       if (host->detect_pin >= 0) {
+       if (gpio_is_valid(host->detect_pin)) {
                if (gpio_request(host->detect_pin, "mmc_detect")) {
                        dev_dbg(&mmc->class_dev, "no detect pin available\n");
                        host->detect_pin = -1;
@@ -873,7 +1059,7 @@ static int __init atmci_probe(struct platform_device *pdev)
                        host->present = !gpio_get_value(host->detect_pin);
                }
        }
-       if (host->wp_pin >= 0) {
+       if (gpio_is_valid(host->wp_pin)) {
                if (gpio_request(host->wp_pin, "mmc_wp")) {
                        dev_dbg(&mmc->class_dev, "no WP pin available\n");
                        host->wp_pin = -1;
@@ -884,7 +1070,7 @@ static int __init atmci_probe(struct platform_device *pdev)
 
        mmc_add_host(mmc);
 
-       if (host->detect_pin >= 0) {
+       if (gpio_is_valid(host->detect_pin)) {
                setup_timer(&host->detect_timer, atmci_detect_change,
                                (unsigned long)host);
 
@@ -905,6 +1091,8 @@ static int __init atmci_probe(struct platform_device *pdev)
                        "Atmel MCI controller at 0x%08lx irq %d\n",
                        host->mapbase, irq);
 
+       atmci_init_debugfs(host);
+
        return 0;
 
 err_request_irq:
@@ -923,7 +1111,9 @@ static int __exit atmci_remove(struct platform_device *pdev)
        platform_set_drvdata(pdev, NULL);
 
        if (host) {
-               if (host->detect_pin >= 0) {
+               /* Debugfs stuff is cleaned up by mmc core */
+
+               if (gpio_is_valid(host->detect_pin)) {
                        int pin = host->detect_pin;
 
                        /* Make sure the timer doesn't enable the interrupt */
@@ -943,7 +1133,7 @@ static int __exit atmci_remove(struct platform_device *pdev)
                mci_readl(host, SR);
                clk_disable(host->mck);
 
-               if (host->wp_pin >= 0)
+               if (gpio_is_valid(host->wp_pin))
                        gpio_free(host->wp_pin);
 
                free_irq(platform_get_irq(pdev, 0), host->mmc);
index 5e880c0f13495105105efee78d1eadcebdfa4a29..f61406da65d2c953e31896395209ce76428990cc 100644 (file)
  *
  */
 
-#ifdef CONFIG_MMC_DEBUG
-#define DEBUG
-#else
-#undef  DEBUG
-#endif
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
@@ -907,31 +901,12 @@ static const struct mmc_host_ops imxmci_ops = {
        .get_ro         = imxmci_get_ro,
 };
 
-static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
-{
-       int i;
-
-       for (i = 0; i < dev->num_resources; i++)
-               if (dev->resource[i].flags == mask && nr-- == 0)
-                       return &dev->resource[i];
-       return NULL;
-}
-
-static int platform_device_irq(struct platform_device *dev, int nr)
-{
-       int i;
-
-       for (i = 0; i < dev->num_resources; i++)
-               if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)
-                       return dev->resource[i].start;
-       return NO_IRQ;
-}
-
 static void imxmci_check_status(unsigned long data)
 {
        struct imxmci_host *host = (struct imxmci_host *)data;
 
-       if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) {
+       if (host->pdata && host->pdata->card_present &&
+           host->pdata->card_present(mmc_dev(host->mmc)) != host->present) {
                host->present ^= 1;
                dev_info(mmc_dev(host->mmc), "card %s\n",
                      host->present ? "inserted" : "removed");
@@ -962,13 +937,12 @@ static int imxmci_probe(struct platform_device *pdev)
 
        printk(KERN_INFO "i.MX mmc driver\n");
 
-       r = platform_device_resource(pdev, IORESOURCE_MEM, 0);
-       irq = platform_device_irq(pdev, 0);
-       if (!r || irq == NO_IRQ)
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       if (!r || irq < 0)
                return -ENXIO;
 
-       r = request_mem_region(r->start, 0x100, "IMXMCI");
-       if (!r)
+       if (!request_mem_region(r->start, 0x100, pdev->name))
                return -EBUSY;
 
        mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
@@ -995,6 +969,8 @@ static int imxmci_probe(struct platform_device *pdev)
        host->mmc = mmc;
        host->dma_allocated = 0;
        host->pdata = pdev->dev.platform_data;
+       if (!host->pdata)
+               dev_warn(&pdev->dev, "No platform data provided!\n");
 
        spin_lock_init(&host->lock);
        host->res = r;
@@ -1047,7 +1023,11 @@ static int imxmci_probe(struct platform_device *pdev)
        if (ret)
                goto out;
 
-       host->present = host->pdata->card_present(mmc_dev(mmc));
+       if (host->pdata && host->pdata->card_present)
+               host->present = host->pdata->card_present(mmc_dev(mmc));
+       else    /* if there is no way to detect assume that card is present */
+               host->present = 1;
+
        init_timer(&host->timer);
        host->timer.data = (unsigned long)host;
        host->timer.function = imxmci_check_status;
@@ -1073,7 +1053,7 @@ out:
        }
        if (mmc)
                mmc_free_host(mmc);
-       release_resource(r);
+       release_mem_region(r->start, 0x100);
        return ret;
 }
 
@@ -1102,7 +1082,7 @@ static int imxmci_remove(struct platform_device *pdev)
                clk_disable(host->clk);
                clk_put(host->clk);
 
-               release_resource(host->res);
+               release_mem_region(host->res->start, 0x100);
 
                mmc_free_host(mmc);
        }
index 41cc63360e43d357db2d5d42a06cf55b0abd47b3..7503b81374e0783690df20afbc88da0b02a75e85 100644 (file)
@@ -1076,6 +1076,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                 */
                if (canpower && ios->power_mode == MMC_POWER_OFF) {
                        int mres;
+                       u8 nullbyte = 0;
 
                        host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
                        mres = spi_setup(host->spi);
@@ -1083,7 +1084,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                                dev_dbg(&host->spi->dev,
                                        "switch to SPI mode 0 failed\n");
 
-                       if (spi_w8r8(host->spi, 0x00) < 0)
+                       if (spi_write(host->spi, &nullbyte, 1) < 0)
                                dev_dbg(&host->spi->dev,
                                        "put spi signals to low failed\n");
 
index eed06d068fd12f36f64c11a37ea0e3ea7c611d32..14f11f8b9e5fd556fcd71dbe4d6178634c851bcc 100644 (file)
@@ -1,5 +1,3 @@
-# $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $
-
 menuconfig MTD
        tristate "Memory Technology Device (MTD) support"
        depends on HAS_IOMEM
index 52d51eb91c16f7c1a7a5ec7c3cbbcd5936cb5a39..d072ca5be689a5ee8f1a426e3d1103762fb75283 100644 (file)
@@ -21,8 +21,6 @@
    This is access code for flashes using ARM's flash partitioning
    standards.
 
-   $Id: afs.c,v 1.15 2005/11/07 11:14:19 gleixner Exp $
-
 ======================================================================*/
 
 #include <linux/module.h>
index fcd1aeccdf9393d0780da7ff83c94d8f32e1af62..5f1b472137a024b96fcff3ea3f9bcd654c7389f8 100644 (file)
@@ -4,8 +4,6 @@
  *
  * (C) 2000 Red Hat. GPL'd
  *
- * $Id: cfi_cmdset_0001.c,v 1.186 2005/11/23 22:07:52 nico Exp $
- *
  *
  * 10/10/2000  Nicolas Pitre <nico@cam.org>
  *     - completely revamped method functions so they are aware and
@@ -50,6 +48,8 @@
 #define I82802AC       0x00ac
 #define MANUFACTURER_ST         0x0020
 #define M50LPW080       0x002F
+#define M50FLW080A     0x0080
+#define M50FLW080B     0x0081
 #define AT49BV640D     0x02de
 
 static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
@@ -204,7 +204,7 @@ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
 {
        struct map_info *map = mtd->priv;
        struct cfi_private *cfi = map->fldrv_priv;
-       struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+       struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 
        printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
                            "erase on write disabled.\n");
@@ -301,6 +301,8 @@ static struct cfi_fixup jedec_fixup_table[] = {
        { MANUFACTURER_INTEL, I82802AB,   fixup_use_fwh_lock, NULL, },
        { MANUFACTURER_INTEL, I82802AC,   fixup_use_fwh_lock, NULL, },
        { MANUFACTURER_ST,    M50LPW080,  fixup_use_fwh_lock, NULL, },
+       { MANUFACTURER_ST,    M50FLW080A, fixup_use_fwh_lock, NULL, },
+       { MANUFACTURER_ST,    M50FLW080B, fixup_use_fwh_lock, NULL, },
        { 0, 0, NULL, NULL }
 };
 static struct cfi_fixup fixup_table[] = {
@@ -1147,7 +1149,7 @@ static int inval_cache_and_wait_for_operation(
        struct cfi_private *cfi = map->fldrv_priv;
        map_word status, status_OK = CMD(0x80);
        int chip_state = chip->state;
-       unsigned int timeo, sleep_time;
+       unsigned int timeo, sleep_time, reset_timeo;
 
        spin_unlock(chip->mutex);
        if (inval_len)
@@ -1158,6 +1160,7 @@ static int inval_cache_and_wait_for_operation(
        timeo = chip_op_time * 8;
        if (!timeo)
                timeo = 500000;
+       reset_timeo = timeo;
        sleep_time = chip_op_time / 2;
 
        for (;;) {
@@ -1199,6 +1202,12 @@ static int inval_cache_and_wait_for_operation(
                        remove_wait_queue(&chip->wq, &wait);
                        spin_lock(chip->mutex);
                }
+               if (chip->erase_suspended || chip->write_suspended)  {
+                       /* Suspend has occured while sleep: reset timeout */
+                       timeo = reset_timeo;
+                       chip->erase_suspended = 0;
+                       chip->write_suspended = 0;
+               }
        }
 
        /* Done and happy. */
index f7fcc6389533c3221dac34ab72878605d4f94e0b..a972cc6be436367f797a90f5099aca14a7e8a7a9 100644 (file)
@@ -16,9 +16,6 @@
  * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
  *
  * This code is GPL
- *
- * $Id: cfi_cmdset_0002.c,v 1.122 2005/11/07 11:14:22 gleixner Exp $
- *
  */
 
 #include <linux/module.h>
index 1b720cc571f315dfbb1ec2a7faed160835bbb00b..d4714dd9f7ab0d1f6626f87b584899844bc056e0 100644 (file)
@@ -4,8 +4,6 @@
  *
  * (C) 2000 Red Hat. GPL'd
  *
- * $Id: cfi_cmdset_0020.c,v 1.22 2005/11/07 11:14:22 gleixner Exp $
- *
  * 10/10/2000  Nicolas Pitre <nico@cam.org>
  *     - completely revamped method functions so they are aware and
  *       independent of the flash geometry (buswidth, interleave, etc.)
index a4463a91ce31877881beb14427bf8bc6b6f5fd5a..c418e92e1d92b79286f46a173bd1f93ff3591939 100644 (file)
@@ -1,7 +1,6 @@
 /*
    Common Flash Interface probe code.
    (C) 2000 Red Hat. GPL'd.
-   $Id: cfi_probe.c,v 1.86 2005/11/29 14:48:31 gleixner Exp $
 */
 
 #include <linux/module.h>
index 72e0022a47bf50e360686068b66e6d52dbe895a6..0ee457018016083743ca772a38d77325aa6911ae 100644 (file)
@@ -6,9 +6,6 @@
  * Copyright (C) 2003 STMicroelectronics Limited
  *
  * This code is covered by the GPL.
- *
- * $Id: cfi_util.c,v 1.10 2005/11/07 11:14:23 gleixner Exp $
- *
  */
 
 #include <linux/module.h>
index 2174c97549f0e868290799970d71697560bdf4bb..c857609682276468baac7079d2de839c60d7a84d 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: chipreg.c,v 1.17 2004/11/16 18:29:00 dwmw2 Exp $
- *
  * Registration for chip drivers
  *
  */
index d338b8c92780255d9d71a62f9484010a0cac7b64..f061885b2812dc5d04bba96f488aa6fa18a9ae49 100644 (file)
@@ -2,7 +2,6 @@
  * Routines common to all CFI-type probes.
  * (C) 2001-2003 Red Hat, Inc.
  * GPL'd
- * $Id: gen_probe.c,v 1.24 2005/11/07 11:14:23 gleixner Exp $
  */
 
 #include <linux/kernel.h>
@@ -71,8 +70,8 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
           interleave and device type, etc. */
        if (!genprobe_new_chip(map, cp, &cfi)) {
                /* The probe didn't like it */
-               printk(KERN_DEBUG "%s: Found no %s device at location zero\n",
-                      cp->name, map->name);
+               pr_debug("%s: Found no %s device at location zero\n",
+                        cp->name, map->name);
                return NULL;
        }
 
index aa07575eb28834adfd09683a95ed87e1a5e6ccc6..dbba5abf0db86d809bc325cbe653f5120ffa183f 100644 (file)
@@ -1,7 +1,6 @@
 /*
    Common Flash Interface probe code.
    (C) 2000 Red Hat. GPL'd.
-   $Id: jedec_probe.c,v 1.66 2005/11/07 11:14:23 gleixner Exp $
    See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
    for the standard this probe goes back to.
 
@@ -26,6 +25,7 @@
 /* Manufacturers */
 #define MANUFACTURER_AMD       0x0001
 #define MANUFACTURER_ATMEL     0x001f
+#define MANUFACTURER_EON       0x001c
 #define MANUFACTURER_FUJITSU   0x0004
 #define MANUFACTURER_HYUNDAI   0x00AD
 #define MANUFACTURER_INTEL     0x0089
@@ -37,6 +37,7 @@
 #define MANUFACTURER_ST                0x0020
 #define MANUFACTURER_TOSHIBA   0x0098
 #define MANUFACTURER_WINBOND   0x00da
+#define CONTINUATION_CODE      0x007f
 
 
 /* AMD */
@@ -58,6 +59,8 @@
 #define AM29LV040B     0x004F
 #define AM29F032B      0x0041
 #define AM29F002T      0x00B0
+#define AM29SL800DB    0x226B
+#define AM29SL800DT    0x22EA
 
 /* Atmel */
 #define AT49BV512      0x0003
 #define AT49BV32X      0x00C8
 #define AT49BV32XT     0x00C9
 
+/* Eon */
+#define EN29SL800BB    0x226B
+#define EN29SL800BT    0x22EA
+
 /* Fujitsu */
 #define MBM29F040C     0x00A4
 #define MBM29F800BA    0x2258
 #define M50FW080       0x002D
 #define M50FW016       0x002E
 #define M50LPW080       0x002F
+#define M50FLW080A     0x0080
+#define M50FLW080B     0x0081
 
 /* SST */
 #define SST29EE020     0x0010
@@ -191,6 +200,7 @@ enum uaddr {
        MTD_UADDR_0x0555_0x0AAA,
        MTD_UADDR_0x5555_0x2AAA,
        MTD_UADDR_0x0AAA_0x0555,
+       MTD_UADDR_0xAAAA_0x5555,
        MTD_UADDR_DONT_CARE,            /* Requires an arbitrary address */
        MTD_UADDR_UNNECESSARY,          /* Does not require any address */
 };
@@ -238,6 +248,11 @@ static const struct unlock_addr  unlock_addrs[] = {
                .addr2 = 0x0555
        },
 
+       [MTD_UADDR_0xAAAA_0x5555] = {
+               .addr1 = 0xaaaa,
+               .addr2 = 0x5555
+       },
+
        [MTD_UADDR_DONT_CARE] = {
                .addr1 = 0x0000,      /* Doesn't matter which address */
                .addr2 = 0x0000       /* is used - must be last entry */
@@ -521,6 +536,36 @@ static const struct amd_flash_info jedec_table[] = {
                        ERASEINFO(0x02000,2),
                        ERASEINFO(0x04000,1),
                }
+       }, {
+               .mfr_id         = MANUFACTURER_AMD,
+               .dev_id         = AM29SL800DT,
+               .name           = "AMD AM29SL800DT",
+               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_0x0AAA_0x0555,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x10000,15),
+                       ERASEINFO(0x08000,1),
+                       ERASEINFO(0x02000,2),
+                       ERASEINFO(0x04000,1),
+               }
+       }, {
+               .mfr_id         = MANUFACTURER_AMD,
+               .dev_id         = AM29SL800DB,
+               .name           = "AMD AM29SL800DB",
+               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_0x0AAA_0x0555,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x04000,1),
+                       ERASEINFO(0x02000,2),
+                       ERASEINFO(0x08000,1),
+                       ERASEINFO(0x10000,15),
+               }
        }, {
                .mfr_id         = MANUFACTURER_ATMEL,
                .dev_id         = AT49BV512,
@@ -598,6 +643,36 @@ static const struct amd_flash_info jedec_table[] = {
                        ERASEINFO(0x10000,63),
                        ERASEINFO(0x02000,8)
                }
+       }, {
+               .mfr_id         = MANUFACTURER_EON,
+               .dev_id         = EN29SL800BT,
+               .name           = "Eon EN29SL800BT",
+               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_0x0AAA_0x0555,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x10000,15),
+                       ERASEINFO(0x08000,1),
+                       ERASEINFO(0x02000,2),
+                       ERASEINFO(0x04000,1),
+               }
+       }, {
+               .mfr_id         = MANUFACTURER_EON,
+               .dev_id         = EN29SL800BB,
+               .name           = "Eon EN29SL800BB",
+               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_0x0AAA_0x0555,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x04000,1),
+                       ERASEINFO(0x02000,2),
+                       ERASEINFO(0x08000,1),
+                       ERASEINFO(0x10000,15),
+               }
        }, {
                .mfr_id         = MANUFACTURER_FUJITSU,
                .dev_id         = MBM29F040C,
@@ -1392,8 +1467,8 @@ static const struct amd_flash_info jedec_table[] = {
                .mfr_id         = MANUFACTURER_SST,     /* should be CFI */
                .dev_id         = SST39LF160,
                .name           = "SST 39LF160",
-               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
-               .uaddr          = MTD_UADDR_0x5555_0x2AAA,      /* ???? */
+               .devtypes       = CFI_DEVICETYPE_X16,
+               .uaddr          = MTD_UADDR_0xAAAA_0x5555,
                .dev_size       = SIZE_2MiB,
                .cmd_set        = P_ID_AMD_STD,
                .nr_regions     = 2,
@@ -1405,8 +1480,8 @@ static const struct amd_flash_info jedec_table[] = {
                .mfr_id         = MANUFACTURER_SST,     /* should be CFI */
                .dev_id         = SST39VF1601,
                .name           = "SST 39VF1601",
-               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
-               .uaddr          = MTD_UADDR_0x5555_0x2AAA,      /* ???? */
+               .devtypes       = CFI_DEVICETYPE_X16,
+               .uaddr          = MTD_UADDR_0xAAAA_0x5555,
                .dev_size       = SIZE_2MiB,
                .cmd_set        = P_ID_AMD_STD,
                .nr_regions     = 2,
@@ -1590,6 +1665,36 @@ static const struct amd_flash_info jedec_table[] = {
                .nr_regions     = 1,
                .regions        = {
                        ERASEINFO(0x10000,16),
+               },
+       }, {
+               .mfr_id         = MANUFACTURER_ST,
+               .dev_id         = M50FLW080A,
+               .name           = "ST M50FLW080A",
+               .devtypes       = CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_UNNECESSARY,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_INTEL_EXT,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x1000,16),
+                       ERASEINFO(0x10000,13),
+                       ERASEINFO(0x1000,16),
+                       ERASEINFO(0x1000,16),
+               }
+       }, {
+               .mfr_id         = MANUFACTURER_ST,
+               .dev_id         = M50FLW080B,
+               .name           = "ST M50FLW080B",
+               .devtypes       = CFI_DEVICETYPE_X8,
+               .uaddr          = MTD_UADDR_UNNECESSARY,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_INTEL_EXT,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x1000,16),
+                       ERASEINFO(0x1000,16),
+                       ERASEINFO(0x10000,13),
+                       ERASEINFO(0x1000,16),
                }
        }, {
                .mfr_id         = MANUFACTURER_TOSHIBA,
@@ -1696,9 +1801,21 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base,
 {
        map_word result;
        unsigned long mask;
-       u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type);
-       mask = (1 << (cfi->device_type * 8)) -1;
-       result = map_read(map, base + ofs);
+       int bank = 0;
+
+       /* According to JEDEC "Standard Manufacturer's Identification Code"
+        * (http://www.jedec.org/download/search/jep106W.pdf)
+        * several first banks can contain 0x7f instead of actual ID
+        */
+       do {
+               uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8),
+                                                 cfi_interleave(cfi),
+                                                 cfi->device_type);
+               mask = (1 << (cfi->device_type * 8)) - 1;
+               result = map_read(map, base + ofs);
+               bank++;
+       } while ((result.x[0] & mask) == CONTINUATION_CODE);
+
        return result.x[0] & mask;
 }
 
index fc478c0f93f55bdcce74f7f82393a5c36a39b605..494d30d0631a30b255b39c3996d827844aae948e 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * Common code to handle absent "placeholder" devices
  * Copyright 2001 Resilience Corporation <ebrower@resilience.com>
- * $Id: map_absent.c,v 1.6 2005/11/07 11:14:23 gleixner Exp $
  *
  * This map driver is used to allocate "placeholder" MTD
  * devices on systems that have socketed/removable media.
index 5cb6d5263661a63f862d458a6b3a2dfdc31686ae..072dd8abf33a726620bd0f9299c3005632ce4ee4 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * Common code to handle map devices which are simple RAM
  * (C) 2000 Red Hat. GPL'd.
- * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $
  */
 
 #include <linux/module.h>
index cb27f855074c279a65ce23a53cc2615ff9417d55..821d0ed6bae3970453a38d2e40bb0bb802930b1d 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * Common code to handle map devices which are simple ROM
  * (C) 2000 Red Hat. GPL'd.
- * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $
  */
 
 #include <linux/module.h>
index e472a0e9de9d913228cfa47bc8351b9f360c9e12..71bc07f149b7910f964a5bc9b4c678a149cfbdf9 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: cmdlinepart.c,v 1.19 2005/11/07 11:14:19 gleixner Exp $
- *
  * Read flash partition table from command line
  *
  * Copyright 2002 SYSGO Real-Time Solutions GmbH
@@ -308,7 +306,7 @@ static int parse_cmdline_partitions(struct mtd_info *master,
        unsigned long offset;
        int i;
        struct cmdline_mtd_partition *part;
-       char *mtd_id = master->name;
+       const char *mtd_id = master->name;
 
        /* parse command line */
        if (!cmdline_parsed)
index 35ed1103dbb22096c183eb0a903072ec8ef73e1a..9c613f06623cd4d1191eafb7e6d4adfb75736037 100644 (file)
@@ -1,5 +1,4 @@
 # drivers/mtd/maps/Kconfig
-# $Id: Kconfig,v 1.18 2005/11/07 11:14:24 gleixner Exp $
 
 menu "Self-contained MTD device drivers"
        depends on MTD!=n
index 0f788d5c4bf8307b9b609891338bc4469b755534..0993d5cf3923f3a4895a6579aa18759bb76a3c72 100644 (file)
@@ -1,7 +1,6 @@
 #
 # linux/drivers/devices/Makefile
 #
-# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $
 
 obj-$(CONFIG_MTD_DOC2000)      += doc2000.o
 obj-$(CONFIG_MTD_DOC2001)      += doc2001.o
index 7b72a1b36115f4b1bf4c581c6d67c1e6f6454566..91fbba767635baba3a3bc7755fd8b6c99b230975 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: block2mtd.c,v 1.30 2005/11/29 14:48:32 gleixner Exp $
- *
  * block2mtd.c - create an mtd from a block device
  *
  * Copyright (C) 2001,2002     Simon Evans <spse@secret.org.uk>
@@ -20,9 +18,6 @@
 #include <linux/mutex.h>
 #include <linux/mount.h>
 
-#define VERSION "$Revision: 1.30 $"
-
-
 #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args)
 #define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args)
 
@@ -453,7 +448,6 @@ MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\"");
 static int __init block2mtd_init(void)
 {
        int ret = 0;
-       INFO("version " VERSION);
 
 #ifndef MODULE
        if (strlen(block2mtd_paramline))
index 846989f292e3f01a1992dd73f999583263b9ec7f..50de839c77a9edf410747ba963ae4c3c93d49156 100644 (file)
@@ -3,8 +3,6 @@
  * Linux driver for Disk-On-Chip 2000 and Millennium
  * (c) 1999 Machine Vision Holdings, Inc.
  * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
- *
- * $Id: doc2000.c,v 1.67 2005/11/07 11:14:24 gleixner Exp $
  */
 
 #include <linux/kernel.h>
index 6413efc045e0c98e1e657229fdbdd55e67671b1d..e32c568c11450e1c8041f4e28604ed1b72654d00 100644 (file)
@@ -3,8 +3,6 @@
  * Linux driver for Disk-On-Chip Millennium
  * (c) 1999 Machine Vision Holdings, Inc.
  * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
- *
- * $Id: doc2001.c,v 1.49 2005/11/07 11:14:24 gleixner Exp $
  */
 
 #include <linux/kernel.h>
index 83be3461658f22527a40ab1e57ec0efa503c8252..d853f891b586d65e82254d44716f3cd78de47005 100644 (file)
@@ -6,8 +6,6 @@
  * (c) 1999 Machine Vision Holdings, Inc.
  * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
  *
- * $Id: doc2001plus.c,v 1.14 2005/11/07 11:14:24 gleixner Exp $
- *
  * Released under GPL
  */
 
index fd8a8daba3a82d47915a705b703bfd0b8f21fa75..874e51b110a2d4cfbdadef073e146aab68040bdc 100644 (file)
@@ -7,8 +7,6 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: docecc.c,v 1.7 2005/11/07 11:14:25 gleixner Exp $
- *
  * 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
index d8cc94ec4e504495421c5a3a558c02cdf8fff024..6e62922942b18efd1a63fc44dfbad3d6ae087612 100644 (file)
@@ -4,9 +4,6 @@
 /* (C) 1999 Machine Vision Holdings, Inc.                      */
 /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>         */
 
-/* $Id: docprobe.c,v 1.46 2005/11/07 11:14:25 gleixner Exp $   */
-
-
 
 /* DOC_PASSIVE_PROBE:
    In order to ensure that the BIOS checksum is correct at boot time, and
@@ -79,8 +76,6 @@ static unsigned long __initdata doc_locations[] = {
        0xe0000, 0xe2000, 0xe4000, 0xe6000,
        0xe8000, 0xea000, 0xec000, 0xee000,
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
-#elif defined(__PPC__)
-       0xe4000000,
 #else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
index 1d324e5c412d547834f05eab936e181f92085c67..f4bda4cee4958869d12291bfa93ad10939eedeb4 100644 (file)
@@ -2,8 +2,6 @@
 /*
  * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
  *
- * $Id: lart.c,v 1.9 2005/11/07 11:14:25 gleixner Exp $
- *
  * Author: Abraham vd Merwe <abraham@2d3d.co.za>
  *
  * Copyright (c) 2001, 2d3D, Inc.
index b402269301f6f64e4d22108ffc148f8220108b03..b35c3333e210b61878493d3fd052e1228b38d6db 100644 (file)
@@ -33,6 +33,7 @@
 /* Flash opcodes. */
 #define        OPCODE_WREN             0x06    /* Write enable */
 #define        OPCODE_RDSR             0x05    /* Read status register */
+#define        OPCODE_WRSR             0x01    /* Write status register 1 byte */
 #define        OPCODE_NORM_READ        0x03    /* Read data bytes (low frequency) */
 #define        OPCODE_FAST_READ        0x0b    /* Read data bytes (high frequency) */
 #define        OPCODE_PP               0x02    /* Page program (up to 256 bytes) */
@@ -112,6 +113,17 @@ static int read_sr(struct m25p *flash)
        return val;
 }
 
+/*
+ * Write status register 1 byte
+ * Returns negative if error occurred.
+ */
+static int write_sr(struct m25p *flash, u8 val)
+{
+       flash->command[0] = OPCODE_WRSR;
+       flash->command[1] = val;
+
+       return spi_write(flash->spi, flash->command, 2);
+}
 
 /*
  * Set write enable latch with Write Enable command.
@@ -589,6 +601,16 @@ static int __devinit m25p_probe(struct spi_device *spi)
        mutex_init(&flash->lock);
        dev_set_drvdata(&spi->dev, flash);
 
+       /*
+        * Atmel serial flash tend to power up
+        * with the software protection bits set
+        */
+
+       if (info->jedec_id >> 16 == 0x1f) {
+               write_enable(flash);
+               write_sr(flash, 0);
+       }
+
        if (data && data->name)
                flash->mtd.name = data->name;
        else
index 9cff119a202483cd9cea74438c190b5a4fbb7171..6a9a24a80a6d206a573fea039a8a0274b389db4d 100644 (file)
@@ -5,8 +5,6 @@
  *     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.
- *
- *     $Id: ms02-nv.c,v 1.11 2005/11/14 13:41:47 macro Exp $
  */
 
 #include <linux/init.h>
index 8a6eef7cfee31d7bb0a8dc442b1309041f148468..04deafd3a7714c8ab7b0826eedf7db983d3b9e72 100644 (file)
@@ -9,8 +9,6 @@
  *     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.
- *
- *     $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $
  */
 
 #include <linux/ioport.h>
index b35e4813a3a5e146f6725fb13d6d3be0f588bfe6..54e36bfc2c3b39caed333e3bbd5209ea704ba08c 100644 (file)
@@ -82,7 +82,7 @@
 
 
 struct dataflash {
-       u8                      command[4];
+       uint8_t                 command[4];
        char                    name[24];
 
        unsigned                partitioned:1;
@@ -150,7 +150,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct spi_transfer     x = { .tx_dma = 0, };
        struct spi_message      msg;
        unsigned                blocksize = priv->page_size << 3;
-       u8                      *command;
+       uint8_t                 *command;
 
        DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n",
                        spi->dev.bus_id,
@@ -182,8 +182,8 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
                pageaddr = pageaddr << priv->page_offset;
 
                command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
-               command[1] = (u8)(pageaddr >> 16);
-               command[2] = (u8)(pageaddr >> 8);
+               command[1] = (uint8_t)(pageaddr >> 16);
+               command[2] = (uint8_t)(pageaddr >> 8);
                command[3] = 0;
 
                DEBUG(MTD_DEBUG_LEVEL3, "ERASE %s: (%x) %x %x %x [%i]\n",
@@ -234,7 +234,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
        struct spi_transfer     x[2] = { { .tx_dma = 0, }, };
        struct spi_message      msg;
        unsigned int            addr;
-       u8                      *command;
+       uint8_t                 *command;
        int                     status;
 
        DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n",
@@ -274,9 +274,9 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
         * fewer "don't care" bytes.  Both buffers stay unchanged.
         */
        command[0] = OP_READ_CONTINUOUS;
-       command[1] = (u8)(addr >> 16);
-       command[2] = (u8)(addr >> 8);
-       command[3] = (u8)(addr >> 0);
+       command[1] = (uint8_t)(addr >> 16);
+       command[2] = (uint8_t)(addr >> 8);
+       command[3] = (uint8_t)(addr >> 0);
        /* plus 4 "don't care" bytes */
 
        status = spi_sync(priv->spi, &msg);
@@ -311,7 +311,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
        size_t                  remaining = len;
        u_char                  *writebuf = (u_char *) buf;
        int                     status = -EINVAL;
-       u8                      *command;
+       uint8_t                 *command;
 
        DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n",
                spi->dev.bus_id, (unsigned)to, (unsigned)(to + len));
@@ -487,7 +487,9 @@ add_dataflash(struct spi_device *spi, char *name,
        device->write = dataflash_write;
        device->priv = priv;
 
-       dev_info(&spi->dev, "%s (%d KBytes)\n", name, device->size/1024);
+       dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes, "
+               "erasesize %d bytes\n", name, device->size/1024,
+                pagesize, pagesize * 8);       /* 8 pages = 1 block */
        dev_set_drvdata(&spi->dev, priv);
 
        if (mtd_has_partitions()) {
@@ -521,7 +523,7 @@ add_dataflash(struct spi_device *spi, char *name,
  *
  *   Device      Density         ID code          #Pages PageSize  Offset
  *   AT45DB011B  1Mbit   (128K)  xx0011xx (0x0c)    512    264      9
- *   AT45DB021B  2Mbit   (256K)  xx0101xx (0x14)   1025    264      9
+ *   AT45DB021B  2Mbit   (256K)  xx0101xx (0x14)   1024    264      9
  *   AT45DB041B  4Mbit   (512K)  xx0111xx (0x1c)   2048    264      9
  *   AT45DB081B  8Mbit   (1M)    xx1001xx (0x24)   4096    264      9
  *   AT45DB0161B 16Mbit  (2M)    xx1011xx (0x2c)   4096    528     10
@@ -529,9 +531,114 @@ add_dataflash(struct spi_device *spi, char *name,
  *   AT45DB0642  64Mbit  (8M)    xx111xxx (0x3c)   8192   1056     11
  *   AT45DB1282  128Mbit (16M)   xx0100xx (0x10)  16384   1056     11
  */
+
+struct flash_info {
+       char            *name;
+
+       /* JEDEC id zero means "no ID" (most older chips); otherwise it has
+        * a high byte of zero plus three data bytes: the manufacturer id,
+        * then a two byte device id.
+        */
+       uint32_t        jedec_id;
+
+       /* The size listed here is what works with OPCODE_SE, which isn't
+        * necessarily called a "sector" by the vendor.
+        */
+       unsigned        nr_pages;
+       uint16_t        pagesize;
+       uint16_t        pageoffset;
+
+       uint16_t        flags;
+#define        SUP_POW2PS      0x02
+#define        IS_POW2PS       0x01
+};
+
+static struct flash_info __devinitdata dataflash_data [] = {
+
+       { "at45db011d",  0x1f2200, 512, 264, 9, SUP_POW2PS},
+       { "at45db011d",  0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+       { "at45db021d",  0x1f2300, 1024, 264, 9, SUP_POW2PS},
+       { "at45db021d",  0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+       { "at45db041d",  0x1f2400, 2048, 264, 9, SUP_POW2PS},
+       { "at45db041d",  0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+       { "at45db081d",  0x1f2500, 4096, 264, 9, SUP_POW2PS},
+       { "at45db081d",  0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+       { "at45db161d",  0x1f2600, 4096, 528, 10, SUP_POW2PS},
+       { "at45db161d",  0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS},
+
+       { "at45db321c",  0x1f2700, 8192, 528, 10, },
+
+       { "at45db321d",  0x1f2701, 8192, 528, 10, SUP_POW2PS},
+       { "at45db321d",  0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS},
+
+       { "at45db641d",  0x1f2800, 8192, 1056, 11, SUP_POW2PS},
+       { "at45db641d",  0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
+};
+
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+       int                     tmp;
+       uint8_t                 code = OP_READ_ID;
+       uint8_t                 id[3];
+       uint32_t                jedec;
+       struct flash_info       *info;
+       int status;
+
+
+       /* JEDEC also defines an optional "extended device information"
+        * string for after vendor-specific data, after the three bytes
+        * we use here.  Supporting some chips might require using it.
+        */
+       tmp = spi_write_then_read(spi, &code, 1, id, 3);
+       if (tmp < 0) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+                       spi->dev.bus_id, tmp);
+               return NULL;
+       }
+       jedec = id[0];
+       jedec = jedec << 8;
+       jedec |= id[1];
+       jedec = jedec << 8;
+       jedec |= id[2];
+
+       for (tmp = 0, info = dataflash_data;
+                       tmp < ARRAY_SIZE(dataflash_data);
+                       tmp++, info++) {
+               if (info->jedec_id == jedec) {
+                       if (info->flags & SUP_POW2PS) {
+                               status = dataflash_status(spi);
+                               if (status & 0x1)
+                                       /* return power of 2 pagesize */
+                                       return ++info;
+                               else
+                                       return info;
+                       }
+               }
+       }
+       return NULL;
+}
+
 static int __devinit dataflash_probe(struct spi_device *spi)
 {
        int status;
+       struct flash_info       *info;
+
+       /*
+        * Try to detect dataflash by JEDEC ID.
+        * If it succeeds we know we have either a C or D part.
+        * D will support power of 2 pagesize option.
+        */
+
+       info = jedec_probe(spi);
+
+       if (info != NULL)
+               return add_dataflash(spi, info->name, info->nr_pages,
+                                info->pagesize, info->pageoffset);
+
 
        status = dataflash_status(spi);
        if (status <= 0 || status == 0xff) {
@@ -551,16 +658,16 @@ static int __devinit dataflash_probe(struct spi_device *spi)
                status = add_dataflash(spi, "AT45DB011B", 512, 264, 9);
                break;
        case 0x14:      /* 0 1 0 1 x x */
-               status = add_dataflash(spi, "AT45DB021B", 1025, 264, 9);
+               status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9);
                break;
        case 0x1c:      /* 0 1 1 1 x x */
-               status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9);
+               status = add_dataflash(spi, "AT45DB041B", 2048, 264, 9);
                break;
        case 0x24:      /* 1 0 0 1 x x */
                status = add_dataflash(spi, "AT45DB081B", 4096, 264, 9);
                break;
        case 0x2c:      /* 1 0 1 1 x x */
-               status = add_dataflash(spi, "AT45DB161x", 4096, 528, 10);
+               status = add_dataflash(spi, "AT45DB161B", 4096, 528, 10);
                break;
        case 0x34:      /* 1 1 0 1 x x */
                status = add_dataflash(spi, "AT45DB321x", 8192, 528, 10);
index 0399be178620ab67be22917e12595118fc3211f0..3aaca88847d39cec5421a072b13609bd25796cda 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * mtdram - a test mtd device
- * $Id: mtdram.c,v 1.37 2005/04/21 03:42:11 joern Exp $
  * Author: Alexander Larsson <alex@cendio.se>
  *
  * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
index c7987b1c5e01db083f237fa26b1c48d963a77a7d..088fbb7595b5adbb5fee1e240ecd605b4a53b51e 100644 (file)
@@ -1,6 +1,4 @@
 /**
- * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $
- *
  * Copyright (c) ????          Jochen Schäuble <psionic@psionic.de>
  * Copyright (c) 2003-2004     Joern Engel <joern@wh.fh-wedel.de>
  *
index bc99817490649572ced4f09e01ec2606c252a854..d38bca64bb1500c93c18641ebb5d29e3749e3c41 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: pmc551.c,v 1.32 2005/11/07 11:14:25 gleixner Exp $
- *
  * PMC551 PCI Mezzanine Ram Device
  *
  * Author:
index cb86db746f28c91f552462696b10d5c99f23c07d..a425d09f35a02fd9f3fbd2f0ff9df68019ba11e3 100644 (file)
@@ -1,7 +1,5 @@
 /*======================================================================
 
-  $Id: slram.c,v 1.36 2005/11/07 11:14:25 gleixner Exp $
-
   This driver provides a method to access memory not used by the kernel
   itself (i.e. if the kernel commandline mem=xxx is used). To actually
   use slram at least mtdblock or mtdchar is required (for block or
index 5c29872184e65b64c3f5938968aaeb29061601f6..f34f20c7891162f6d5fb7c101d3cac9c55da2e82 100644 (file)
@@ -1,5 +1,4 @@
 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
- * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $
  *
  * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
@@ -1078,8 +1077,6 @@ static struct mtd_blktrans_ops ftl_tr = {
 
 static int init_ftl(void)
 {
-       DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n");
-
        return register_mtd_blktrans(&ftl_tr);
 }
 
index b0e396504e67cf98d2469afc881594706c706bd9..c4f9d3378b24c658f246d8777d47735050cfdffe 100644 (file)
@@ -7,8 +7,6 @@
  * (c) 1999 Machine Vision Holdings, Inc.
  * Author: David Woodhouse <dwmw2@infradead.org>
  *
- * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $
- *
  * 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
@@ -953,9 +951,6 @@ static struct mtd_blktrans_ops inftl_tr = {
 
 static int __init init_inftl(void)
 {
-       printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "
-               "inftlmount.c %s\n", inftlmountrev);
-
        return register_mtd_blktrans(&inftl_tr);
 }
 
index c551d2f0779c2e8c141c3a18c3f5bae05686dd84..9113628ed1ef3b54505780efcd7dc729537ea202 100644 (file)
@@ -8,8 +8,6 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: inftlmount.c,v 1.18 2005/11/07 11:14:20 gleixner Exp $
- *
  * 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
@@ -39,8 +37,6 @@
 #include <linux/mtd/inftl.h>
 #include <linux/mtd/compatmac.h>
 
-char inftlmountrev[]="$Revision: 1.18 $";
-
 /*
  * find_boot_record: Find the INFTL Media Header and its Spare copy which
  *     contains the various device information of the INFTL partition and
index d2fbc29645236d838dd046abb057b0f3a5f0d488..df8e00bba07b2726f71fbf02b6b1d27ff0cf396a 100644 (file)
@@ -1,5 +1,4 @@
 # drivers/mtd/maps/Kconfig
-# $Id: Kconfig,v 1.61 2005/11/07 11:14:26 gleixner Exp $
 
 menu "Mapping drivers for chip access"
        depends on MTD!=n
@@ -510,6 +509,17 @@ config MTD_PCMCIA_ANONYMOUS
 
          If unsure, say N.
 
+config MTD_BFIN_ASYNC
+       tristate "Blackfin BF533-STAMP Flash Chip Support"
+       depends on BFIN533_STAMP && MTD_CFI
+       select MTD_PARTITIONS
+       default y
+       help
+         Map driver which allows for simultaneous utilization of
+         ethernet and CFI parallel flash.
+
+         If compiled as a module, it will be called bfin-async-flash.
+
 config MTD_UCLINUX
        tristate "Generic uClinux RAM/ROM filesystem support"
        depends on MTD_PARTITIONS && !MMU
@@ -539,24 +549,6 @@ config MTD_DMV182
         help
           Map driver for Dy-4 SVME/DMV-182 board.
 
-config MTD_BAST
-       tristate "Map driver for Simtec BAST (EB2410ITX) or Thorcom VR1000"
-       depends on ARCH_BAST || MACH_VR1000
-       select MTD_PARTITIONS
-       select MTD_MAP_BANK_WIDTH_16
-       select MTD_JEDECPROBE
-       help
-         Map driver for NOR flash on the Simtec BAST (EB2410ITX), or the
-         Thorcom VR1000
-
-         Note, this driver *cannot* over-ride the WP link on the
-         board, or currently detect the state of the link.
-
-config MTD_BAST_MAXSIZE
-       int "Maximum size for BAST flash area (MiB)"
-       depends on MTD_BAST
-       default "4"
-
 config MTD_SHARP_SL
        tristate "ROM mapped on Sharp SL Series"
        depends on ARCH_PXA
index c6ce8673dab293e755887c903cd4eb6738f4625a..6cda6df973e5d11c187533e48534ac5ff60c6038 100644 (file)
@@ -1,7 +1,6 @@
 #
 # linux/drivers/maps/Makefile
 #
-# $Id: Makefile.common,v 1.34 2005/11/07 11:14:26 gleixner Exp $
 
 ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
 obj-$(CONFIG_MTD)              += map_funcs.o
@@ -10,7 +9,6 @@ endif
 # Chip mappings
 obj-$(CONFIG_MTD_CDB89712)     += cdb89712.o
 obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
-obj-$(CONFIG_MTD_BAST)         += bast-flash.o
 obj-$(CONFIG_MTD_CFI_FLAGADM)  += cfi_flagadm.o
 obj-$(CONFIG_MTD_DC21285)      += dc21285.o
 obj-$(CONFIG_MTD_DILNETPC)     += dilnetpc.o
@@ -66,3 +64,4 @@ obj-$(CONFIG_MTD_SHARP_SL)    += sharpsl-flash.o
 obj-$(CONFIG_MTD_PLATRAM)      += plat-ram.o
 obj-$(CONFIG_MTD_OMAP_NOR)     += omap_nor.o
 obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
+obj-$(CONFIG_MTD_BFIN_ASYNC)   += bfin-async-flash.o
index 728aed6ad722e276e1642da0a7951b3bd61363f2..948b86f35ef4a2e2068f12878d21f6653f4e9868 100644 (file)
@@ -2,7 +2,6 @@
  * amd76xrom.c
  *
  * Normal mappings of chips in physical memory
- * $Id: amd76xrom.c,v 1.21 2005/11/07 11:14:26 gleixner Exp $
  */
 
 #include <linux/module.h>
index 7ed3424dd959bb2aa3ce42af93a4dc25ad74f25f..cf32267263df815ed07ce6dbb41bd8f96c8e81aa 100644 (file)
@@ -2,8 +2,6 @@
  * NV-RAM memory access on autcpu12
  * (C) 2002 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: autcpu12-nvram.c,v 1.9 2005/11/07 11:14:26 gleixner Exp $
- *
  * 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
diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c
deleted file mode 100644 (file)
index 1f49206..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-/* linux/drivers/mtd/maps/bast-flash.c
- *
- * Copyright (c) 2004-2005 Simtec Electronics
- *     Ben Dooks <ben@simtec.co.uk>
- *
- * Simtec Bast (EB2410ITX) NOR MTD Mapping driver
- *
- * Changelog:
- *     20-Sep-2004  BJD  Initial version
- *     17-Jan-2005  BJD  Add whole device if no partitions found
- *
- * $Id: bast-flash.c,v 1.5 2005/11/07 11:14:26 gleixner Exp $
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/mach/flash.h>
-
-#include <asm/arch/map.h>
-#include <asm/arch/bast-map.h>
-#include <asm/arch/bast-cpld.h>
-
-#ifdef CONFIG_MTD_BAST_MAXSIZE
-#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M)
-#else
-#define AREA_MAXSIZE (32 * SZ_1M)
-#endif
-
-#define PFX "bast-flash: "
-
-struct bast_flash_info {
-       struct mtd_info         *mtd;
-       struct map_info          map;
-       struct mtd_partition    *partitions;
-       struct resource         *area;
-};
-
-static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-
-static void bast_flash_setrw(int to)
-{
-       unsigned int val;
-       unsigned long flags;
-
-       local_irq_save(flags);
-       val = __raw_readb(BAST_VA_CTRL3);
-
-       if (to)
-               val |= BAST_CPLD_CTRL3_ROMWEN;
-       else
-               val &= ~BAST_CPLD_CTRL3_ROMWEN;
-
-       pr_debug("new cpld ctrl3=%02x\n", val);
-
-       __raw_writeb(val, BAST_VA_CTRL3);
-       local_irq_restore(flags);
-}
-
-static int bast_flash_remove(struct platform_device *pdev)
-{
-       struct bast_flash_info *info = platform_get_drvdata(pdev);
-
-       platform_set_drvdata(pdev, NULL);
-
-       if (info == NULL)
-               return 0;
-
-       if (info->map.virt != NULL)
-               iounmap(info->map.virt);
-
-       if (info->mtd) {
-               del_mtd_partitions(info->mtd);
-               map_destroy(info->mtd);
-       }
-
-       kfree(info->partitions);
-
-       if (info->area) {
-               release_resource(info->area);
-               kfree(info->area);
-       }
-
-       kfree(info);
-
-       return 0;
-}
-
-static int bast_flash_probe(struct platform_device *pdev)
-{
-       struct bast_flash_info *info;
-       struct resource *res;
-       int err = 0;
-
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
-       if (info == NULL) {
-               printk(KERN_ERR PFX "no memory for flash info\n");
-               err = -ENOMEM;
-               goto exit_error;
-       }
-
-       memzero(info, sizeof(*info));
-       platform_set_drvdata(pdev, info);
-
-       res = pdev->resource;  /* assume that the flash has one resource */
-
-       info->map.phys = res->start;
-       info->map.size = res->end - res->start + 1;
-       info->map.name = pdev->dev.bus_id;
-       info->map.bankwidth = 2;
-
-       if (info->map.size > AREA_MAXSIZE)
-               info->map.size = AREA_MAXSIZE;
-
-       pr_debug("%s: area %08lx, size %ld\n", __func__,
-                info->map.phys, info->map.size);
-
-       info->area = request_mem_region(res->start, info->map.size,
-                                       pdev->name);
-       if (info->area == NULL) {
-               printk(KERN_ERR PFX "cannot reserve flash memory region\n");
-               err = -ENOENT;
-               goto exit_error;
-       }
-
-       info->map.virt = ioremap(res->start, info->map.size);
-       pr_debug("%s: virt at %08x\n", __func__, (int)info->map.virt);
-
-       if (info->map.virt == 0) {
-               printk(KERN_ERR PFX "failed to ioremap() region\n");
-               err = -EIO;
-               goto exit_error;
-       }
-
-       simple_map_init(&info->map);
-
-       /* enable the write to the flash area */
-
-       bast_flash_setrw(1);
-
-       /* probe for the device(s) */
-
-       info->mtd = do_map_probe("jedec_probe", &info->map);
-       if (info->mtd == NULL)
-               info->mtd = do_map_probe("cfi_probe", &info->map);
-
-       if (info->mtd == NULL) {
-               printk(KERN_ERR PFX "map_probe() failed\n");
-               err = -ENXIO;
-               goto exit_error;
-       }
-
-       /* mark ourselves as the owner */
-       info->mtd->owner = THIS_MODULE;
-
-       err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0);
-       if (err > 0) {
-               err = add_mtd_partitions(info->mtd, info->partitions, err);
-               if (err)
-                       printk(KERN_ERR PFX "cannot add/parse partitions\n");
-       } else {
-               err = add_mtd_device(info->mtd);
-       }
-
-       if (err == 0)
-               return 0;
-
-       /* fall through to exit error */
-
- exit_error:
-       bast_flash_remove(pdev);
-       return err;
-}
-
-static struct platform_driver bast_flash_driver = {
-       .probe          = bast_flash_probe,
-       .remove         = bast_flash_remove,
-       .driver         = {
-               .name   = "bast-nor",
-               .owner  = THIS_MODULE,
-       },
-};
-
-static int __init bast_flash_init(void)
-{
-       printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n");
-       return platform_driver_register(&bast_flash_driver);
-}
-
-static void __exit bast_flash_exit(void)
-{
-       platform_driver_unregister(&bast_flash_driver);
-}
-
-module_init(bast_flash_init);
-module_exit(bast_flash_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("BAST MTD Map driver");
-MODULE_ALIAS("platform:bast-nor");
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c
new file mode 100644 (file)
index 0000000..6fec86a
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * drivers/mtd/maps/bfin-async-flash.c
+ *
+ * Handle the case where flash memory and ethernet mac/phy are
+ * mapped onto the same async bank.  The BF533-STAMP does this
+ * for example.  All board-specific configuration goes in your
+ * board resources file.
+ *
+ * Copyright 2000 Nicolas Pitre <nico@cam.org>
+ * Copyright 2005-2008 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <asm/blackfin.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <asm/unaligned.h>
+
+#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); })
+
+#define DRIVER_NAME "bfin-async-flash"
+
+struct async_state {
+       struct mtd_info *mtd;
+       struct map_info map;
+       int enet_flash_pin;
+       uint32_t flash_ambctl0, flash_ambctl1;
+       uint32_t save_ambctl0, save_ambctl1;
+       unsigned long irq_flags;
+};
+
+static void switch_to_flash(struct async_state *state)
+{
+       local_irq_save(state->irq_flags);
+
+       gpio_set_value(state->enet_flash_pin, 0);
+
+       state->save_ambctl0 = bfin_read_EBIU_AMBCTL0();
+       state->save_ambctl1 = bfin_read_EBIU_AMBCTL1();
+       bfin_write_EBIU_AMBCTL0(state->flash_ambctl0);
+       bfin_write_EBIU_AMBCTL1(state->flash_ambctl1);
+       SSYNC();
+}
+
+static void switch_back(struct async_state *state)
+{
+       bfin_write_EBIU_AMBCTL0(state->save_ambctl0);
+       bfin_write_EBIU_AMBCTL1(state->save_ambctl1);
+       SSYNC();
+
+       gpio_set_value(state->enet_flash_pin, 1);
+
+       local_irq_restore(state->irq_flags);
+}
+
+static map_word bfin_read(struct map_info *map, unsigned long ofs)
+{
+       struct async_state *state = (struct async_state *)map->map_priv_1;
+       uint16_t word;
+       map_word test;
+
+       switch_to_flash(state);
+
+       word = readw(map->virt + ofs);
+
+       switch_back(state);
+
+       test.x[0] = word;
+       return test;
+}
+
+static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+       struct async_state *state = (struct async_state *)map->map_priv_1;
+
+       switch_to_flash(state);
+
+       memcpy(to, map->virt + from, len);
+
+       switch_back(state);
+}
+
+static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs)
+{
+       struct async_state *state = (struct async_state *)map->map_priv_1;
+       uint16_t d;
+
+       d = d1.x[0];
+
+       switch_to_flash(state);
+
+       writew(d, map->virt + ofs);
+       SSYNC();
+
+       switch_back(state);
+}
+
+static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+       struct async_state *state = (struct async_state *)map->map_priv_1;
+
+       switch_to_flash(state);
+
+       memcpy(map->virt + to, from, len);
+       SSYNC();
+
+       switch_back(state);
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
+#endif
+
+static int __devinit bfin_flash_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct physmap_flash_data *pdata = pdev->dev.platform_data;
+       struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       struct async_state *state;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       state->map.name       = DRIVER_NAME;
+       state->map.read       = bfin_read;
+       state->map.copy_from  = bfin_copy_from;
+       state->map.write      = bfin_write;
+       state->map.copy_to    = bfin_copy_to;
+       state->map.bankwidth  = pdata->width;
+       state->map.size       = memory->end - memory->start + 1;
+       state->map.virt       = (void __iomem *)memory->start;
+       state->map.phys       = memory->start;
+       state->map.map_priv_1 = (unsigned long)state;
+       state->enet_flash_pin = platform_get_irq(pdev, 0);
+       state->flash_ambctl0  = flash_ambctl->start;
+       state->flash_ambctl1  = flash_ambctl->end;
+
+       if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) {
+               pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin);
+               return -EBUSY;
+       }
+       gpio_direction_output(state->enet_flash_pin, 1);
+
+       pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8);
+       state->mtd = do_map_probe(memory->name, &state->map);
+       if (!state->mtd)
+               return -ENXIO;
+
+#ifdef CONFIG_MTD_PARTITIONS
+       ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0);
+       if (ret > 0) {
+               pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n");
+               add_mtd_partitions(state->mtd, pdata->parts, ret);
+
+       } else if (pdata->nr_parts) {
+               pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n");
+               add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts);
+
+       } else
+#endif
+       {
+               pr_devinit(KERN_NOTICE DRIVER_NAME ": no partition info available, registering whole flash at once\n");
+               add_mtd_device(state->mtd);
+       }
+
+       platform_set_drvdata(pdev, state);
+
+       return 0;
+}
+
+static int __devexit bfin_flash_remove(struct platform_device *pdev)
+{
+       struct async_state *state = platform_get_drvdata(pdev);
+       gpio_free(state->enet_flash_pin);
+#ifdef CONFIG_MTD_PARTITIONS
+       del_mtd_partitions(state->mtd);
+#endif
+       map_destroy(state->mtd);
+       kfree(state);
+       return 0;
+}
+
+static struct platform_driver bfin_flash_driver = {
+       .probe          = bfin_flash_probe,
+       .remove         = __devexit_p(bfin_flash_remove),
+       .driver         = {
+               .name   = DRIVER_NAME,
+       },
+};
+
+static int __init bfin_flash_init(void)
+{
+       return platform_driver_register(&bfin_flash_driver);
+}
+module_init(bfin_flash_init);
+
+static void __exit bfin_flash_exit(void)
+{
+       platform_driver_unregister(&bfin_flash_driver);
+}
+module_exit(bfin_flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MTD map driver for Blackfins with flash/ethernet on same async bank");
index 9f17bb6c5a9d388714c8822ea412230d918b6b38..cb507da0a87d74e62dea447b08a2ee64cc1faade 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * Flash on Cirrus CDB89712
  *
- * $Id: cdb89712.c,v 1.11 2005/11/07 11:14:26 gleixner Exp $
  */
 
 #include <linux/module.h>
index 629e6e2641a84c7ca5e2d0ecd53b56566008362b..6464d487eb1ade983c336ab65edbe7312c0de4bc 100644 (file)
@@ -11,7 +11,6 @@
  *
  * (C) 2000 Nicolas Pitre <nico@cam.org>
  *
- * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $
  */
 
 #include <linux/module.h>
index 65e5ee552010be7a4c147a34f806ac3d9a5e46d0..0ecc3f6d735bfb78a2b8ad467dc2d69cb16b50ac 100644 (file)
@@ -1,8 +1,6 @@
 /*
  *  Copyright Â© 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
  *
- *  $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $
- *
  *  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
index 92a9c7fac99328adbb0208cc69503685b84882cc..e115667bf1d04083b50d5ea18df34d55f34af7a4 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: dbox2-flash.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $
- *
  * D-Box 2 flash driver
  */
 
index b32bb9347d713045bf9db02cdc08470e70b3165f..3aa018c092f893369ac7d4ff9b4073efa7af861f 100644 (file)
@@ -4,8 +4,6 @@
  * (C) 2000  Nicolas Pitre <nico@cam.org>
  *
  * This code is GPL
- *
- * $Id: dc21285.c,v 1.24 2005/11/07 11:14:26 gleixner Exp $
  */
 #include <linux/module.h>
 #include <linux/types.h>
index 1c3b34ad732511f56c5138a0ac92d9fad2b95263..0713e3a5a22cb4b4bf1baa1db4c5d57d01259e13 100644 (file)
@@ -14,8 +14,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  *
- * $Id: dilnetpc.c,v 1.20 2005/11/07 11:14:26 gleixner Exp $
- *
  * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
  * featuring the AMD Elan SC410 processor. There are two variants of this
  * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash
index e0558b0b2fe65946ecfd00b3a05f57f79624633d..d171674eb2ed1ec17ee73a0c63a4bdb034e576d7 100644 (file)
@@ -4,8 +4,6 @@
  *
  * Flash map driver for the Dy4 SVME182 board
  *
- * $Id: dmv182.c,v 1.6 2005/11/07 11:14:26 gleixner Exp $
- *
  * Copyright 2003-2004, TimeSys Corporation
  *
  * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp.
index 1488bb92f26fab6ad19e46515263332311997279..d92b7c70d3ed58677d5e7971f21c657a875a8b74 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ebony.c,v 1.16 2005/11/07 11:14:26 gleixner Exp $
- *
  * Mapping for Ebony user flash
  *
  * Matt Porter <mporter@kernel.crashing.org>
index 1c5b97c8968540703130e7e6817f5c4a32e71d1f..9433738c1664178071690e484c4f346886ee4f0e 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: edb7312.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $
- *
  * Handle mapping of the NOR flash on Cogent EDB7312 boards
  *
  * Copyright 2002 SYSGO Real-Time Solutions GmbH
index 7c50c271651c768a73307ee04cbc5eb3f11b4460..a8e3fde4cbd557f9feb18f61d8b114b5a267fb56 100644 (file)
@@ -1,6 +1,5 @@
 /* fortunet.c memory map
  *
- * $Id: fortunet.c,v 1.11 2005/11/07 11:14:27 gleixner Exp $
  */
 
 #include <linux/module.h>
index 6dde3182d64a6966d1b6247bc52dd5ee25c8c78b..ef891547446270ade69927c00627d39be3845810 100644 (file)
@@ -2,8 +2,6 @@
  * Flash memory access on Hynix GMS30C7201/HMS30C7202 based
  * evaluation boards
  *
- * $Id: h720x-flash.c,v 1.12 2005/11/07 11:14:27 gleixner Exp $
- *
  * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com>
  *     2003 Thomas Gleixner <tglx@linutronix.de>
  */
index 2c884c49e84a5cd5f0bb1a50e8fc285bb408ceb3..aeb6c916e23f8f6925dda4152055c8417241b78e 100644 (file)
@@ -2,7 +2,6 @@
  * ichxrom.c
  *
  * Normal mappings of chips in physical memory
- * $Id: ichxrom.c,v 1.19 2005/11/07 11:14:27 gleixner Exp $
  */
 
 #include <linux/module.h>
index a0b4dc7155dc66ffc6fd02a5582f860f0381794b..2682ab51a36786732fdd08b83b8cde83f331ad62 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: impa7.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $
- *
  * Handle mapping of the NOR flash on implementa A7 boards
  *
  * Copyright 2002 SYSGO Real-Time Solutions GmbH
index 325c8880c4379393b4266c0849ee0bfc85488344..ee361aaadb1e19a7620de24f2a05aad9fbb48edb 100644 (file)
@@ -22,8 +22,6 @@
    This is access code for flashes using ARM's flash partitioning
    standards.
 
-   $Id: integrator-flash.c,v 1.20 2005/11/07 11:14:27 gleixner Exp $
-
 ======================================================================*/
 
 #include <linux/module.h>
index f27c132794c3efa365e6408b7fa40196ec661ecb..a806119797e0feeb3fd2969199a334daa6aa9c21 100644 (file)
@@ -4,8 +4,6 @@
  * (C) 2000 Nicolas Pitre <nico@cam.org>
  * (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com>
  * (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes
- *
- * $Id: ipaq-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $
  */
 
 #include <linux/module.h>
index c8396b8574c4624debf97d0846ab0a88dd07867f..c2264792a20b2b77a2a3ef5a49ea2fcbc8184f2a 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ixp2000.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $
- *
  * drivers/mtd/maps/ixp2000.c
  *
  * Mapping for the Intel XScale IXP2000 based systems
index 01f19a4714b5b7383506d929208b0637a6cc7c0f..9c7a5fbd4e512e93c58de1f08ee2d2b388ca8af7 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ixp4xx.c,v 1.13 2005/11/16 16:23:21 dvrabel Exp $
- *
  * drivers/mtd/maps/ixp4xx.c
  *
  * MTD Map file for IXP4XX based systems. Please do not make per-board
index 67620adf48114713366d24a8e0175b71e7e65501..9e054503c4cf2920e23c7fded7d2bd39741c4166 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: l440gx.c,v 1.18 2005/11/07 11:14:27 gleixner Exp $
- *
  * BIOS Flash chip on Intel 440GX board.
  *
  * Bugs this currently does not work under linuxBIOS.
index 9105e6ca0aa61de3be27f9063464e1c3372a9a25..3f268370eeca150a7029b7a28c9868d5170f3a3b 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: map_funcs.c,v 1.10 2005/06/06 23:04:36 tpoynor Exp $
- *
  * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
  * is enabled.
  */
index 06b1187278468bb4e0392b8a70edc1b32842c3f3..706f67394b073cb97f8e5894bd88129de4f858cd 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mbx860.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $
- *
  * Handle mapping of the flash on MBX860 boards
  *
  * Author:     Anton Todorov
index 95dcab2146ad821c90fa29df1784d62076b7d3ee..c0cb319b2b70e26f38f6c9133263098ff245bad0 100644 (file)
@@ -3,8 +3,6 @@
  * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
  *     based on sc520cdp.c by Sysgo Real-Time Solutions GmbH
  *
- * $Id: netsc520.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $
- *
  * 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
index 0c9b305a72e0de104e8ad53514f4c36ceebdd2ee..965e6c6d6ab022237d523c224eb47b13830d67c3 100644 (file)
@@ -5,8 +5,6 @@
  *
  *      (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
  *      (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
- *
- *     $Id: nettel.c,v 1.12 2005/11/29 14:30:00 gleixner Exp $
  */
 
 /****************************************************************************/
index a6642db3d325e19e6418ce0675fdd0f596e1f522..43e04c1d22a9dd74140f51e3f42092b7b950d0d4 100644 (file)
@@ -1,4 +1,3 @@
-// $Id: octagon-5066.c,v 1.28 2005/11/07 11:14:27 gleixner Exp $
 /* ######################################################################
 
    Octagon 5066 MTD Driver.
index e6e391efbeb6925c30398e5eb8bc2879337aee9b..0a60ebbc217591af7c00511ad3096f8d2fa9656d 100644 (file)
@@ -4,8 +4,6 @@
  * jzhang@ti.com (C) 2003 Texas Instruments.
  *
  *  (C) 2002 MontVista Software, Inc.
- *
- * $Id: omap-toto-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $
  */
 
 #include <linux/module.h>
index d2ab1bae9c346fbf2168ff59199222f226e6f2cb..5c6a25c90380aa70ab2afb4b3f34860adf76d43f 100644 (file)
@@ -7,8 +7,6 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- *  $Id: pci.c,v 1.14 2005/11/17 08:20:27 dwmw2 Exp $
- *
  * Generic PCI memory map driver.  We support the following boards:
  *  - Intel IQ80310 ATU.
  *  - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001
index 0cc31675aeb909c472479c41a14abc970ec9217e..90924fb00481eda9289716337883155326bd8130 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: pcmciamtd.c,v 1.55 2005/11/07 11:14:28 gleixner Exp $
- *
  * pcmciamtd.c - MTD driver for PCMCIA flash memory cards
  *
  * Author: Simon Evans <spse@secret.org.uk>
@@ -48,7 +46,6 @@ static const int debug = 0;
 
 
 #define DRIVER_DESC    "PCMCIA Flash memory card driver"
-#define DRIVER_VERSION "$Revision: 1.55 $"
 
 /* Size of the PCMCIA address space: 26 bits = 64 MB */
 #define MAX_PCMCIA_ADDR        0x4000000
@@ -785,7 +782,7 @@ static struct pcmcia_driver pcmciamtd_driver = {
 
 static int __init init_pcmciamtd(void)
 {
-       info(DRIVER_DESC " " DRIVER_VERSION);
+       info(DRIVER_DESC);
 
        if(bankwidth && bankwidth != 1 && bankwidth != 2) {
                info("bad bankwidth (%d), using default", bankwidth);
index 183255fcfdcbd288dc38e697b6a6ed8ff3be3321..42d844f8f6bfef79bdfef42ed48b33368ac4ecd0 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: physmap.c,v 1.39 2005/11/29 14:49:36 gleixner Exp $
- *
  * Normal mappings of chips in physical memory
  *
  * Copyright (C) 2003 MontaVista Software Inc.
@@ -203,7 +201,19 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state
        int i;
 
        for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
-               ret |= info->mtd[i]->suspend(info->mtd[i]);
+               if (info->mtd[i]->suspend) {
+                       ret = info->mtd[i]->suspend(info->mtd[i]);
+                       if (ret)
+                               goto fail;
+               }
+
+       return 0;
+fail:
+       for (--i; i >= 0; --i)
+               if (info->mtd[i]->suspend) {
+                       BUG_ON(!info->mtd[i]->resume);
+                       info->mtd[i]->resume(info->mtd[i]);
+               }
 
        return ret;
 }
@@ -214,7 +224,8 @@ static int physmap_flash_resume(struct platform_device *dev)
        int i;
 
        for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
-               info->mtd[i]->resume(info->mtd[i]);
+               if (info->mtd[i]->resume)
+                       info->mtd[i]->resume(info->mtd[i]);
 
        return 0;
 }
@@ -225,8 +236,9 @@ static void physmap_flash_shutdown(struct platform_device *dev)
        int i;
 
        for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
-               if (info->mtd[i]->suspend(info->mtd[i]) == 0)
-                       info->mtd[i]->resume(info->mtd[i]);
+               if (info->mtd[i]->suspend && info->mtd[i]->resume)
+                       if (info->mtd[i]->suspend(info->mtd[i]) == 0)
+                               info->mtd[i]->resume(info->mtd[i]);
 }
 #else
 #define physmap_flash_suspend NULL
index 3eb2643b232875cea389ed0cfb45a55147eb898b..e7dd9c8a965e2fc86d32b98eaa060657d0819724 100644 (file)
@@ -6,8 +6,6 @@
  *
  * Generic platfrom device based RAM map
  *
- * $Id: plat-ram.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $
- *
  * 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
index 4d858b3d5f826f368872d5317ecc4e9388c91a7c..de002eb1a7fe3a943753b2622a2dd64f7cea909a 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: redwood.c,v 1.11 2005/11/07 11:14:28 gleixner Exp $
- *
  * drivers/mtd/maps/redwood.c
  *
  * FLASH map for the IBM Redwood 4/5/6 boards.
index 809a0c8e7aaf7c0fd2a1b5d8d12536f6aadd7eb1..14d90edb443076d3759e98f66464b70ff22ad172 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $
- *
  * Handle mapping of the flash on the RPX Lite and CLLF boards
  */
 
index c7d5a52a2d559e997c62ba629bdf38f28f72e2c1..e177a43dfff0e7b07ec74f7752550ce136c4d6d1 100644 (file)
@@ -2,8 +2,6 @@
  * Flash memory access on SA11x0 based devices
  *
  * (C) 2000 Nicolas Pitre <nico@cam.org>
- *
- * $Id: sa1100-flash.c,v 1.51 2005/11/07 11:14:28 gleixner Exp $
  */
 #include <linux/module.h>
 #include <linux/types.h>
index b8c1331b7a04e18d6cafda2fcb86d03ea0925681..6e1e99cd2b59c443715450c03b7f446261ac1776 100644 (file)
@@ -4,9 +4,6 @@
  * Carolyn Smith, Tektronix, Inc.
  *
  * This code is GPLed
- *
- * $Id: sbc8240.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $
- *
  */
 
 /*
index 7cc4041d096d6eaf5fb9e351760a95e1f1d2382c..1b1c0b7e11ef410e64921cdb34db049d86968183 100644 (file)
@@ -17,8 +17,6 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
-   $Id: sbc_gxx.c,v 1.35 2005/11/07 11:14:28 gleixner Exp $
-
 The SBC-MediaGX / SBC-GXx has up to 16 MiB of
 Intel StrataFlash (28F320/28F640) in x8 mode.
 
index 4045e372b90db10b53baaae4d15d045a881e90f9..85c1e56309ec571a326066273be94099a425f7cb 100644 (file)
@@ -16,8 +16,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  *
- * $Id: sc520cdp.c,v 1.23 2005/11/17 08:20:27 dwmw2 Exp $
- *
  *
  * The SC520CDP is an evaluation board for the Elan SC520 processor available
  * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
index 0fc5584324e3d38488dd62627867787c2201e5d1..21169e6d646c0df26318fdd22176915e515fa244 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * MTD map driver for BIOS Flash on Intel SCB2 boards
- * $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $
  * Copyright (C) 2002 Sun Microsystems, Inc.
  * Tim Hockin <thockin@sun.com>
  *
index 5e2bce22f37ca6146ee46c7abf3eca84628ff2f0..b5391ebb736e1bac6b4175d7e897670dc38e7ea9 100644 (file)
@@ -2,8 +2,6 @@
 
    Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
 
-   $Id: scx200_docflash.c,v 1.12 2005/11/07 11:14:28 gleixner Exp $
-
    National Semiconductor SCx200 flash mapped with DOCCS
 */
 
index 917dc778f24e12f2c758473c38266804aa517d79..026eab028189a170ebab7c7d013a6da86b4981d3 100644 (file)
@@ -4,8 +4,6 @@
  * Copyright (C) 2001 Lineo Japan, Inc.
  * Copyright (C) 2002  SHARP
  *
- * $Id: sharpsl-flash.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $
- *
  * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp
  *          Handle mapping of the flash on the RPX Lite and CLLF boards
  *
index d76ceef453ce66169c6b02754ed19f26aa05db59..0eb41d9c67867fb90280ba0570408e1536b4760e 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: solutionengine.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $
- *
  * Flash and EPROM on Hitachi Solution Engine and similar boards.
  *
  * (C) 2001 Red Hat, Inc.
index 001af7f7dddad5273164cac453f4ea3367386f91..0d7c88396c888a3d367d84f7b3e654b77124429a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sun_uflash.c,v 1.13 2005/11/07 11:14:28 gleixner Exp $
+/*
  *
  * sun_uflash - Driver implementation for user-programmable flash
  * present on many Sun Microsystems SME boardsets.
index 5217340573140f9f7994e88b4ee0d93e74e013fe..a5d3d8531faa1aa22c1fbf8a4d442c4ef7402862 100644 (file)
@@ -2,8 +2,6 @@
  * Handle mapping of the flash memory access routines
  * on TQM8xxL based devices.
  *
- * $Id: tqm8xxl.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $
- *
  * based on rpxlite.c
  *
  * Copyright(C) 2001 Kirk Lee <kirk@hpc.ee.ntu.edu.tw>
index b47270e850bc78754ed876e2704f9355e0d87fec..e2147bf11c88948036c6d4d696b38f7952e62f01 100644 (file)
@@ -22,8 +22,6 @@
  * - Drive A and B use the resident flash disk (RFD) flash translation layer.
  * - If you have created your own jffs file system and the bios overwrites
  *   it during boot, try disabling Drive A: and B: in the boot order.
- *
- * $Id: ts5500_flash.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $
  */
 
 #include <linux/init.h>
index 0f915ac3102e21dba8a8b9fc5c9a5624f20ff183..77a8bfc025770b657ae404b814d7e718ea3da005 100644 (file)
@@ -2,7 +2,6 @@
  * tsunami_flash.c
  *
  * flash chip on alpha ds10...
- * $Id: tsunami_flash.c,v 1.10 2005/11/07 11:14:29 gleixner Exp $
  */
 #include <asm/io.h>
 #include <asm/core_tsunami.h>
index 3fcf92130aa4cb3fda410a51ba1602667b096147..0dc645f8152f21c90031d86443eb604f1af97957 100644 (file)
@@ -4,8 +4,6 @@
  *     uclinux.c -- generic memory mapped MTD driver for uclinux
  *
  *     (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
- *
- *     $Id: uclinux.c,v 1.12 2005/11/07 11:14:29 gleixner Exp $
  */
 
 /****************************************************************************/
index b3e48739543551e6f506fcb362739262073e22e3..5a0c9a353b0fa63021834321afa2eab691c6864f 100644 (file)
@@ -1,4 +1,3 @@
-// $Id: vmax301.c,v 1.32 2005/11/07 11:14:29 gleixner Exp $
 /* ######################################################################
 
    Tempustech VMAX SBC301 MTD Driver.
index ca932122fb6409e213c9a0f551bf7f01c0bac497..e243476c8171e114ac0f9b254f7f0b932e6e3e5c 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: walnut.c,v 1.3 2005/11/07 11:14:29 gleixner Exp $
- *
  * Mapping for Walnut flash
  * (used ebony.c as a "framework")
  *
index ac5b8105b6efe69ca210571ffc2b098a7997b762..413b0cf9bbd2f6e3e234f661cd1f16c80e6f9a7e 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: wr_sbc82xx_flash.c,v 1.8 2005/11/07 11:14:29 gleixner Exp $
- *
  * Map for flash chips on Wind River PowerQUICC II SBC82xx board.
  *
  * Copyright (C) 2004 Red Hat, Inc.
index 839eed8430a2c77aaf1643937de83ae99501fe6b..9ff007c4962c9f3c58868385729a915ba6b28222 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtd_blkdevs.c,v 1.27 2005/11/07 11:14:20 gleixner Exp $
- *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
  * Interface to Linux 2.5 block layer for MTD 'translation layers'.
@@ -212,7 +210,7 @@ static struct block_device_operations mtd_blktrans_ops = {
 int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
 {
        struct mtd_blktrans_ops *tr = new->tr;
-       struct list_head *this;
+       struct mtd_blktrans_dev *d;
        int last_devnum = -1;
        struct gendisk *gd;
 
@@ -221,8 +219,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
                BUG();
        }
 
-       list_for_each(this, &tr->devs) {
-               struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
+       list_for_each_entry(d, &tr->devs, list) {
                if (new->devnum == -1) {
                        /* Use first free number */
                        if (d->devnum != last_devnum+1) {
@@ -309,33 +306,24 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
 
 static void blktrans_notify_remove(struct mtd_info *mtd)
 {
-       struct list_head *this, *this2, *next;
-
-       list_for_each(this, &blktrans_majors) {
-               struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
-
-               list_for_each_safe(this2, next, &tr->devs) {
-                       struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
+       struct mtd_blktrans_ops *tr;
+       struct mtd_blktrans_dev *dev, *next;
 
+       list_for_each_entry(tr, &blktrans_majors, list)
+               list_for_each_entry_safe(dev, next, &tr->devs, list)
                        if (dev->mtd == mtd)
                                tr->remove_dev(dev);
-               }
-       }
 }
 
 static void blktrans_notify_add(struct mtd_info *mtd)
 {
-       struct list_head *this;
+       struct mtd_blktrans_ops *tr;
 
        if (mtd->type == MTD_ABSENT)
                return;
 
-       list_for_each(this, &blktrans_majors) {
-               struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
-
+       list_for_each_entry(tr, &blktrans_majors, list)
                tr->add_mtd(tr, mtd);
-       }
-
 }
 
 static struct mtd_notifier blktrans_notifier = {
@@ -406,7 +394,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
 
 int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
 {
-       struct list_head *this, *next;
+       struct mtd_blktrans_dev *dev, *next;
 
        mutex_lock(&mtd_table_mutex);
 
@@ -416,10 +404,8 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
        /* Remove it from the list of active majors */
        list_del(&tr->list);
 
-       list_for_each_safe(this, next, &tr->devs) {
-               struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
+       list_for_each_entry_safe(dev, next, &tr->devs, list)
                tr->remove_dev(dev);
-       }
 
        blk_cleanup_queue(tr->blkcore_priv->rq);
        unregister_blkdev(tr->major, tr->name);
index 952da30b17452d829bc97b669b626a38d1836ab5..208c6faa0358afe995608599ea7e39dc4a405e6d 100644 (file)
@@ -1,8 +1,6 @@
 /*
  * Direct MTD block device access
  *
- * $Id: mtdblock.c,v 1.68 2005/11/07 11:14:20 gleixner Exp $
- *
  * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
  * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
  */
index f79dbb49b1a2601ae596283d75ca83564121d0d5..852165f8b1c3510720cfc03ad80c2e68b4cbd728 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $
- *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
  * Simple read-only (writable only for RAM) mtdblock driver
index aef9f4b687c9fdfafc7eb5fddfc6cdb98ea28cc1..d2f331876e4c86a9b8ee74a36541a1baf01b4b9a 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $
- *
  * Character-device access to raw MTD devices.
  *
  */
@@ -494,6 +492,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                struct mtd_oob_buf buf;
                struct mtd_oob_ops ops;
+               struct mtd_oob_buf __user *user_buf = argp;
                uint32_t retlen;
 
                if(!(file->f_mode & 2))
@@ -537,8 +536,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (ops.oobretlen > 0xFFFFFFFFU)
                        ret = -EOVERFLOW;
                retlen = ops.oobretlen;
-               if (copy_to_user(&((struct mtd_oob_buf *)argp)->length,
-                                &retlen, sizeof(buf.length)))
+               if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
                        ret = -EFAULT;
 
                kfree(ops.oobbuf);
@@ -592,29 +590,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case MEMLOCK:
        {
-               struct erase_info_user info;
+               struct erase_info_user einfo;
 
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&einfo, argp, sizeof(einfo)))
                        return -EFAULT;
 
                if (!mtd->lock)
                        ret = -EOPNOTSUPP;
                else
-                       ret = mtd->lock(mtd, info.start, info.length);
+                       ret = mtd->lock(mtd, einfo.start, einfo.length);
                break;
        }
 
        case MEMUNLOCK:
        {
-               struct erase_info_user info;
+               struct erase_info_user einfo;
 
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&einfo, argp, sizeof(einfo)))
                        return -EFAULT;
 
                if (!mtd->unlock)
                        ret = -EOPNOTSUPP;
                else
-                       ret = mtd->unlock(mtd, info.start, info.length);
+                       ret = mtd->unlock(mtd, einfo.start, einfo.length);
                break;
        }
 
@@ -714,15 +712,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case OTPLOCK:
        {
-               struct otp_info info;
+               struct otp_info oinfo;
 
                if (mfi->mode != MTD_MODE_OTP_USER)
                        return -EINVAL;
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
                        return -EFAULT;
                if (!mtd->lock_user_prot_reg)
                        return -EOPNOTSUPP;
-               ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
+               ret = mtd->lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
                break;
        }
 #endif
index d563dcd4b2644e2330398909e02f8e8146dbca9a..2972a5edb73d507e60b9b633b5e85e14a9070074 100644 (file)
@@ -6,8 +6,6 @@
  * NAND support by Christian Gan <cgan@iders.ca>
  *
  * This code is GPL
- *
- * $Id: mtdconcat.c,v 1.11 2005/11/07 11:14:20 gleixner Exp $
  */
 
 #include <linux/kernel.h>
index f7e7890e5bc6889f1ce0aa9fdc18c7be6cf42e6a..a9d246949820a9b0b61aef86061ddf341be21f92 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtdcore.c,v 1.47 2005/11/07 11:14:20 gleixner Exp $
- *
  * Core registration and callback routines for MTD
  * drivers and users.
  *
@@ -53,7 +51,7 @@ int add_mtd_device(struct mtd_info *mtd)
 
        for (i=0; i < MAX_MTD_DEVICES; i++)
                if (!mtd_table[i]) {
-                       struct list_head *this;
+                       struct mtd_notifier *not;
 
                        mtd_table[i] = mtd;
                        mtd->index = i;
@@ -72,10 +70,8 @@ int add_mtd_device(struct mtd_info *mtd)
                        DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
                        /* No need to get a refcount on the module containing
                           the notifier, since we hold the mtd_table_mutex */
-                       list_for_each(this, &mtd_notifiers) {
-                               struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
+                       list_for_each_entry(not, &mtd_notifiers, list)
                                not->add(mtd);
-                       }
 
                        mutex_unlock(&mtd_table_mutex);
                        /* We _know_ we aren't being removed, because
@@ -113,14 +109,12 @@ int del_mtd_device (struct mtd_info *mtd)
                       mtd->index, mtd->name, mtd->usecount);
                ret = -EBUSY;
        } else {
-               struct list_head *this;
+               struct mtd_notifier *not;
 
                /* No need to get a refcount on the module containing
                   the notifier, since we hold the mtd_table_mutex */
-               list_for_each(this, &mtd_notifiers) {
-                       struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
+               list_for_each_entry(not, &mtd_notifiers, list)
                        not->remove(mtd);
-               }
 
                mtd_table[mtd->index] = NULL;
 
index 07c701169344266e907ce92ee2068c2a8ad6881d..edb90b58a9b16980bd42066f6026bbd1ca7c0667 100644 (file)
@@ -5,8 +5,6 @@
  *
  * This code is GPL
  *
- * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $
- *
  *     02-21-2002      Thomas Gleixner <gleixner@autronix.de>
  *                     added support for read_oob, write_oob
  */
@@ -46,8 +44,8 @@ struct mtd_part {
  * to the _real_ device.
  */
 
-static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, u_char *buf)
+static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
        int res;
@@ -56,7 +54,7 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
                len = 0;
        else if (from + len > mtd->size)
                len = mtd->size - from;
-       res = part->master->read (part->master, from + part->offset,
+       res = part->master->read(part->master, from + part->offset,
                                   len, retlen, buf);
        if (unlikely(res)) {
                if (res == -EUCLEAN)
@@ -67,8 +65,8 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
        return res;
 }
 
-static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, void **virt, resource_size_t *phys)
+static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, void **virt, resource_size_t *phys)
 {
        struct mtd_part *part = PART(mtd);
        if (from >= mtd->size)
@@ -87,7 +85,7 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 }
 
 static int part_read_oob(struct mtd_info *mtd, loff_t from,
-                        struct mtd_oob_ops *ops)
+               struct mtd_oob_ops *ops)
 {
        struct mtd_part *part = PART(mtd);
        int res;
@@ -107,38 +105,38 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
        return res;
 }
 
-static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, u_char *buf)
+static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+               size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->read_user_prot_reg (part->master, from,
+       return part->master->read_user_prot_reg(part->master, from,
                                        len, retlen, buf);
 }
 
-static int part_get_user_prot_info (struct mtd_info *mtd,
-                                   struct otp_info *buf, size_t len)
+static int part_get_user_prot_info(struct mtd_info *mtd,
+               struct otp_info *buf, size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->get_user_prot_info (part->master, buf, len);
+       return part->master->get_user_prot_info(part->master, buf, len);
 }
 
-static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, u_char *buf)
+static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+               size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->read_fact_prot_reg (part->master, from,
+       return part->master->read_fact_prot_reg(part->master, from,
                                        len, retlen, buf);
 }
 
-static int part_get_fact_prot_info (struct mtd_info *mtd,
-                                   struct otp_info *buf, size_t len)
+static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
+               size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->get_fact_prot_info (part->master, buf, len);
+       return part->master->get_fact_prot_info(part->master, buf, len);
 }
 
-static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
-                       size_t *retlen, const u_char *buf)
+static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
        if (!(mtd->flags & MTD_WRITEABLE))
@@ -147,12 +145,12 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
                len = 0;
        else if (to + len > mtd->size)
                len = mtd->size - to;
-       return part->master->write (part->master, to + part->offset,
+       return part->master->write(part->master, to + part->offset,
                                    len, retlen, buf);
 }
 
-static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len,
-                       size_t *retlen, const u_char *buf)
+static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
        if (!(mtd->flags & MTD_WRITEABLE))
@@ -161,12 +159,12 @@ static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len,
                len = 0;
        else if (to + len > mtd->size)
                len = mtd->size - to;
-       return part->master->panic_write (part->master, to + part->offset,
+       return part->master->panic_write(part->master, to + part->offset,
                                    len, retlen, buf);
 }
 
 static int part_write_oob(struct mtd_info *mtd, loff_t to,
-                        struct mtd_oob_ops *ops)
+               struct mtd_oob_ops *ops)
 {
        struct mtd_part *part = PART(mtd);
 
@@ -180,31 +178,32 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
        return part->master->write_oob(part->master, to + part->offset, ops);
 }
 
-static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
-                       size_t *retlen, u_char *buf)
+static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+               size_t len, size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->write_user_prot_reg (part->master, from,
+       return part->master->write_user_prot_reg(part->master, from,
                                        len, retlen, buf);
 }
 
-static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len)
+static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
+               size_t len)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->lock_user_prot_reg (part->master, from, len);
+       return part->master->lock_user_prot_reg(part->master, from, len);
 }
 
-static int part_writev (struct mtd_info *mtd,  const struct kvec *vecs,
-                        unsigned long count, loff_t to, size_t *retlen)
+static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
+               unsigned long count, loff_t to, size_t *retlen)
 {
        struct mtd_part *part = PART(mtd);
        if (!(mtd->flags & MTD_WRITEABLE))
                return -EROFS;
-       return part->master->writev (part->master, vecs, count,
+       return part->master->writev(part->master, vecs, count,
                                        to + part->offset, retlen);
 }
 
-static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
+static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct mtd_part *part = PART(mtd);
        int ret;
@@ -236,7 +235,7 @@ void mtd_erase_callback(struct erase_info *instr)
 }
 EXPORT_SYMBOL_GPL(mtd_erase_callback);
 
-static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
+static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
        struct mtd_part *part = PART(mtd);
        if ((len + ofs) > mtd->size)
@@ -244,7 +243,7 @@ static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
        return part->master->lock(part->master, ofs + part->offset, len);
 }
 
-static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
+static int part_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
        struct mtd_part *part = PART(mtd);
        if ((len + ofs) > mtd->size)
@@ -270,7 +269,7 @@ static void part_resume(struct mtd_info *mtd)
        part->master->resume(part->master);
 }
 
-static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
+static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
        struct mtd_part *part = PART(mtd);
        if (ofs >= mtd->size)
@@ -279,7 +278,7 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
        return part->master->block_isbad(part->master, ofs);
 }
 
-static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
+static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
        struct mtd_part *part = PART(mtd);
        int res;
@@ -302,229 +301,237 @@ static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
 
 int del_mtd_partitions(struct mtd_info *master)
 {
-       struct list_head *node;
-       struct mtd_part *slave;
+       struct mtd_part *slave, *next;
 
-       for (node = mtd_partitions.next;
-            node != &mtd_partitions;
-            node = node->next) {
-               slave = list_entry(node, struct mtd_part, list);
+       list_for_each_entry_safe(slave, next, &mtd_partitions, list)
                if (slave->master == master) {
-                       struct list_head *prev = node->prev;
-                       __list_del(prev, node->next);
-                       if(slave->registered)
+                       list_del(&slave->list);
+                       if (slave->registered)
                                del_mtd_device(&slave->mtd);
                        kfree(slave);
-                       node = prev;
                }
-       }
 
        return 0;
 }
+EXPORT_SYMBOL(del_mtd_partitions);
 
-/*
- * This function, given a master MTD object and a partition table, creates
- * and registers slave MTD objects which are bound to the master according to
- * the partition definitions.
- * (Q: should we register the master MTD object as well?)
- */
-
-int add_mtd_partitions(struct mtd_info *master,
-                      const struct mtd_partition *parts,
-                      int nbparts)
+static struct mtd_part *add_one_partition(struct mtd_info *master,
+               const struct mtd_partition *part, int partno,
+               u_int32_t cur_offset)
 {
        struct mtd_part *slave;
-       u_int32_t cur_offset = 0;
-       int i;
-
-       printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
-
-       for (i = 0; i < nbparts; i++) {
 
-               /* allocate the partition structure */
-               slave = kzalloc (sizeof(*slave), GFP_KERNEL);
-               if (!slave) {
-                       printk ("memory allocation error while creating partitions for \"%s\"\n",
-                               master->name);
-                       del_mtd_partitions(master);
-                       return -ENOMEM;
-               }
-               list_add(&slave->list, &mtd_partitions);
+       /* allocate the partition structure */
+       slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+       if (!slave) {
+               printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
+                       master->name);
+               del_mtd_partitions(master);
+               return NULL;
+       }
+       list_add(&slave->list, &mtd_partitions);
 
-               /* set up the MTD object for this partition */
-               slave->mtd.type = master->type;
-               slave->mtd.flags = master->flags & ~parts[i].mask_flags;
-               slave->mtd.size = parts[i].size;
-               slave->mtd.writesize = master->writesize;
-               slave->mtd.oobsize = master->oobsize;
-               slave->mtd.oobavail = master->oobavail;
-               slave->mtd.subpage_sft = master->subpage_sft;
+       /* set up the MTD object for this partition */
+       slave->mtd.type = master->type;
+       slave->mtd.flags = master->flags & ~part->mask_flags;
+       slave->mtd.size = part->size;
+       slave->mtd.writesize = master->writesize;
+       slave->mtd.oobsize = master->oobsize;
+       slave->mtd.oobavail = master->oobavail;
+       slave->mtd.subpage_sft = master->subpage_sft;
 
-               slave->mtd.name = parts[i].name;
-               slave->mtd.owner = master->owner;
+       slave->mtd.name = part->name;
+       slave->mtd.owner = master->owner;
 
-               slave->mtd.read = part_read;
-               slave->mtd.write = part_write;
+       slave->mtd.read = part_read;
+       slave->mtd.write = part_write;
 
-               if (master->panic_write)
-                       slave->mtd.panic_write = part_panic_write;
+       if (master->panic_write)
+               slave->mtd.panic_write = part_panic_write;
 
-               if(master->point && master->unpoint){
-                       slave->mtd.point = part_point;
-                       slave->mtd.unpoint = part_unpoint;
-               }
+       if (master->point && master->unpoint) {
+               slave->mtd.point = part_point;
+               slave->mtd.unpoint = part_unpoint;
+       }
 
-               if (master->read_oob)
-                       slave->mtd.read_oob = part_read_oob;
-               if (master->write_oob)
-                       slave->mtd.write_oob = part_write_oob;
-               if(master->read_user_prot_reg)
-                       slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
-               if(master->read_fact_prot_reg)
-                       slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
-               if(master->write_user_prot_reg)
-                       slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
-               if(master->lock_user_prot_reg)
-                       slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
-               if(master->get_user_prot_info)
-                       slave->mtd.get_user_prot_info = part_get_user_prot_info;
-               if(master->get_fact_prot_info)
-                       slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
-               if (master->sync)
-                       slave->mtd.sync = part_sync;
-               if (!i && master->suspend && master->resume) {
-                               slave->mtd.suspend = part_suspend;
-                               slave->mtd.resume = part_resume;
+       if (master->read_oob)
+               slave->mtd.read_oob = part_read_oob;
+       if (master->write_oob)
+               slave->mtd.write_oob = part_write_oob;
+       if (master->read_user_prot_reg)
+               slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
+       if (master->read_fact_prot_reg)
+               slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
+       if (master->write_user_prot_reg)
+               slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
+       if (master->lock_user_prot_reg)
+               slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
+       if (master->get_user_prot_info)
+               slave->mtd.get_user_prot_info = part_get_user_prot_info;
+       if (master->get_fact_prot_info)
+               slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
+       if (master->sync)
+               slave->mtd.sync = part_sync;
+       if (!partno && master->suspend && master->resume) {
+                       slave->mtd.suspend = part_suspend;
+                       slave->mtd.resume = part_resume;
+       }
+       if (master->writev)
+               slave->mtd.writev = part_writev;
+       if (master->lock)
+               slave->mtd.lock = part_lock;
+       if (master->unlock)
+               slave->mtd.unlock = part_unlock;
+       if (master->block_isbad)
+               slave->mtd.block_isbad = part_block_isbad;
+       if (master->block_markbad)
+               slave->mtd.block_markbad = part_block_markbad;
+       slave->mtd.erase = part_erase;
+       slave->master = master;
+       slave->offset = part->offset;
+       slave->index = partno;
+
+       if (slave->offset == MTDPART_OFS_APPEND)
+               slave->offset = cur_offset;
+       if (slave->offset == MTDPART_OFS_NXTBLK) {
+               slave->offset = cur_offset;
+               if ((cur_offset % master->erasesize) != 0) {
+                       /* Round up to next erasesize */
+                       slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize;
+                       printk(KERN_NOTICE "Moving partition %d: "
+                              "0x%08x -> 0x%08x\n", partno,
+                              cur_offset, slave->offset);
                }
-               if (master->writev)
-                       slave->mtd.writev = part_writev;
-               if (master->lock)
-                       slave->mtd.lock = part_lock;
-               if (master->unlock)
-                       slave->mtd.unlock = part_unlock;
-               if (master->block_isbad)
-                       slave->mtd.block_isbad = part_block_isbad;
-               if (master->block_markbad)
-                       slave->mtd.block_markbad = part_block_markbad;
-               slave->mtd.erase = part_erase;
-               slave->master = master;
-               slave->offset = parts[i].offset;
-               slave->index = i;
-
-               if (slave->offset == MTDPART_OFS_APPEND)
-                       slave->offset = cur_offset;
-               if (slave->offset == MTDPART_OFS_NXTBLK) {
-                       slave->offset = cur_offset;
-                       if ((cur_offset % master->erasesize) != 0) {
-                               /* Round up to next erasesize */
-                               slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize;
-                               printk(KERN_NOTICE "Moving partition %d: "
-                                      "0x%08x -> 0x%08x\n", i,
-                                      cur_offset, slave->offset);
+       }
+       if (slave->mtd.size == MTDPART_SIZ_FULL)
+               slave->mtd.size = master->size - slave->offset;
+
+       printk(KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
+               slave->offset + slave->mtd.size, slave->mtd.name);
+
+       /* let's do some sanity checks */
+       if (slave->offset >= master->size) {
+               /* let's register it anyway to preserve ordering */
+               slave->offset = 0;
+               slave->mtd.size = 0;
+               printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
+                       part->name);
+               goto out_register;
+       }
+       if (slave->offset + slave->mtd.size > master->size) {
+               slave->mtd.size = master->size - slave->offset;
+               printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
+                       part->name, master->name, slave->mtd.size);
+       }
+       if (master->numeraseregions > 1) {
+               /* Deal with variable erase size stuff */
+               int i, max = master->numeraseregions;
+               u32 end = slave->offset + slave->mtd.size;
+               struct mtd_erase_region_info *regions = master->eraseregions;
+
+               /* Find the first erase regions which is part of this
+                * partition. */
+               for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
+                       ;
+               /* The loop searched for the region _behind_ the first one */
+               i--;
+
+               /* Pick biggest erasesize */
+               for (; i < max && regions[i].offset < end; i++) {
+                       if (slave->mtd.erasesize < regions[i].erasesize) {
+                               slave->mtd.erasesize = regions[i].erasesize;
                        }
                }
-               if (slave->mtd.size == MTDPART_SIZ_FULL)
-                       slave->mtd.size = master->size - slave->offset;
-               cur_offset = slave->offset + slave->mtd.size;
+               BUG_ON(slave->mtd.erasesize == 0);
+       } else {
+               /* Single erase size */
+               slave->mtd.erasesize = master->erasesize;
+       }
 
-               printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
-                       slave->offset + slave->mtd.size, slave->mtd.name);
+       if ((slave->mtd.flags & MTD_WRITEABLE) &&
+           (slave->offset % slave->mtd.erasesize)) {
+               /* Doesn't start on a boundary of major erase size */
+               /* FIXME: Let it be writable if it is on a boundary of
+                * _minor_ erase size though */
+               slave->mtd.flags &= ~MTD_WRITEABLE;
+               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+                       part->name);
+       }
+       if ((slave->mtd.flags & MTD_WRITEABLE) &&
+           (slave->mtd.size % slave->mtd.erasesize)) {
+               slave->mtd.flags &= ~MTD_WRITEABLE;
+               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+                       part->name);
+       }
 
-               /* let's do some sanity checks */
-               if (slave->offset >= master->size) {
-                               /* let's register it anyway to preserve ordering */
-                       slave->offset = 0;
-                       slave->mtd.size = 0;
-                       printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
-                               parts[i].name);
-               }
-               if (slave->offset + slave->mtd.size > master->size) {
-                       slave->mtd.size = master->size - slave->offset;
-                       printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
-                               parts[i].name, master->name, slave->mtd.size);
-               }
-               if (master->numeraseregions>1) {
-                       /* Deal with variable erase size stuff */
-                       int i;
-                       struct mtd_erase_region_info *regions = master->eraseregions;
-
-                       /* Find the first erase regions which is part of this partition. */
-                       for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
-                               ;
-
-                       for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {
-                               if (slave->mtd.erasesize < regions[i].erasesize) {
-                                       slave->mtd.erasesize = regions[i].erasesize;
-                               }
-                       }
-               } else {
-                       /* Single erase size */
-                       slave->mtd.erasesize = master->erasesize;
-               }
+       slave->mtd.ecclayout = master->ecclayout;
+       if (master->block_isbad) {
+               uint32_t offs = 0;
 
-               if ((slave->mtd.flags & MTD_WRITEABLE) &&
-                   (slave->offset % slave->mtd.erasesize)) {
-                       /* Doesn't start on a boundary of major erase size */
-                       /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
-                       slave->mtd.flags &= ~MTD_WRITEABLE;
-                       printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
-                               parts[i].name);
-               }
-               if ((slave->mtd.flags & MTD_WRITEABLE) &&
-                   (slave->mtd.size % slave->mtd.erasesize)) {
-                       slave->mtd.flags &= ~MTD_WRITEABLE;
-                       printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
-                               parts[i].name);
+               while (offs < slave->mtd.size) {
+                       if (master->block_isbad(master,
+                                               offs + slave->offset))
+                               slave->mtd.ecc_stats.badblocks++;
+                       offs += slave->mtd.erasesize;
                }
+       }
 
-               slave->mtd.ecclayout = master->ecclayout;
-               if (master->block_isbad) {
-                       uint32_t offs = 0;
+out_register:
+       if (part->mtdp) {
+               /* store the object pointer (caller may or may not register it*/
+               *part->mtdp = &slave->mtd;
+               slave->registered = 0;
+       } else {
+               /* register our partition */
+               add_mtd_device(&slave->mtd);
+               slave->registered = 1;
+       }
+       return slave;
+}
 
-                       while(offs < slave->mtd.size) {
-                               if (master->block_isbad(master,
-                                                       offs + slave->offset))
-                                       slave->mtd.ecc_stats.badblocks++;
-                               offs += slave->mtd.erasesize;
-                       }
-               }
+/*
+ * This function, given a master MTD object and a partition table, creates
+ * and registers slave MTD objects which are bound to the master according to
+ * the partition definitions.
+ * (Q: should we register the master MTD object as well?)
+ */
 
-               if(parts[i].mtdp)
-               {       /* store the object pointer (caller may or may not register it */
-                       *parts[i].mtdp = &slave->mtd;
-                       slave->registered = 0;
-               }
-               else
-               {
-                       /* register our partition */
-                       add_mtd_device(&slave->mtd);
-                       slave->registered = 1;
-               }
+int add_mtd_partitions(struct mtd_info *master,
+                      const struct mtd_partition *parts,
+                      int nbparts)
+{
+       struct mtd_part *slave;
+       u_int32_t cur_offset = 0;
+       int i;
+
+       printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
+
+       for (i = 0; i < nbparts; i++) {
+               slave = add_one_partition(master, parts + i, i, cur_offset);
+               if (!slave)
+                       return -ENOMEM;
+               cur_offset = slave->offset + slave->mtd.size;
        }
 
        return 0;
 }
-
 EXPORT_SYMBOL(add_mtd_partitions);
-EXPORT_SYMBOL(del_mtd_partitions);
 
 static DEFINE_SPINLOCK(part_parser_lock);
 static LIST_HEAD(part_parsers);
 
 static struct mtd_part_parser *get_partition_parser(const char *name)
 {
-       struct list_head *this;
-       void *ret = NULL;
-       spin_lock(&part_parser_lock);
+       struct mtd_part_parser *p, *ret = NULL;
 
-       list_for_each(this, &part_parsers) {
-               struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list);
+       spin_lock(&part_parser_lock);
 
+       list_for_each_entry(p, &part_parsers, list)
                if (!strcmp(p->name, name) && try_module_get(p->owner)) {
                        ret = p;
                        break;
                }
-       }
+
        spin_unlock(&part_parser_lock);
 
        return ret;
@@ -538,6 +545,7 @@ int register_mtd_parser(struct mtd_part_parser *p)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(register_mtd_parser);
 
 int deregister_mtd_parser(struct mtd_part_parser *p)
 {
@@ -546,6 +554,7 @@ int deregister_mtd_parser(struct mtd_part_parser *p)
        spin_unlock(&part_parser_lock);
        return 0;
 }
+EXPORT_SYMBOL_GPL(deregister_mtd_parser);
 
 int parse_mtd_partitions(struct mtd_info *master, const char **types,
                         struct mtd_partition **pparts, unsigned long origin)
@@ -573,7 +582,4 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
        }
        return ret;
 }
-
 EXPORT_SYMBOL_GPL(parse_mtd_partitions);
-EXPORT_SYMBOL_GPL(register_mtd_parser);
-EXPORT_SYMBOL_GPL(deregister_mtd_parser);
index 5076faf9ca66c54ae300d960f5befccbfc01fb55..71406e517857c456384ddd95ef51633119fe562a 100644 (file)
@@ -1,5 +1,4 @@
 # drivers/mtd/nand/Kconfig
-# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $
 
 menuconfig MTD_NAND
        tristate "NAND Device Support"
@@ -272,22 +271,23 @@ config MTD_NAND_CS553X
 
          If you say "m", the module will be called "cs553x_nand.ko".
 
-config MTD_NAND_AT91
-       bool "Support for NAND Flash / SmartMedia on AT91"
-       depends on ARCH_AT91
+config MTD_NAND_ATMEL
+       tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32"
+       depends on ARCH_AT91 || AVR32
        help
          Enables support for NAND Flash / Smart Media Card interface
-         on Atmel AT91 processors.
+         on Atmel AT91 and AVR32 processors.
 choice
-       prompt "ECC management for NAND Flash / SmartMedia on AT91"
-       depends on MTD_NAND_AT91
+       prompt "ECC management for NAND Flash / SmartMedia on AT91 / AVR32"
+       depends on MTD_NAND_ATMEL
 
-config MTD_NAND_AT91_ECC_HW
+config MTD_NAND_ATMEL_ECC_HW
        bool "Hardware ECC"
-       depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260
+       depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 || AVR32
        help
-         Uses hardware ECC provided by the at91sam9260/at91sam9263 chip
-         instead of software ECC.
+         Use hardware ECC instead of software ECC when the chip
+         supports it.
+
          The hardware ECC controller is capable of single bit error
          correction and 2-bit random detection per page.
 
@@ -297,16 +297,16 @@ config MTD_NAND_AT91_ECC_HW
 
          If unsure, say Y
 
-config MTD_NAND_AT91_ECC_SOFT
+config MTD_NAND_ATMEL_ECC_SOFT
        bool "Software ECC"
        help
-         Uses software ECC.
+         Use software ECC.
 
          NB : hardware and software ECC schemes are incompatible.
          If you switch from one to another, you'll have to erase your
          mtd partition.
 
-config MTD_NAND_AT91_ECC_NONE
+config MTD_NAND_ATMEL_ECC_NONE
        bool "No ECC (testing only, DANGEROUS)"
        depends on DEBUG_KERNEL
        help
index a6e74a46992a7bde250a5ca7a5d9b20eef96ea00..d772581de573cf4281e64cf11598e234ca2dd6e7 100644 (file)
@@ -1,7 +1,6 @@
 #
 # linux/drivers/nand/Makefile
 #
-# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $
 
 obj-$(CONFIG_MTD_NAND)                 += nand.o nand_ecc.o
 obj-$(CONFIG_MTD_NAND_IDS)             += nand_ids.o
@@ -24,7 +23,7 @@ obj-$(CONFIG_MTD_NAND_TS7250)         += ts7250.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)         += nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)          += cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
-obj-$(CONFIG_MTD_NAND_AT91)            += at91_nand.o
+obj-$(CONFIG_MTD_NAND_ATMEL)           += atmel_nand.o
 obj-$(CONFIG_MTD_NAND_CM_X270)         += cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)   += excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PXA3xx)          += pxa3xx_nand.o
similarity index 62%
rename from drivers/mtd/nand/at91_nand.c
rename to drivers/mtd/nand/atmel_nand.c
index 0adb287027a249409062c5ba6a46cadc717f50a8..99aec46e21450565e3e323adccfcb1f077597511 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * drivers/mtd/nand/at91_nand.c
- *
  *  Copyright (C) 2003 Rick Bronson
  *
  *  Derived from drivers/mtd/nand/autcpu12.c
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 
-#include <asm/io.h>
-#include <asm/sizes.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
 
-#include <asm/hardware.h>
 #include <asm/arch/board.h>
-#include <asm/arch/gpio.h>
+#include <asm/arch/cpu.h>
 
-#ifdef CONFIG_MTD_NAND_AT91_ECC_HW
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW
 #define hard_ecc       1
 #else
 #define hard_ecc       0
 #endif
 
-#ifdef CONFIG_MTD_NAND_AT91_ECC_NONE
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE
 #define no_ecc         1
 #else
 #define no_ecc         0
 
 /* Register access macros */
 #define ecc_readl(add, reg)                            \
-       __raw_readl(add + AT91_ECC_##reg)
+       __raw_readl(add + ATMEL_ECC_##reg)
 #define ecc_writel(add, reg, value)                    \
-       __raw_writel((value), add + AT91_ECC_##reg)
+       __raw_writel((value), add + ATMEL_ECC_##reg)
 
-#include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 ECC registers */
+#include "atmel_nand_ecc.h"    /* Hardware ECC registers */
 
 /* oob layout for large page size
  * bad block info is on bytes 0 and 1
  * the bytes have to be consecutives to avoid
  * several NAND_CMD_RNDOUT during read
  */
-static struct nand_ecclayout at91_oobinfo_large = {
+static struct nand_ecclayout atmel_oobinfo_large = {
        .eccbytes = 4,
        .eccpos = {60, 61, 62, 63},
        .oobfree = {
@@ -76,7 +73,7 @@ static struct nand_ecclayout at91_oobinfo_large = {
  * the bytes have to be consecutives to avoid
  * several NAND_CMD_RNDOUT during read
  */
-static struct nand_ecclayout at91_oobinfo_small = {
+static struct nand_ecclayout atmel_oobinfo_small = {
        .eccbytes = 4,
        .eccpos = {0, 1, 2, 3},
        .oobfree = {
@@ -84,11 +81,11 @@ static struct nand_ecclayout at91_oobinfo_small = {
        },
 };
 
-struct at91_nand_host {
+struct atmel_nand_host {
        struct nand_chip        nand_chip;
        struct mtd_info         mtd;
        void __iomem            *io_base;
-       struct at91_nand_data   *board;
+       struct atmel_nand_data  *board;
        struct device           *dev;
        void __iomem            *ecc;
 };
@@ -96,34 +93,34 @@ struct at91_nand_host {
 /*
  * Enable NAND.
  */
-static void at91_nand_enable(struct at91_nand_host *host)
+static void atmel_nand_enable(struct atmel_nand_host *host)
 {
        if (host->board->enable_pin)
-               at91_set_gpio_value(host->board->enable_pin, 0);
+               gpio_set_value(host->board->enable_pin, 0);
 }
 
 /*
  * Disable NAND.
  */
-static void at91_nand_disable(struct at91_nand_host *host)
+static void atmel_nand_disable(struct atmel_nand_host *host)
 {
        if (host->board->enable_pin)
-               at91_set_gpio_value(host->board->enable_pin, 1);
+               gpio_set_value(host->board->enable_pin, 1);
 }
 
 /*
  * Hardware specific access to control-lines
  */
-static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
        struct nand_chip *nand_chip = mtd->priv;
-       struct at91_nand_host *host = nand_chip->priv;
+       struct atmel_nand_host *host = nand_chip->priv;
 
        if (ctrl & NAND_CTRL_CHANGE) {
                if (ctrl & NAND_NCE)
-                       at91_nand_enable(host);
+                       atmel_nand_enable(host);
                else
-                       at91_nand_disable(host);
+                       atmel_nand_disable(host);
        }
        if (cmd == NAND_CMD_NONE)
                return;
@@ -137,18 +134,49 @@ static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 /*
  * Read the Device Ready pin.
  */
-static int at91_nand_device_ready(struct mtd_info *mtd)
+static int atmel_nand_device_ready(struct mtd_info *mtd)
 {
        struct nand_chip *nand_chip = mtd->priv;
-       struct at91_nand_host *host = nand_chip->priv;
+       struct atmel_nand_host *host = nand_chip->priv;
 
-       return at91_get_gpio_value(host->board->rdy_pin);
+       return gpio_get_value(host->board->rdy_pin);
+}
+
+/*
+ * Minimal-overhead PIO for data access.
+ */
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
+}
+
+static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_writesb(nand_chip->IO_ADDR_W, buf, len);
+}
+
+static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
+{
+       struct nand_chip        *nand_chip = mtd->priv;
+
+       __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
 }
 
 /*
  * write oob for small pages
  */
-static int at91_nand_write_oob_512(struct mtd_info *mtd,
+static int atmel_nand_write_oob_512(struct mtd_info *mtd,
                struct nand_chip *chip, int page)
 {
        int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
@@ -176,7 +204,7 @@ static int at91_nand_write_oob_512(struct mtd_info *mtd,
 /*
  * read oob for small pages
  */
-static int at91_nand_read_oob_512(struct mtd_info *mtd,
+static int atmel_nand_read_oob_512(struct mtd_info *mtd,
                struct nand_chip *chip, int page, int sndcmd)
 {
        if (sndcmd) {
@@ -196,11 +224,11 @@ static int at91_nand_read_oob_512(struct mtd_info *mtd,
  * dat:        raw data (unused)
  * ecc_code:   buffer for ECC
  */
-static int at91_nand_calculate(struct mtd_info *mtd,
+static int atmel_nand_calculate(struct mtd_info *mtd,
                const u_char *dat, unsigned char *ecc_code)
 {
        struct nand_chip *nand_chip = mtd->priv;
-       struct at91_nand_host *host = nand_chip->priv;
+       struct atmel_nand_host *host = nand_chip->priv;
        uint32_t *eccpos = nand_chip->ecc.layout->eccpos;
        unsigned int ecc_value;
 
@@ -211,7 +239,7 @@ static int at91_nand_calculate(struct mtd_info *mtd,
        ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF;
 
        /* get the last 2 ECC bytes */
-       ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY;
+       ecc_value = ecc_readl(host->ecc, NPR) & ATMEL_ECC_NPARITY;
 
        ecc_code[eccpos[2]] = ecc_value & 0xFF;
        ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF;
@@ -226,7 +254,7 @@ static int at91_nand_calculate(struct mtd_info *mtd,
  * chip:       nand chip info structure
  * buf:        buffer to store read data
  */
-static int at91_nand_read_page(struct mtd_info *mtd,
+static int atmel_nand_read_page(struct mtd_info *mtd,
                struct nand_chip *chip, uint8_t *buf)
 {
        int eccsize = chip->ecc.size;
@@ -237,6 +265,19 @@ static int at91_nand_read_page(struct mtd_info *mtd,
        uint8_t *ecc_pos;
        int stat;
 
+       /*
+        * Errata: ALE is incorrectly wired up to the ECC controller
+        * on the AP7000, so it will include the address cycles in the
+        * ECC calculation.
+        *
+        * Workaround: Reset the parity registers before reading the
+        * actual data.
+        */
+       if (cpu_is_at32ap7000()) {
+               struct atmel_nand_host *host = chip->priv;
+               ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
+       }
+
        /* read the page */
        chip->read_buf(mtd, p, eccsize);
 
@@ -285,11 +326,11 @@ static int at91_nand_read_page(struct mtd_info *mtd,
  *
  * Detect and correct a 1 bit error for a page
  */
-static int at91_nand_correct(struct mtd_info *mtd, u_char *dat,
+static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
                u_char *read_ecc, u_char *isnull)
 {
        struct nand_chip *nand_chip = mtd->priv;
-       struct at91_nand_host *host = nand_chip->priv;
+       struct atmel_nand_host *host = nand_chip->priv;
        unsigned int ecc_status;
        unsigned int ecc_word, ecc_bit;
 
@@ -297,43 +338,43 @@ static int at91_nand_correct(struct mtd_info *mtd, u_char *dat,
        ecc_status = ecc_readl(host->ecc, SR);
 
        /* if there's no error */
-       if (likely(!(ecc_status & AT91_ECC_RECERR)))
+       if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
                return 0;
 
        /* get error bit offset (4 bits) */
-       ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR;
+       ecc_bit = ecc_readl(host->ecc, PR) & ATMEL_ECC_BITADDR;
        /* get word address (12 bits) */
-       ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR;
+       ecc_word = ecc_readl(host->ecc, PR) & ATMEL_ECC_WORDADDR;
        ecc_word >>= 4;
 
        /* if there are multiple errors */
-       if (ecc_status & AT91_ECC_MULERR) {
+       if (ecc_status & ATMEL_ECC_MULERR) {
                /* check if it is a freshly erased block
                 * (filled with 0xff) */
-               if ((ecc_bit == AT91_ECC_BITADDR)
-                               && (ecc_word == (AT91_ECC_WORDADDR >> 4))) {
+               if ((ecc_bit == ATMEL_ECC_BITADDR)
+                               && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
                        /* the block has just been erased, return OK */
                        return 0;
                }
                /* it doesn't seems to be a freshly
                 * erased block.
                 * We can't correct so many errors */
-               dev_dbg(host->dev, "at91_nand : multiple errors detected."
+               dev_dbg(host->dev, "atmel_nand : multiple errors detected."
                                " Unable to correct.\n");
                return -EIO;
        }
 
        /* if there's a single bit error : we can correct it */
-       if (ecc_status & AT91_ECC_ECCERR) {
+       if (ecc_status & ATMEL_ECC_ECCERR) {
                /* there's nothing much to do here.
                 * the bit error is on the ECC itself.
                 */
-               dev_dbg(host->dev, "at91_nand : one bit error on ECC code."
+               dev_dbg(host->dev, "atmel_nand : one bit error on ECC code."
                                " Nothing to correct\n");
                return 0;
        }
 
-       dev_dbg(host->dev, "at91_nand : one bit error on data."
+       dev_dbg(host->dev, "atmel_nand : one bit error on data."
                        " (word offset in the page :"
                        " 0x%x bit offset : 0x%x)\n",
                        ecc_word, ecc_bit);
@@ -345,14 +386,21 @@ static int at91_nand_correct(struct mtd_info *mtd, u_char *dat,
                /* 8 bits words */
                dat[ecc_word] ^= (1 << ecc_bit);
        }
-       dev_dbg(host->dev, "at91_nand : error corrected\n");
+       dev_dbg(host->dev, "atmel_nand : error corrected\n");
        return 1;
 }
 
 /*
- * Enable HW ECC : unsused
+ * Enable HW ECC : unused on most chips
  */
-static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; }
+static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
+{
+       if (cpu_is_at32ap7000()) {
+               struct nand_chip *nand_chip = mtd->priv;
+               struct atmel_nand_host *host = nand_chip->priv;
+               ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
+       }
+}
 
 #ifdef CONFIG_MTD_PARTITIONS
 static const char *part_probes[] = { "cmdlinepart", NULL };
@@ -361,9 +409,9 @@ static const char *part_probes[] = { "cmdlinepart", NULL };
 /*
  * Probe for the NAND device.
  */
-static int __init at91_nand_probe(struct platform_device *pdev)
+static int __init atmel_nand_probe(struct platform_device *pdev)
 {
-       struct at91_nand_host *host;
+       struct atmel_nand_host *host;
        struct mtd_info *mtd;
        struct nand_chip *nand_chip;
        struct resource *regs;
@@ -375,24 +423,24 @@ static int __init at91_nand_probe(struct platform_device *pdev)
        int num_partitions = 0;
 #endif
 
-       /* Allocate memory for the device structure (and zero it) */
-       host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL);
-       if (!host) {
-               printk(KERN_ERR "at91_nand: failed to allocate device structure.\n");
-               return -ENOMEM;
-       }
-
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!mem) {
-               printk(KERN_ERR "at91_nand: can't get I/O resource mem\n");
+               printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n");
                return -ENXIO;
        }
 
+       /* Allocate memory for the device structure (and zero it) */
+       host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL);
+       if (!host) {
+               printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n");
+               return -ENOMEM;
+       }
+
        host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
        if (host->io_base == NULL) {
-               printk(KERN_ERR "at91_nand: ioremap failed\n");
-               kfree(host);
-               return -EIO;
+               printk(KERN_ERR "atmel_nand: ioremap failed\n");
+               res = -EIO;
+               goto err_nand_ioremap;
        }
 
        mtd = &host->mtd;
@@ -407,14 +455,14 @@ static int __init at91_nand_probe(struct platform_device *pdev)
        /* Set address of NAND IO lines */
        nand_chip->IO_ADDR_R = host->io_base;
        nand_chip->IO_ADDR_W = host->io_base;
-       nand_chip->cmd_ctrl = at91_nand_cmd_ctrl;
+       nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
 
        if (host->board->rdy_pin)
-               nand_chip->dev_ready = at91_nand_device_ready;
+               nand_chip->dev_ready = atmel_nand_device_ready;
 
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        if (!regs && hard_ecc) {
-               printk(KERN_ERR "at91_nand: can't get I/O resource "
+               printk(KERN_ERR "atmel_nand: can't get I/O resource "
                                "regs\nFalling back on software ECC\n");
        }
 
@@ -424,15 +472,15 @@ static int __init at91_nand_probe(struct platform_device *pdev)
        if (hard_ecc && regs) {
                host->ecc = ioremap(regs->start, regs->end - regs->start + 1);
                if (host->ecc == NULL) {
-                       printk(KERN_ERR "at91_nand: ioremap failed\n");
+                       printk(KERN_ERR "atmel_nand: ioremap failed\n");
                        res = -EIO;
                        goto err_ecc_ioremap;
                }
                nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME;
-               nand_chip->ecc.calculate = at91_nand_calculate;
-               nand_chip->ecc.correct = at91_nand_correct;
-               nand_chip->ecc.hwctl = at91_nand_hwctl;
-               nand_chip->ecc.read_page = at91_nand_read_page;
+               nand_chip->ecc.calculate = atmel_nand_calculate;
+               nand_chip->ecc.correct = atmel_nand_correct;
+               nand_chip->ecc.hwctl = atmel_nand_hwctl;
+               nand_chip->ecc.read_page = atmel_nand_read_page;
                nand_chip->ecc.bytes = 4;
                nand_chip->ecc.prepad = 0;
                nand_chip->ecc.postpad = 0;
@@ -440,24 +488,30 @@ static int __init at91_nand_probe(struct platform_device *pdev)
 
        nand_chip->chip_delay = 20;             /* 20us command delay time */
 
-       if (host->board->bus_width_16)          /* 16-bit bus width */
+       if (host->board->bus_width_16) {        /* 16-bit bus width */
                nand_chip->options |= NAND_BUSWIDTH_16;
+               nand_chip->read_buf = atmel_read_buf16;
+               nand_chip->write_buf = atmel_write_buf16;
+       } else {
+               nand_chip->read_buf = atmel_read_buf;
+               nand_chip->write_buf = atmel_write_buf;
+       }
 
        platform_set_drvdata(pdev, host);
-       at91_nand_enable(host);
+       atmel_nand_enable(host);
 
        if (host->board->det_pin) {
-               if (at91_get_gpio_value(host->board->det_pin)) {
-                       printk ("No SmartMedia card inserted.\n");
+               if (gpio_get_value(host->board->det_pin)) {
+                       printk("No SmartMedia card inserted.\n");
                        res = ENXIO;
-                       goto out;
+                       goto err_no_card;
                }
        }
 
        /* first scan to find the device and get the page size */
        if (nand_scan_ident(mtd, 1)) {
                res = -ENXIO;
-               goto out;
+               goto err_scan_ident;
        }
 
        if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
@@ -467,22 +521,22 @@ static int __init at91_nand_probe(struct platform_device *pdev)
                /* set ECC page size and oob layout */
                switch (mtd->writesize) {
                case 512:
-                       nand_chip->ecc.layout = &at91_oobinfo_small;
-                       nand_chip->ecc.read_oob = at91_nand_read_oob_512;
-                       nand_chip->ecc.write_oob = at91_nand_write_oob_512;
-                       ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528);
+                       nand_chip->ecc.layout = &atmel_oobinfo_small;
+                       nand_chip->ecc.read_oob = atmel_nand_read_oob_512;
+                       nand_chip->ecc.write_oob = atmel_nand_write_oob_512;
+                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
                        break;
                case 1024:
-                       nand_chip->ecc.layout = &at91_oobinfo_large;
-                       ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056);
+                       nand_chip->ecc.layout = &atmel_oobinfo_large;
+                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
                        break;
                case 2048:
-                       nand_chip->ecc.layout = &at91_oobinfo_large;
-                       ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112);
+                       nand_chip->ecc.layout = &atmel_oobinfo_large;
+                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
                        break;
                case 4096:
-                       nand_chip->ecc.layout = &at91_oobinfo_large;
-                       ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224);
+                       nand_chip->ecc.layout = &atmel_oobinfo_large;
+                       ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
                        break;
                default:
                        /* page size not handled by HW ECC */
@@ -502,12 +556,12 @@ static int __init at91_nand_probe(struct platform_device *pdev)
        /* second phase scan */
        if (nand_scan_tail(mtd)) {
                res = -ENXIO;
-               goto out;
+               goto err_scan_tail;
        }
 
 #ifdef CONFIG_MTD_PARTITIONS
 #ifdef CONFIG_MTD_CMDLINE_PARTS
-       mtd->name = "at91_nand";
+       mtd->name = "atmel_nand";
        num_partitions = parse_mtd_partitions(mtd, part_probes,
                                              &partitions, 0);
 #endif
@@ -516,9 +570,9 @@ static int __init at91_nand_probe(struct platform_device *pdev)
                                                         &num_partitions);
 
        if ((!partitions) || (num_partitions == 0)) {
-               printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
+               printk(KERN_ERR "atmel_nand: No parititions defined, or unsupported device.\n");
                res = ENXIO;
-               goto release;
+               goto err_no_partitions;
        }
 
        res = add_mtd_partitions(mtd, partitions, num_partitions);
@@ -530,17 +584,19 @@ static int __init at91_nand_probe(struct platform_device *pdev)
                return res;
 
 #ifdef CONFIG_MTD_PARTITIONS
-release:
+err_no_partitions:
 #endif
        nand_release(mtd);
-
-out:
-       iounmap(host->ecc);
-
-err_ecc_ioremap:
-       at91_nand_disable(host);
+err_scan_tail:
+err_scan_ident:
+err_no_card:
+       atmel_nand_disable(host);
        platform_set_drvdata(pdev, NULL);
+       if (host->ecc)
+               iounmap(host->ecc);
+err_ecc_ioremap:
        iounmap(host->io_base);
+err_nand_ioremap:
        kfree(host);
        return res;
 }
@@ -548,47 +604,47 @@ err_ecc_ioremap:
 /*
  * Remove a NAND device.
  */
-static int __devexit at91_nand_remove(struct platform_device *pdev)
+static int __exit atmel_nand_remove(struct platform_device *pdev)
 {
-       struct at91_nand_host *host = platform_get_drvdata(pdev);
+       struct atmel_nand_host *host = platform_get_drvdata(pdev);
        struct mtd_info *mtd = &host->mtd;
 
        nand_release(mtd);
 
-       at91_nand_disable(host);
+       atmel_nand_disable(host);
 
+       if (host->ecc)
+               iounmap(host->ecc);
        iounmap(host->io_base);
-       iounmap(host->ecc);
        kfree(host);
 
        return 0;
 }
 
-static struct platform_driver at91_nand_driver = {
-       .probe          = at91_nand_probe,
-       .remove         = at91_nand_remove,
+static struct platform_driver atmel_nand_driver = {
+       .remove         = __exit_p(atmel_nand_remove),
        .driver         = {
-               .name   = "at91_nand",
+               .name   = "atmel_nand",
                .owner  = THIS_MODULE,
        },
 };
 
-static int __init at91_nand_init(void)
+static int __init atmel_nand_init(void)
 {
-       return platform_driver_register(&at91_nand_driver);
+       return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe);
 }
 
 
-static void __exit at91_nand_exit(void)
+static void __exit atmel_nand_exit(void)
 {
-       platform_driver_unregister(&at91_nand_driver);
+       platform_driver_unregister(&atmel_nand_driver);
 }
 
 
-module_init(at91_nand_init);
-module_exit(at91_nand_exit);
+module_init(atmel_nand_init);
+module_exit(atmel_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Rick Bronson");
-MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9");
-MODULE_ALIAS("platform:at91_nand");
+MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91 / AVR32");
+MODULE_ALIAS("platform:atmel_nand");
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
new file mode 100644 (file)
index 0000000..1ee7f99
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Error Corrected Code Controller (ECC) - System peripherals regsters.
+ * Based on AT91SAM9260 datasheet revision B.
+ *
+ * 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 ATMEL_NAND_ECC_H
+#define ATMEL_NAND_ECC_H
+
+#define ATMEL_ECC_CR           0x00                    /* Control register */
+#define                ATMEL_ECC_RST           (1 << 0)                /* Reset parity */
+
+#define ATMEL_ECC_MR           0x04                    /* Mode register */
+#define                ATMEL_ECC_PAGESIZE      (3 << 0)                /* Page Size */
+#define                        ATMEL_ECC_PAGESIZE_528          (0)
+#define                        ATMEL_ECC_PAGESIZE_1056         (1)
+#define                        ATMEL_ECC_PAGESIZE_2112         (2)
+#define                        ATMEL_ECC_PAGESIZE_4224         (3)
+
+#define ATMEL_ECC_SR           0x08                    /* Status register */
+#define                ATMEL_ECC_RECERR                (1 << 0)                /* Recoverable Error */
+#define                ATMEL_ECC_ECCERR                (1 << 1)                /* ECC Single Bit Error */
+#define                ATMEL_ECC_MULERR                (1 << 2)                /* Multiple Errors */
+
+#define ATMEL_ECC_PR           0x0c                    /* Parity register */
+#define                ATMEL_ECC_BITADDR       (0xf << 0)              /* Bit Error Address */
+#define                ATMEL_ECC_WORDADDR      (0xfff << 4)            /* Word Error Address */
+
+#define ATMEL_ECC_NPR          0x10                    /* NParity register */
+#define                ATMEL_ECC_NPARITY       (0xffff << 0)           /* NParity */
+
+#endif
index 09e421a968934828a95100aebb4a6df8c6e9de54..761946ea45b132da9dde13b1d3ea9b7935f53a8a 100644 (file)
@@ -3,8 +3,6 @@
  *
  *  Copyright (C) 2004 Embedded Edge, LLC
  *
- * $Id: au1550nd.c,v 1.13 2005/11/07 11:14:30 gleixner Exp $
- *
  * 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.
@@ -604,8 +602,6 @@ module_init(au1xxx_nand_init);
  */
 static void __exit au1550_cleanup(void)
 {
-       struct nand_chip *this = (struct nand_chip *)&au1550_mtd[1];
-
        /* Release resources, unregister device */
        nand_release(au1550_mtd);
 
index dd38011ee0b7326096973b694a3339467bc984f5..553dd7e9b41c983711898a3a0020d4647731efc5 100644 (file)
@@ -6,8 +6,6 @@
  *  Derived from drivers/mtd/spia.c
  *      Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
- * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $
- *
  * 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.
index da6ceaa80ba137c1c4114a199d59efb09bcf08ed..95345d05157922fdfedf21b6a7d9527215f40be3 100644 (file)
@@ -626,10 +626,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
 {
        struct mtd_info *mtd;
        struct cafe_priv *cafe;
-       struct mtd_partition *parts;
        uint32_t ctrl;
-       int nr_parts;
        int err = 0;
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts;
+       int nr_parts;
+#endif
 
        /* Very old versions shared the same PCI ident for all three
           functions on the chip. Verify the class too... */
index 0e72153b329736000e4c43abfc76038b725d5959..765d4f0f7c865c1682e94d11ba48fa6986c3d02c 100644 (file)
@@ -15,8 +15,6 @@
  * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
  *
  * Interface to generic NAND code for M-Systems DiskOnChip devices
- *
- * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $
  */
 
 #include <linux/kernel.h>
@@ -54,8 +52,6 @@ static unsigned long __initdata doc_locations[] = {
        0xe0000, 0xe2000, 0xe4000, 0xe6000,
        0xe8000, 0xea000, 0xec000, 0xee000,
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
-#elif defined(__PPC__)
-       0xe4000000,
 #else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
index ba67bbec20d3a4fbab3ac9733d05ee7cc3f61229..387e4352903e80af7807c155129ab131e9dfbacf 100644 (file)
@@ -6,8 +6,6 @@
  *  Derived from drivers/mtd/nand/autcpu12.c
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: edb7312.c,v 1.12 2005/11/07 11:14:30 gleixner Exp $
- *
  * 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.
index bed87290deccd3b07e120278d77f47c347e57517..ced14b5294d51dac99826cee87899e464abf5fe5 100644 (file)
@@ -209,7 +209,7 @@ static int __init excite_nand_probe(struct device *dev)
        if (likely(!scan_res)) {
                DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id);
                add_mtd_partitions(&drvdata->board_mtd, partition_info,
-                                  sizeof partition_info / sizeof partition_info[0]);
+                                  ARRAY_SIZE(partition_info));
        } else {
                iounmap(drvdata->regs);
                kfree(drvdata);
index 4b69aacdf5ca45410207dca1f8908413f166e1c1..9dff51351f4fba32559541ce50c175d48ef52828 100644 (file)
@@ -89,7 +89,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
        .eccbytes = 3,
        .eccpos = {6, 7, 8},
        .oobfree = { {0, 5}, {9, 7} },
-       .oobavail = 12,
 };
 
 /* Small Page FLASH with FMR[ECCM] = 1 */
@@ -97,7 +96,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
        .eccbytes = 3,
        .eccpos = {8, 9, 10},
        .oobfree = { {0, 5}, {6, 2}, {11, 5} },
-       .oobavail = 12,
 };
 
 /* Large Page FLASH with FMR[ECCM] = 0 */
@@ -105,7 +103,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
        .eccbytes = 12,
        .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
        .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
-       .oobavail = 48,
 };
 
 /* Large Page FLASH with FMR[ECCM] = 1 */
@@ -113,7 +110,48 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
        .eccbytes = 12,
        .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
        .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
-       .oobavail = 48,
+};
+
+/*
+ * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset
+ * 1, so we have to adjust bad block pattern. This pattern should be used for
+ * x8 chips only. So far hardware does not support x16 chips anyway.
+ */
+static u8 scan_ff_pattern[] = { 0xff, };
+
+static struct nand_bbt_descr largepage_memorybased = {
+       .options = 0,
+       .offs = 0,
+       .len = 1,
+       .pattern = scan_ff_pattern,
+};
+
+/*
+ * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
+ * interfere with ECC positions, that's why we implement our own descriptors.
+ * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+                  NAND_BBT_2BIT | NAND_BBT_VERSION,
+       .offs = 11,
+       .len = 4,
+       .veroffs = 15,
+       .maxblocks = 4,
+       .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+                  NAND_BBT_2BIT | NAND_BBT_VERSION,
+       .offs = 11,
+       .len = 4,
+       .veroffs = 15,
+       .maxblocks = 4,
+       .pattern = mirror_pattern,
 };
 
 /*=================================*/
@@ -687,8 +725,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
                        chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
                                           &fsl_elbc_oob_lp_eccm1 :
                                           &fsl_elbc_oob_lp_eccm0;
-                       mtd->ecclayout = chip->ecc.layout;
-                       mtd->oobavail = chip->ecc.layout->oobavail;
+                       chip->badblock_pattern = &largepage_memorybased;
                }
        } else {
                dev_err(ctrl->dev,
@@ -752,8 +789,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
        chip->cmdfunc = fsl_elbc_cmdfunc;
        chip->waitfunc = fsl_elbc_wait;
 
+       chip->bbt_td = &bbt_main_descr;
+       chip->bbt_md = &bbt_mirror_descr;
+
        /* set up nand options */
-       chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
+       chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
+                       NAND_USE_FLASH_BBT;
 
        chip->controller = &ctrl->controller;
        chip->priv = priv;
@@ -795,8 +836,8 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
        return 0;
 }
 
-static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
-                               struct device_node *node)
+static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
+                                        struct device_node *node)
 {
        struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
        struct fsl_elbc_mtd *priv;
@@ -917,7 +958,7 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
        return 0;
 }
 
-static int __devexit fsl_elbc_ctrl_remove(struct of_device *ofdev)
+static int fsl_elbc_ctrl_remove(struct of_device *ofdev)
 {
        struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
        int i;
@@ -1041,7 +1082,7 @@ static struct of_platform_driver fsl_elbc_ctrl_driver = {
        },
        .match_table = fsl_elbc_match,
        .probe = fsl_elbc_ctrl_probe,
-       .remove = __devexit_p(fsl_elbc_ctrl_remove),
+       .remove = fsl_elbc_ctrl_remove,
 };
 
 static int __init fsl_elbc_init(void)
index 2d585d2d090c97e58efc831ec988159ee1a3fb22..9e59de501c2e95beab5a18881b38ed2c312794a0 100644 (file)
@@ -7,8 +7,6 @@
  *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $
- *
  * 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.
index ba1bdf787323190377c20aa239317ee278769b5b..d1129bae6c27d20731ef0ead6b86a863e590e7f4 100644 (file)
@@ -797,6 +797,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
        return 0;
 }
 
+/**
+ * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @dataofs    offset of requested data within the page
+ * @readlen    data length
+ * @buf:       buffer to store read data
+ */
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+{
+       int start_step, end_step, num_steps;
+       uint32_t *eccpos = chip->ecc.layout->eccpos;
+       uint8_t *p;
+       int data_col_addr, i, gaps = 0;
+       int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+       int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+
+       /* Column address wihin the page aligned to ECC size (256bytes). */
+       start_step = data_offs / chip->ecc.size;
+       end_step = (data_offs + readlen - 1) / chip->ecc.size;
+       num_steps = end_step - start_step + 1;
+
+       /* Data size aligned to ECC ecc.size*/
+       datafrag_len = num_steps * chip->ecc.size;
+       eccfrag_len = num_steps * chip->ecc.bytes;
+
+       data_col_addr = start_step * chip->ecc.size;
+       /* If we read not a page aligned data */
+       if (data_col_addr != 0)
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+
+       p = bufpoi + data_col_addr;
+       chip->read_buf(mtd, p, datafrag_len);
+
+       /* Calculate  ECC */
+       for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+               chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+
+       /* The performance is faster if to position offsets
+          according to ecc.pos. Let make sure here that
+          there are no gaps in ecc positions */
+       for (i = 0; i < eccfrag_len - 1; i++) {
+               if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
+                       eccpos[i + start_step * chip->ecc.bytes + 1]) {
+                       gaps = 1;
+                       break;
+               }
+       }
+       if (gaps) {
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+               chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+       } else {
+               /* send the command to read the particular ecc bytes */
+               /* take care about buswidth alignment in read_buf */
+               aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
+               aligned_len = eccfrag_len;
+               if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
+                       aligned_len++;
+               if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
+                       aligned_len++;
+
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
+               chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+       }
+
+       for (i = 0; i < eccfrag_len; i++)
+               chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
+
+       p = bufpoi + data_col_addr;
+       for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+               int stat;
+
+               stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+               if (stat == -1)
+                       mtd->ecc_stats.failed++;
+               else
+                       mtd->ecc_stats.corrected += stat;
+       }
+       return 0;
+}
+
 /**
  * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
  * @mtd:       mtd info structure
@@ -994,6 +1075,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                        /* Now read the page into the buffer */
                        if (unlikely(ops->mode == MTD_OOB_RAW))
                                ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
+                       else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
+                               ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
                        else
                                ret = chip->ecc.read_page(mtd, chip, bufpoi);
                        if (ret < 0)
@@ -1001,7 +1084,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 
                        /* Transfer not aligned data */
                        if (!aligned) {
-                               chip->pagebuf = realpage;
+                               if (!NAND_SUBPAGE_READ(chip) && !oob)
+                                       chip->pagebuf = realpage;
                                memcpy(buf, chip->buffers->databuf + col, bytes);
                        }
 
@@ -2521,6 +2605,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.calculate = nand_calculate_ecc;
                chip->ecc.correct = nand_correct_data;
                chip->ecc.read_page = nand_read_page_swecc;
+               chip->ecc.read_subpage = nand_read_subpage;
                chip->ecc.write_page = nand_write_page_swecc;
                chip->ecc.read_oob = nand_read_oob_std;
                chip->ecc.write_oob = nand_write_oob_std;
index 5e121ceaa59899ea94d964aab94df39845c18520..0b1c48595f1255547aea9eae6e41ba03e47dc05f 100644 (file)
@@ -6,8 +6,6 @@
  *
  *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
  *
- * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $
- *
  * 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.
index 9003a135e0502089f6a8e44f77f26b129a849375..918a806a8471f7aaa1b68b29e5e401a15737d4a4 100644 (file)
@@ -9,8 +9,6 @@
  *
  * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
  *
- * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
- *
  * This file is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 or (at your option) any
index a3e3ab0185d52b6e076f7380279ca6a8caf29cd2..69ee2c90eb0b3499e30c9ce00bdf787c2fb9dd53 100644 (file)
@@ -3,8 +3,6 @@
  *
  *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
  *
- * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $
- *
  * 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.
index bb885d1fcab5e9bbb87a085f0af76a76ea5e88bb..ecd70e2504f6c07eef8f87b824ba1a2d71d02ba6 100644 (file)
@@ -21,8 +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
- *
- * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $
  */
 
 #include <linux/init.h>
@@ -39,6 +37,7 @@
 #include <linux/delay.h>
 #include <linux/list.h>
 #include <linux/random.h>
+#include <asm/div64.h>
 
 /* Default simulator parameters values */
 #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
@@ -298,11 +297,11 @@ struct nandsim {
 
        /* NAND flash "geometry" */
        struct nandsin_geometry {
-               uint32_t totsz;     /* total flash size, bytes */
+               uint64_t totsz;     /* total flash size, bytes */
                uint32_t secsz;     /* flash sector (erase block) size, bytes */
                uint pgsz;          /* NAND flash page size, bytes */
                uint oobsz;         /* page OOB area size, bytes */
-               uint32_t totszoob;  /* total flash size including OOB, bytes */
+               uint64_t totszoob;  /* total flash size including OOB, bytes */
                uint pgszoob;       /* page size including OOB , bytes*/
                uint secszoob;      /* sector size including OOB, bytes */
                uint pgnum;         /* total number of pages */
@@ -459,6 +458,12 @@ static char *get_partition_name(int i)
        return kstrdup(buf, GFP_KERNEL);
 }
 
+static u_int64_t divide(u_int64_t n, u_int32_t d)
+{
+       do_div(n, d);
+       return n;
+}
+
 /*
  * Initialize the nandsim structure.
  *
@@ -469,8 +474,8 @@ static int init_nandsim(struct mtd_info *mtd)
        struct nand_chip *chip = (struct nand_chip *)mtd->priv;
        struct nandsim   *ns   = (struct nandsim *)(chip->priv);
        int i, ret = 0;
-       u_int32_t remains;
-       u_int32_t next_offset;
+       u_int64_t remains;
+       u_int64_t next_offset;
 
        if (NS_IS_INITIALIZED(ns)) {
                NS_ERR("init_nandsim: nandsim is already initialized\n");
@@ -487,8 +492,8 @@ static int init_nandsim(struct mtd_info *mtd)
        ns->geom.oobsz    = mtd->oobsize;
        ns->geom.secsz    = mtd->erasesize;
        ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
-       ns->geom.pgnum    = ns->geom.totsz / ns->geom.pgsz;
-       ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz;
+       ns->geom.pgnum    = divide(ns->geom.totsz, ns->geom.pgsz);
+       ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz;
        ns->geom.secshift = ffs(ns->geom.secsz) - 1;
        ns->geom.pgshift  = chip->page_shift;
        ns->geom.oobshift = ffs(ns->geom.oobsz) - 1;
@@ -511,7 +516,7 @@ static int init_nandsim(struct mtd_info *mtd)
        }
 
        if (ns->options & OPT_SMALLPAGE) {
-               if (ns->geom.totsz < (32 << 20)) {
+               if (ns->geom.totsz <= (32 << 20)) {
                        ns->geom.pgaddrbytes  = 3;
                        ns->geom.secaddrbytes = 2;
                } else {
@@ -537,15 +542,16 @@ static int init_nandsim(struct mtd_info *mtd)
        remains = ns->geom.totsz;
        next_offset = 0;
        for (i = 0; i < parts_num; ++i) {
-               unsigned long part = parts[i];
-               if (!part || part > remains / ns->geom.secsz) {
+               u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz;
+
+               if (!part_sz || part_sz > remains) {
                        NS_ERR("bad partition size.\n");
                        ret = -EINVAL;
                        goto error;
                }
                ns->partitions[i].name   = get_partition_name(i);
                ns->partitions[i].offset = next_offset;
-               ns->partitions[i].size   = part * ns->geom.secsz;
+               ns->partitions[i].size   = part_sz;
                next_offset += ns->partitions[i].size;
                remains -= ns->partitions[i].size;
        }
@@ -573,7 +579,7 @@ static int init_nandsim(struct mtd_info *mtd)
        if (ns->busw == 16)
                NS_WARN("16-bit flashes support wasn't tested\n");
 
-       printk("flash size: %u MiB\n",          ns->geom.totsz >> 20);
+       printk("flash size: %llu MiB\n",        ns->geom.totsz >> 20);
        printk("page size: %u bytes\n",         ns->geom.pgsz);
        printk("OOB area size: %u bytes\n",     ns->geom.oobsz);
        printk("sector size: %u KiB\n",         ns->geom.secsz >> 10);
@@ -583,7 +589,7 @@ static int init_nandsim(struct mtd_info *mtd)
        printk("bits in sector size: %u\n",     ns->geom.secshift);
        printk("bits in page size: %u\n",       ns->geom.pgshift);
        printk("bits in OOB size: %u\n",        ns->geom.oobshift);
-       printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10);
+       printk("flash size with OOB: %llu KiB\n", ns->geom.totszoob >> 10);
        printk("page address bytes: %u\n",      ns->geom.pgaddrbytes);
        printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
        printk("options: %#x\n",                ns->options);
@@ -825,7 +831,7 @@ static int setup_wear_reporting(struct mtd_info *mtd)
 
        if (!rptwear)
                return 0;
-       wear_eb_count = mtd->size / mtd->erasesize;
+       wear_eb_count = divide(mtd->size, mtd->erasesize);
        mem = wear_eb_count * sizeof(unsigned long);
        if (mem / sizeof(unsigned long) != wear_eb_count) {
                NS_ERR("Too many erase blocks for wear reporting\n");
@@ -2013,7 +2019,7 @@ static int __init ns_init_module(void)
        }
 
        if (overridesize) {
-               u_int32_t new_size = nsmtd->erasesize << overridesize;
+               u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize;
                if (new_size >> overridesize != nsmtd->erasesize) {
                        NS_ERR("overridesize is too big\n");
                        goto err_exit;
@@ -2021,7 +2027,8 @@ static int __init ns_init_module(void)
                /* N.B. This relies on nand_scan not doing anything with the size before we change it */
                nsmtd->size = new_size;
                chip->chipsize = new_size;
-               chip->chip_shift = ffs(new_size) - 1;
+               chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1;
+               chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
        }
 
        if ((retval = setup_wear_reporting(nsmtd)) != 0)
index 082073acf20fc51b6a252623bca2d7cc59ba456b..cc86584318512acc1b00547d3d87404bf95a98a0 100644 (file)
@@ -6,8 +6,6 @@
  *  Derived from drivers/mtd/nand/edb7312.c
  *
  *
- * $Id: ppchameleonevb.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $
- *
  * 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.
index 26f88215bc47d48c12ff9768da72829366d4ac76..a033c4cd8e16e8b7a6d0b8225eedc78c8df0e690 100644 (file)
@@ -6,8 +6,6 @@
  *  Derived from drivers/mtd/nand/spia.c
  *       Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
- * $Id: rtc_from4.c,v 1.10 2005/11/07 11:14:31 gleixner Exp $
- *
  * 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.
index b34a460ab67915afe3812d6ced1410cfff6c16b7..556139ed1fdf8b50919dfa078fe74a98626008d6 100644 (file)
@@ -1,26 +1,10 @@
 /* linux/drivers/mtd/nand/s3c2410.c
  *
- * Copyright (c) 2004,2005 Simtec Electronics
- *     http://www.simtec.co.uk/products/SWLINUX/
+ * Copyright Â© 2004-2008 Simtec Electronics
+ *     http://armlinux.simtec.co.uk/
  *     Ben Dooks <ben@simtec.co.uk>
  *
- * Samsung S3C2410/S3C240 NAND driver
- *
- * Changelog:
- *     21-Sep-2004  BJD  Initial version
- *     23-Sep-2004  BJD  Multiple device support
- *     28-Sep-2004  BJD  Fixed ECC placement for Hardware mode
- *     12-Oct-2004  BJD  Fixed errors in use of platform data
- *     18-Feb-2005  BJD  Fix sparse errors
- *     14-Mar-2005  BJD  Applied tglx's code reduction patch
- *     02-May-2005  BJD  Fixed s3c2440 support
- *     02-May-2005  BJD  Reduced hwcontrol decode
- *     20-Jun-2005  BJD  Updated s3c2440 support, fixed timing bug
- *     08-Jul-2005  BJD  Fix OOPS when no platform data supplied
- *     20-Oct-2005  BJD  Fix timing calculation bug
- *     14-Jan-2006  BJD  Allow clock to be stopped when idle
- *
- * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $
+ * Samsung S3C2410/S3C2440/S3C2412 NAND 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
@@ -52,6 +36,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/clk.h>
+#include <linux/cpufreq.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -120,8 +105,13 @@ struct s3c2410_nand_info {
        int                             sel_bit;
        int                             mtd_count;
        unsigned long                   save_sel;
+       unsigned long                   clk_rate;
 
        enum s3c_cpu_type               cpu_type;
+
+#ifdef CONFIG_CPU_FREQ
+       struct notifier_block   freq_transition;
+#endif
 };
 
 /* conversion functions */
@@ -179,17 +169,18 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
 
 /* controller setup */
 
-static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
-                              struct platform_device *pdev)
+static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
 {
-       struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
-       unsigned long clkrate = clk_get_rate(info->clk);
+       struct s3c2410_platform_nand *plat = info->platform;
        int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
        int tacls, twrph0, twrph1;
-       unsigned long cfg = 0;
+       unsigned long clkrate = clk_get_rate(info->clk);
+       unsigned long set, cfg, mask;
+       unsigned long flags;
 
        /* calculate the timing information for the controller */
 
+       info->clk_rate = clkrate;
        clkrate /= 1000;        /* turn clock into kHz for ease of use */
 
        if (plat != NULL) {
@@ -211,28 +202,69 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
        dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
               tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
 
+       switch (info->cpu_type) {
+       case TYPE_S3C2410:
+               mask = (S3C2410_NFCONF_TACLS(3) |
+                       S3C2410_NFCONF_TWRPH0(7) |
+                       S3C2410_NFCONF_TWRPH1(7));
+               set = S3C2410_NFCONF_EN;
+               set |= S3C2410_NFCONF_TACLS(tacls - 1);
+               set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
+               set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
+               break;
+
+       case TYPE_S3C2440:
+       case TYPE_S3C2412:
+               mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) |
+                       S3C2410_NFCONF_TWRPH0(7) |
+                       S3C2410_NFCONF_TWRPH1(7));
+
+               set = S3C2440_NFCONF_TACLS(tacls - 1);
+               set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
+               set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
+               break;
+
+       default:
+               /* keep compiler happy */
+               mask = 0;
+               set = 0;
+               BUG();
+       }
+
+       dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
+
+       local_irq_save(flags);
+
+       cfg = readl(info->regs + S3C2410_NFCONF);
+       cfg &= ~mask;
+       cfg |= set;
+       writel(cfg, info->regs + S3C2410_NFCONF);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
+{
+       int ret;
+
+       ret = s3c2410_nand_setrate(info);
+       if (ret < 0)
+               return ret;
+
        switch (info->cpu_type) {
        case TYPE_S3C2410:
-               cfg = S3C2410_NFCONF_EN;
-               cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
-               cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
-               cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
+       default:
                break;
 
        case TYPE_S3C2440:
        case TYPE_S3C2412:
-               cfg = S3C2440_NFCONF_TACLS(tacls - 1);
-               cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
-               cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
-
                /* enable the controller and de-assert nFCE */
 
                writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
        }
 
-       dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
-
-       writel(cfg, info->regs + S3C2410_NFCONF);
        return 0;
 }
 
@@ -513,6 +545,52 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
        writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
 }
 
+/* cpufreq driver support */
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
+                                         unsigned long val, void *data)
+{
+       struct s3c2410_nand_info *info;
+       unsigned long newclk;
+
+       info = container_of(nb, struct s3c2410_nand_info, freq_transition);
+       newclk = clk_get_rate(info->clk);
+
+       if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) ||
+           (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) {
+               s3c2410_nand_setrate(info);
+       }
+
+       return 0;
+}
+
+static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
+{
+       info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition;
+
+       return cpufreq_register_notifier(&info->freq_transition,
+                                        CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
+{
+       cpufreq_unregister_notifier(&info->freq_transition,
+                                   CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
+{
+       return 0;
+}
+
+static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
+{
+}
+#endif
+
 /* device management functions */
 
 static int s3c2410_nand_remove(struct platform_device *pdev)
@@ -524,9 +602,10 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
        if (info == NULL)
                return 0;
 
-       /* first thing we need to do is release all our mtds
-        * and their partitions, then go through freeing the
-        * resources used
+       s3c2410_nand_cpufreq_deregister(info);
+
+       /* Release all our mtds  and their partitions, then go through
+        * freeing the resources used
         */
 
        if (info->mtds != NULL) {
@@ -691,7 +770,8 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
 {
        struct nand_chip *chip = &nmtd->chip;
 
-       printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift);
+       dev_dbg(info->device, "chip %p => page shift %d\n",
+               chip, chip->page_shift);
 
        if (hardware_ecc) {
                /* change the behaviour depending on wether we are using
@@ -784,7 +864,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
 
        /* initialise the hardware */
 
-       err = s3c2410_nand_inithw(info, pdev);
+       err = s3c2410_nand_inithw(info);
        if (err != 0)
                goto exit_error;
 
@@ -827,6 +907,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
                        sets++;
        }
 
+       err = s3c2410_nand_cpufreq_register(info);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to init cpufreq support\n");
+               goto exit_error;
+       }
+
        if (allow_clk_stop(info)) {
                dev_info(&pdev->dev, "clock idle support enabled\n");
                clk_disable(info->clk);
@@ -874,7 +960,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
 
        if (info) {
                clk_enable(info->clk);
-               s3c2410_nand_inithw(info, dev);
+               s3c2410_nand_inithw(info);
 
                /* Restore the state of the nFCE line. */
 
index 033f8800b1e69224d81c15dd670bc24e95190a7f..6dba2fb66ae5e32536b11757f0289af9002f47a1 100644 (file)
@@ -3,8 +3,6 @@
  *
  *  Copyright (C) 2004 Richard Purdie
  *
- *  $Id: sharpsl.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $
- *
  *  Based on Sharp's NAND driver sharp_sl.c
  *
  * This program is free software; you can redistribute it and/or modify
index 1f6d429b158319c6db75f710e8daad63bbe48588..0cc6d0acb8febdcde011545e5022bb950e3ccf0e 100644 (file)
@@ -8,8 +8,6 @@
  *                     to controllines (due to change in nand.c)
  *                     page_cache added
  *
- * $Id: spia.c,v 1.25 2005/11/07 11:14:31 gleixner Exp $
- *
  * 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.
index f9e2d4a0ab8c661bba8dec6e46f1e194433eecfd..bbf492e6830dafde45f20b2fe70032ccc3da4f20 100644 (file)
@@ -14,8 +14,6 @@
  *  Overview:
  *   This is a device driver for the NAND flash device found on the
  *   TI fido board. It supports 32MiB and 64MiB cards
- *
- * $Id: toto.c,v 1.5 2005/11/07 11:14:31 gleixner Exp $
  */
 
 #include <linux/slab.h>
index f40081069ab2c6fa0f2a5705eaad7c9e698c28af..807a72752eeb035885b1f633d77d7f4e08e4ee6e 100644 (file)
@@ -9,8 +9,6 @@
  * Derived from drivers/mtd/nand/autcpu12.c
  *   Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $
- *
  * 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.
index 0c9ce19ea27a3476f44384749476822b3c311cfa..320b929abe795ea00eb5bf4c130f6db966d32893 100644 (file)
@@ -1,7 +1,6 @@
 /* Linux driver for NAND Flash Translation Layer      */
 /* (c) 1999 Machine Vision Holdings, Inc.             */
 /* Author: David Woodhouse <dwmw2@infradead.org>      */
-/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */
 
 /*
   The contents of this file are distributed under the GNU General
@@ -803,12 +802,8 @@ static struct mtd_blktrans_ops nftl_tr = {
        .owner          = THIS_MODULE,
 };
 
-extern char nftlmountrev[];
-
 static int __init init_nftl(void)
 {
-       printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
-
        return register_mtd_blktrans(&nftl_tr);
 }
 
index 345e6eff89ce5929c3548d1874b6363b102ae554..ccc4f209fbb5c17985ba5e264b0a6ddde9824fa3 100644 (file)
@@ -4,8 +4,6 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: nftlmount.c,v 1.41 2005/11/07 11:14:21 gleixner Exp $
- *
  * 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
@@ -31,8 +29,6 @@
 
 #define SECTORSIZE 512
 
-char nftlmountrev[]="$Revision: 1.41 $";
-
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
  *     various device information of the NFTL partition and Bad Unit Table. Update
  *     the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
index 5d7965f7e9ce6a3f8232a53cb2c0e2a243f13379..926cf3a4135d1ca84094c40d018803709d059334 100644 (file)
@@ -325,28 +325,11 @@ static int onenand_wait(struct mtd_info *mtd, int state)
 
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
-       if (ctrl & ONENAND_CTRL_ERROR) {
-               printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl);
-               if (ctrl & ONENAND_CTRL_LOCK)
-                       printk(KERN_ERR "onenand_wait: it's locked error.\n");
-               if (state == FL_READING) {
-                       /*
-                        * A power loss while writing can result in a page
-                        * becoming unreadable.  When the device is mounted
-                        * again, reading that page gives controller errors.
-                        * Upper level software like JFFS2 treat -EIO as fatal,
-                        * refusing to mount at all.  That means it is necessary
-                        * to treat the error as an ECC error to allow recovery.
-                        * Note that typically in this case, the eraseblock can
-                        * still be erased and rewritten i.e. it has not become
-                        * a bad block.
-                        */
-                       mtd->ecc_stats.failed++;
-                       return -EBADMSG;
-               }
-               return -EIO;
-       }
-
+       /*
+        * In the Spec. it checks the controller status first
+        * However if you get the correct information in case of
+        * power off recovery (POR) test, it should read ECC status first
+        */
        if (interrupt & ONENAND_INT_READ) {
                int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
                if (ecc) {
@@ -364,6 +347,15 @@ static int onenand_wait(struct mtd_info *mtd, int state)
                return -EIO;
        }
 
+       /* If there's controller error, it's a real error */
+       if (ctrl & ONENAND_CTRL_ERROR) {
+               printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n",
+                       ctrl);
+               if (ctrl & ONENAND_CTRL_LOCK)
+                       printk(KERN_ERR "onenand_wait: it's locked error.\n");
+               return -EIO;
+       }
+
        return 0;
 }
 
@@ -1135,22 +1127,26 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
        interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
-       /* Initial bad block case: 0x2400 or 0x0400 */
-       if (ctrl & ONENAND_CTRL_ERROR) {
-               printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
-               return ONENAND_BBT_READ_ERROR;
-       }
-
        if (interrupt & ONENAND_INT_READ) {
                int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
-               if (ecc & ONENAND_ECC_2BIT_ALL)
+               if (ecc & ONENAND_ECC_2BIT_ALL) {
+                       printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
+                               ", controller error 0x%04x\n", ecc, ctrl);
                        return ONENAND_BBT_READ_ERROR;
+               }
        } else {
                printk(KERN_ERR "onenand_bbt_wait: read timeout!"
                        "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
                return ONENAND_BBT_READ_FATAL_ERROR;
        }
 
+       /* Initial bad block case: 0x2400 or 0x0400 */
+       if (ctrl & ONENAND_CTRL_ERROR) {
+               printk(KERN_DEBUG "onenand_bbt_wait: "
+                       "controller error = 0x%04x\n", ctrl);
+               return ONENAND_BBT_READ_ERROR;
+       }
+
        return 0;
 }
 
index c5030f94f04e180adeea6eda0af5f0b8994e83cb..2d600a1bf2aa76e804679e2df284d8b7f9be2742 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $
- *
  * Parse RedBoot-style Flash Image System (FIS) tables and
  * produce a Linux partition array to match.
  */
index c84e45465499dba5ad0117c1fc213b4104705171..e538c0a72abb0d531ddd992a559671d7020a7f5f 100644 (file)
@@ -3,8 +3,6 @@
  *
  * Copyright (C) 2005  Sean Young <sean@mess.org>
  *
- * $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $
- *
  * This type of flash translation layer (FTL) is used by the Embedded BIOS
  * by General Software. It is known as the Resident Flash Disk (RFD), see:
  *
index c7cc760a1777f96fad3ba631c76df774e4579adf..af251a5df844929097ef67adb394936871bbec3b 100644 (file)
@@ -814,7 +814,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
        }
 
        /* release skb */
-       BUG_TRAP(skb);
+       WARN_ON(!skb);
        dev_kfree_skb(skb);
        tx_buf->first_bd = 0;
        tx_buf->skb = NULL;
@@ -837,9 +837,9 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp)
        used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS;
 
 #ifdef BNX2X_STOP_ON_ERROR
-       BUG_TRAP(used >= 0);
-       BUG_TRAP(used <= fp->bp->tx_ring_size);
-       BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL);
+       WARN_ON(used < 0);
+       WARN_ON(used > fp->bp->tx_ring_size);
+       WARN_ON((fp->bp->tx_ring_size - used) > MAX_TX_AVAIL);
 #endif
 
        return (s16)(fp->bp->tx_ring_size) - used;
@@ -4374,7 +4374,7 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp)
                        }
                        ring_prod = NEXT_RX_IDX(ring_prod);
                        cqe_ring_prod = NEXT_RCQ_IDX(cqe_ring_prod);
-                       BUG_TRAP(ring_prod > i);
+                       WARN_ON(ring_prod <= i);
                }
 
                fp->rx_bd_prod = ring_prod;
index f9d6b4dca180e49f6d16fc4ebfe594831cc0cf29..096bca54bcf77c33300d43b7e46a7734434378c0 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index aa95287790442e83e623e8a5f4895db5f5c1dcf5..f094ee00c416a240cd564089d10ba5d0bba067b1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 04d5bc69a6f817a182a341a0121ad397b7bbce69..2845a0560b84e18fcd28c64e9caa145b59c9a034 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
index 95e87a2f8896f97ca5110613fdcd2a6527ccfc5f..9bb50e3f8974386ff329d0d2d97671d27bf8a5f6 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
  * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2004 Voltaire, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
index 7df928d3a3d82a55db3f970cf20257e6168161ec..8a8b56135a58bda015fac0a4b184b4e344072072 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
index 57278224ba1e73ad1b43ee945369c1b0f51a5d32..7e32955da9829afcc9b80c16f2a06bae1402af96 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
index fbf0e22be122be3a2d77dbe16a6635d4b2cee285..decbb5c2ad41b7379ddbe5b5ce8df81abc4243ca 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2006, 2007 Cisco Systems.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
index 2a5bef6388fec9fb28995edb44e350474f60a1d7..baf4bf66062ca6a91652eb6bd5e5224b3ffb9440 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
index 6c44edf35847e07c1de518f2bc78d4dbe70947b7..ab56a2f89b651eb3c33cba5da21e1686a21bb98e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
index 4a6c4d526f1b51e72dad933fed05558faf8f57e5..0e7eb1038f9f443e8b0af3525a5fd9b871c608d7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 8e1d24cda1b03173ec94f64fe8da2ff02b2a4383..1252a919de2eb9bd43e8e3c92aa5792b47e81932 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
index b4b57870ddfdbefc7ecb26ff0167eed4d398e3c5..c83f88ce073663697e7cff88b6a6428e59b1b547 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 78038499cff52652f64d7796880b1b92c0ef027b..5337e3ac3e78c152143e31bc2283f2dc2c7b1752 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
  * Copyright (c) 2005, 2006, 2007 Cisco Systems.  All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2004 Voltaire, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
index a3c04c5f12c26089e9b112cf1c9f5b58faa27418..62071d9c4a557c61a38da5167cf2d017590b5ac7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2004 Topspin Communications.  All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
index ee5484c44a1833781887dbbdc42775f5d661bc60..c49a86044bf7bec2c53d77264164e0c5e223e005 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2004 Topspin Communications.  All rights reserved.
  * Copyright (c) 2005, 2006, 2007 Cisco Systems, Inc. All rights reserved.
- * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
  * Copyright (c) 2004 Voltaire, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
index e199715fabd0947d6014a9b068bd583aa3745eb6..3951b884c0fba94abebab3e254de14cdf99c63d2 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index d23f46d692ef5daf02ad7076842b4859c558bd82..533eb6db24b37d589996baa8ea882c3e764116b8 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved.
+ * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 739b3ab7bccc2d9ea719c6080eb3137bf2ba2dbc..ddccc074a76a88da15574be350b408e0e9605817 100644 (file)
@@ -581,12 +581,12 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        if (file == ppp->owner)
                                ppp_shutdown_interface(ppp);
                }
-               if (atomic_read(&file->f_count) <= 2) {
+               if (atomic_long_read(&file->f_count) <= 2) {
                        ppp_release(NULL, file);
                        err = 0;
                } else
-                       printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n",
-                              atomic_read(&file->f_count));
+                       printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n",
+                              atomic_long_read(&file->f_count));
                unlock_kernel();
                return err;
        }
index 4a5ec39f9ca60a4dc0beb227703c0fedc3777535..0815690ac1e03cd8b7fb003e7c9fabb38804647c 100644 (file)
@@ -6,4 +6,4 @@
 # it under the terms of the GNU General Public License (version 2 only)
 # as published by the Free Software Foundation.
 
-obj-$(CONFIG_VIRTIO) += kvm_virtio.o
+obj-$(CONFIG_S390_GUEST) += kvm_virtio.o
index c3ad89e302bd1ae6695e0e377faf21b4a3fa4008..cebb25e36e8238c8217d7db0204a6e8ccd221ca5 100644 (file)
@@ -3321,7 +3321,7 @@ int qeth_change_mtu(struct net_device *dev, int new_mtu)
        struct qeth_card *card;
        char dbf_text[15];
 
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "chgmtu");
        sprintf(dbf_text, "%8x", new_mtu);
@@ -3343,7 +3343,7 @@ struct net_device_stats *qeth_get_stats(struct net_device *dev)
 {
        struct qeth_card *card;
 
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 5, "getstat");
 
@@ -3395,7 +3395,7 @@ void qeth_tx_timeout(struct net_device *dev)
 {
        struct qeth_card *card;
 
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        card->stats.tx_errors++;
        qeth_schedule_recovery(card);
 }
@@ -3403,7 +3403,7 @@ EXPORT_SYMBOL_GPL(qeth_tx_timeout);
 
 int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        int rc = 0;
 
        switch (regnum) {
@@ -4253,7 +4253,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_stats_count);
 void qeth_core_get_ethtool_stats(struct net_device *dev,
                struct ethtool_stats *stats, u64 *data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        data[0] = card->stats.rx_packets -
                                card->perf_stats.initial_rx_packets;
        data[1] = card->perf_stats.bufs_rec;
@@ -4313,7 +4313,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_strings);
 void qeth_core_get_drvinfo(struct net_device *dev,
                struct ethtool_drvinfo *info)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        if (card->options.layer2)
                strcpy(info->driver, "qeth_l2");
        else
@@ -4331,7 +4331,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
 int qeth_core_ethtool_get_settings(struct net_device *netdev,
                                        struct ethtool_cmd *ecmd)
 {
-       struct qeth_card *card = netdev_priv(netdev);
+       struct qeth_card *card = netdev->ml_priv;
        enum qeth_link_types link_type;
 
        if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
index 3fbc3bdec0c5cfada6edccd973072f81ee2b97f7..a8b069cd9a4c63efb2197e24ba469790692bcdce 100644 (file)
@@ -35,7 +35,7 @@ static int qeth_l2_recover(void *);
 
 static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct mii_ioctl_data *mii_data;
        int rc = 0;
 
@@ -317,7 +317,7 @@ static void qeth_l2_process_vlans(struct qeth_card *card, int clear)
 
 static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct qeth_vlan_vid *id;
 
        QETH_DBF_TEXT_(TRACE, 4, "aid:%d", vid);
@@ -334,7 +334,7 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
        struct qeth_vlan_vid *id, *tmpid = NULL;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
        spin_lock_bh(&card->vlanlock);
@@ -566,7 +566,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
 static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 {
        struct sockaddr *addr = p;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        int rc = 0;
 
        QETH_DBF_TEXT(TRACE, 3, "setmac");
@@ -590,7 +590,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 
 static void qeth_l2_set_multicast_list(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct dev_mc_list *dm;
 
        if (card->info.type == QETH_CARD_TYPE_OSN)
@@ -612,7 +612,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int rc;
        struct qeth_hdr *hdr = NULL;
        int elements = 0;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = skb;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_get_cast_type(card, skb);
@@ -767,7 +767,7 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
 
 static int qeth_l2_open(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethopen");
        if (card->state != CARD_STATE_SOFTSETUP)
@@ -791,7 +791,7 @@ static int qeth_l2_open(struct net_device *dev)
 
 static int qeth_l2_stop(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethstop");
        netif_tx_disable(dev);
@@ -838,7 +838,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
 
 static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        if (data) {
                if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -894,7 +894,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        if (!card->dev)
                return -ENODEV;
 
-       card->dev->priv = card;
+       card->dev->ml_priv = card;
        card->dev->tx_timeout = &qeth_tx_timeout;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->open = qeth_l2_open;
@@ -1178,7 +1178,7 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
        QETH_DBF_TEXT(TRACE, 2, "osnsdmc");
        if (!dev)
                return -ENODEV;
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        if (!card)
                return -ENODEV;
        if ((card->state != CARD_STATE_UP) &&
@@ -1201,7 +1201,7 @@ int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
        *dev = qeth_l2_netdev_by_devno(read_dev_no);
        if (*dev == NULL)
                return -ENODEV;
-       card = netdev_priv(*dev);
+       card = (*dev)->ml_priv;
        if (!card)
                return -ENODEV;
        if ((assist_cb == NULL) || (data_cb == NULL))
@@ -1219,7 +1219,7 @@ void qeth_osn_deregister(struct net_device *dev)
        QETH_DBF_TEXT(TRACE, 2, "osndereg");
        if (!dev)
                return;
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        if (!card)
                return;
        card->osn_info.assist_cb = NULL;
index 38de31b557082cbe006f227bb3f6e88603ed3dc4..3e1d13857350959519524ec655f5659d3f633444 100644 (file)
@@ -1813,7 +1813,7 @@ static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
 static void qeth_l3_vlan_rx_register(struct net_device *dev,
                        struct vlan_group *grp)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        unsigned long flags;
 
        QETH_DBF_TEXT(TRACE, 4, "vlanreg");
@@ -1825,7 +1825,7 @@ static void qeth_l3_vlan_rx_register(struct net_device *dev,
 static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
        struct net_device *vlandev;
-       struct qeth_card *card = (struct qeth_card *) dev->priv;
+       struct qeth_card *card = dev->ml_priv;
        struct in_device *in_dev;
 
        if (card->info.type == QETH_CARD_TYPE_IQD)
@@ -1851,7 +1851,7 @@ static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 
 static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        unsigned long flags;
 
        QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
@@ -2013,7 +2013,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
                }
        }
 
-       if (rc && !(netdev_priv(vlan_dev_real_dev(dev)) == (void *)card))
+       if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
                return 0;
 
        return rc;
@@ -2047,9 +2047,9 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
 
        rc = qeth_l3_verify_dev(dev);
        if (rc == QETH_REAL_CARD)
-               card = netdev_priv(dev);
+               card = dev->ml_priv;
        else if (rc == QETH_VLAN_CARD)
-               card = netdev_priv(vlan_dev_real_dev(dev));
+               card = vlan_dev_real_dev(dev)->ml_priv;
        if (card && card->options.layer2)
                card = NULL;
        QETH_DBF_TEXT_(TRACE, 4, "%d", rc);
@@ -2110,7 +2110,7 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
 
 static void qeth_l3_set_multicast_list(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 3, "setmulti");
        qeth_l3_delete_mc_addresses(card);
@@ -2438,7 +2438,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
 
 static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct qeth_arp_cache_entry arp_entry;
        struct mii_ioctl_data *mii_data;
        int rc = 0;
@@ -2595,7 +2595,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        u16 *tag;
        struct qeth_hdr *hdr = NULL;
        int elements_needed = 0;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = NULL;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_get_cast_type(card, skb);
@@ -2763,7 +2763,7 @@ tx_drop:
 
 static int qeth_l3_open(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethopen");
        if (card->state != CARD_STATE_SOFTSETUP)
@@ -2780,7 +2780,7 @@ static int qeth_l3_open(struct net_device *dev)
 
 static int qeth_l3_stop(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethstop");
        netif_tx_disable(dev);
@@ -2792,14 +2792,14 @@ static int qeth_l3_stop(struct net_device *dev)
 
 static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        return (card->options.checksum_type == HW_CHECKSUMMING);
 }
 
 static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        enum qeth_card_states old_state;
        enum qeth_checksum_types csum_type;
 
@@ -2825,7 +2825,7 @@ static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
 
 static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        if (data) {
                if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -2915,7 +2915,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
                return -ENODEV;
 
        card->dev->hard_start_xmit = qeth_l3_hard_start_xmit;
-       card->dev->priv = card;
+       card->dev->ml_priv = card;
        card->dev->tx_timeout = &qeth_tx_timeout;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->open = qeth_l3_open;
index 7045511f9ad2be543f9c4d5940b67d9a28bb0a09..b92c19bb6876971bf042c9f0e8e372e6bd5e91ef 100644 (file)
@@ -4,7 +4,7 @@
    Written By: Adam Radford <linuxraid@amcc.com>
    Modifications By: Tom Couch <linuxraid@amcc.com>
 
-   Copyright (C) 2004-2007 Applied Micro Circuits Corporation.
+   Copyright (C) 2004-2008 Applied Micro Circuits Corporation.
 
    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
                  Add support for 9650SE controllers.
    2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails.
    2.26.02.010 - Add support for 9690SA controllers.
+   2.26.02.011 - Increase max AENs drained to 256.
+                 Add MSI support and "use_msi" module parameter.
+                 Fix bug in twa_get_param() on 4GB+.
+                 Use pci_resource_len() for ioremap().
 */
 
 #include <linux/module.h>
@@ -95,7 +99,7 @@
 #include "3w-9xxx.h"
 
 /* Globals */
-#define TW_DRIVER_VERSION "2.26.02.010"
+#define TW_DRIVER_VERSION "2.26.02.011"
 static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
 static unsigned int twa_device_extension_count;
 static int twa_major = -1;
@@ -107,6 +111,10 @@ MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(TW_DRIVER_VERSION);
 
+static int use_msi = 0;
+module_param(use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts.  Default: 0");
+
 /* Function prototypes */
 static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
 static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
@@ -1038,7 +1046,6 @@ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl
        TW_Command_Full *full_command_packet;
        TW_Command *command_packet;
        TW_Param_Apache *param;
-       unsigned long param_value;
        void *retval = NULL;
 
        /* Setup the command packet */
@@ -1057,9 +1064,8 @@ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int tabl
        param->table_id = cpu_to_le16(table_id | 0x8000);
        param->parameter_id = cpu_to_le16(parameter_id);
        param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes);
-       param_value = tw_dev->generic_buffer_phys[request_id];
 
-       command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(param_value);
+       command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
        command_packet->byte8_offset.param.sgl[0].length = cpu_to_le32(TW_SECTOR_SIZE);
 
        /* Post the command packet to the board */
@@ -2000,7 +2006,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
 {
        struct Scsi_Host *host = NULL;
        TW_Device_Extension *tw_dev;
-       u32 mem_addr;
+       unsigned long mem_addr, mem_len;
        int retval = -ENODEV;
 
        retval = pci_enable_device(pdev);
@@ -2045,13 +2051,16 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
                goto out_free_device_extension;
        }
 
-       if (pdev->device == PCI_DEVICE_ID_3WARE_9000)
+       if (pdev->device == PCI_DEVICE_ID_3WARE_9000) {
                mem_addr = pci_resource_start(pdev, 1);
-       else
+               mem_len = pci_resource_len(pdev, 1);
+       } else {
                mem_addr = pci_resource_start(pdev, 2);
+               mem_len = pci_resource_len(pdev, 2);
+       }
 
        /* Save base address */
-       tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE);
+       tw_dev->base_addr = ioremap(mem_addr, mem_len);
        if (!tw_dev->base_addr) {
                TW_PRINTK(tw_dev->host, TW_DRIVER, 0x35, "Failed to ioremap");
                goto out_release_mem_region;
@@ -2086,7 +2095,7 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
 
        pci_set_drvdata(pdev, host);
 
-       printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n",
+       printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%lx, IRQ: %d.\n",
               host->host_no, mem_addr, pdev->irq);
        printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n",
               host->host_no,
@@ -2097,6 +2106,11 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
               le32_to_cpu(*(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE,
                                     TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH)));
 
+       /* Try to enable MSI */
+       if (use_msi && (pdev->device != PCI_DEVICE_ID_3WARE_9000) &&
+           !pci_enable_msi(pdev))
+               set_bit(TW_USING_MSI, &tw_dev->flags);
+
        /* Now setup the interrupt handler */
        retval = request_irq(pdev->irq, twa_interrupt, IRQF_SHARED, "3w-9xxx", tw_dev);
        if (retval) {
@@ -2120,6 +2134,8 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
        return 0;
 
 out_remove_host:
+       if (test_bit(TW_USING_MSI, &tw_dev->flags))
+               pci_disable_msi(pdev);
        scsi_remove_host(host);
 out_iounmap:
        iounmap(tw_dev->base_addr);
@@ -2151,6 +2167,10 @@ static void twa_remove(struct pci_dev *pdev)
        /* Shutdown the card */
        __twa_shutdown(tw_dev);
 
+       /* Disable MSI if enabled */
+       if (test_bit(TW_USING_MSI, &tw_dev->flags))
+               pci_disable_msi(pdev);
+
        /* Free IO remapping */
        iounmap(tw_dev->base_addr);
 
index d14a9479e3895edf2dea95800128073b8a686954..1729a8785fea197c0e1ae16f962952372dc0b5cf 100644 (file)
@@ -4,7 +4,7 @@
    Written By: Adam Radford <linuxraid@amcc.com>
    Modifications By: Tom Couch <linuxraid@amcc.com>
 
-   Copyright (C) 2004-2007 Applied Micro Circuits Corporation.
+   Copyright (C) 2004-2008 Applied Micro Circuits Corporation.
 
    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
@@ -319,8 +319,8 @@ static twa_message_type twa_error_table[] = {
 
 /* Compatibility defines */
 #define TW_9000_ARCH_ID 0x5
-#define TW_CURRENT_DRIVER_SRL 30
-#define TW_CURRENT_DRIVER_BUILD 80
+#define TW_CURRENT_DRIVER_SRL 35
+#define TW_CURRENT_DRIVER_BUILD 0
 #define TW_CURRENT_DRIVER_BRANCH 0
 
 /* Phase defines */
@@ -352,8 +352,9 @@ static twa_message_type twa_error_table[] = {
 #define TW_MAX_RESET_TRIES                   2
 #define TW_MAX_CMDS_PER_LUN                  254
 #define TW_MAX_RESPONSE_DRAIN                256
-#define TW_MAX_AEN_DRAIN                     40
+#define TW_MAX_AEN_DRAIN                     255
 #define TW_IN_RESET                           2
+#define TW_USING_MSI                         3
 #define TW_IN_ATTENTION_LOOP                 4
 #define TW_MAX_SECTORS                        256
 #define TW_AEN_WAIT_TIME                      1000
index 26be540d1dd373ec79872fed9d97d71b3d8f16a1..c7f06298bd3c09242abd800cebb1b2b7dfb3f104 100644 (file)
@@ -63,6 +63,7 @@ comment "SCSI support type (disk, tape, CD-ROM)"
 config BLK_DEV_SD
        tristate "SCSI disk support"
        depends on SCSI
+       select CRC_T10DIF
        ---help---
          If you want to use SCSI hard disks, Fibre Channel disks,
          Serial ATA (SATA) or Parallel ATA (PATA) hard disks,
index a8149677de23613076b65e7f8a318a027de4c39c..72fd5043cfa1c14cef585db3077317ce8c6699a0 100644 (file)
@@ -151,6 +151,8 @@ scsi_mod-$(CONFIG_SCSI_PROC_FS)     += scsi_proc.o
 scsi_tgt-y                     += scsi_tgt_lib.o scsi_tgt_if.o
 
 sd_mod-objs    := sd.o
+sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o
+
 sr_mod-objs    := sr.o sr_ioctl.o sr_vendor.o
 ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
                := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
index 8591585e5cc55c27669a004c48c048521a3dc9e4..218777bfc143db8bc54b1967b4c07f0a29232492 100644 (file)
@@ -2278,7 +2278,7 @@ do { \
 #define ASC_DBG(lvl, format, arg...) {                                 \
        if (asc_dbglvl >= (lvl))                                        \
                printk(KERN_DEBUG "%s: %s: " format, DRV_NAME,          \
-                       __FUNCTION__ , ## arg);                         \
+                       __func__ , ## arg);                             \
 }
 
 #define ASC_DBG_PRT_SCSI_HOST(lvl, s) \
index 0899cb61e3ddc08dd9db34a541decdb00f76a242..b5a868d85eb49760ac1f512195b29cb88c43578b 100644 (file)
@@ -288,20 +288,20 @@ static LIST_HEAD(aha152x_host_list);
 #define DO_LOCK(flags) \
        do { \
                if(spin_is_locked(&QLOCK)) { \
-                       DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \
+                       DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \
                } \
-               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \
+               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
                spin_lock_irqsave(&QLOCK,flags); \
-               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \
-               QLOCKER=__FUNCTION__; \
+               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
+               QLOCKER=__func__; \
                QLOCKERL=__LINE__; \
        } while(0)
 
 #define DO_UNLOCK(flags)       \
        do { \
-               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__, QLOCKER, QLOCKERL); \
+               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \
                spin_unlock_irqrestore(&QLOCK,flags); \
-               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __FUNCTION__, __LINE__); \
+               DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
                QLOCKER="(not locked)"; \
                QLOCKERL=0; \
        } while(0)
index 2ef459e9cda1a190213ae59fc2b64126f50c8923..2863a9d22851511f1fb9f8de2b9982903c8671bf 100644 (file)
@@ -39,9 +39,9 @@
 
 #ifdef ASD_ENTER_EXIT
 #define ENTER  printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \
-               __FUNCTION__)
+               __func__)
 #define EXIT   printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \
-               __FUNCTION__)
+               __func__)
 #else
 #define ENTER
 #define EXIT
index 83a78222896dfa171136dbb0bd6e04fc836a4f87..eb9dc3195fdfdbe7918b7d3ce0a3547e53e55ce4 100644 (file)
@@ -1359,7 +1359,7 @@ int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask)
        struct asd_ascb *ascb_list;
 
        if (!phy_mask) {
-               asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__);
+               asd_printk("%s called with phy_mask of 0!?\n", __func__);
                return 0;
        }
 
index 46643319c5201d1a1fbdc0b060982c4198085347..ca55013b6ae588cd699dde7739faf1f8dcbc3025 100644 (file)
@@ -211,7 +211,7 @@ static void asd_form_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
                phy->asd_port = port;
        }
        ASD_DPRINTK("%s: updating phy_mask 0x%x for phy%d\n",
-                   __FUNCTION__, phy->asd_port->phy_mask, sas_phy->id);
+                   __func__, phy->asd_port->phy_mask, sas_phy->id);
        asd_update_port_links(asd_ha, phy);
        spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
 }
@@ -294,7 +294,7 @@ static void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
                struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,
                                                          GFP_ATOMIC);
                if (!cp) {
-                       asd_printk("%s: out of memory\n", __FUNCTION__);
+                       asd_printk("%s: out of memory\n", __func__);
                        goto out;
                }
                ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",
@@ -446,7 +446,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
                struct domain_device *failed_dev = NULL;
 
                ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n",
-                           __FUNCTION__, dl->status_block[3]);
+                           __func__, dl->status_block[3]);
 
                /*
                 * Find the task that caused the abort and abort it first.
@@ -474,7 +474,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
 
                if (!failed_dev) {
                        ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n",
-                                   __FUNCTION__, tc_abort);
+                                   __func__, tc_abort);
                        goto out;
                }
 
@@ -502,7 +502,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
                conn_handle = *((u16*)(&dl->status_block[1]));
                conn_handle = le16_to_cpu(conn_handle);
 
-               ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__,
+               ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __func__,
                            dl->status_block[3]);
 
                /* Find the last pending task for the device... */
@@ -522,7 +522,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
 
                if (!last_dev_task) {
                        ASD_DPRINTK("%s: Device reset for idle device %d?\n",
-                                   __FUNCTION__, conn_handle);
+                                   __func__, conn_handle);
                        goto out;
                }
 
@@ -549,10 +549,10 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
                goto out;
        }
        case SIGNAL_NCQ_ERROR:
-               ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__);
+               ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __func__);
                goto out;
        case CLEAR_NCQ_ERROR:
-               ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__);
+               ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __func__);
                goto out;
        }
 
@@ -560,26 +560,26 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
 
        switch (sb_opcode) {
        case BYTES_DMAED:
-               ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id);
+               ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __func__, phy_id);
                asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
                break;
        case PRIMITIVE_RECVD:
-               ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__,
+               ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __func__,
                            phy_id);
                asd_primitive_rcvd_tasklet(ascb, dl, phy_id);
                break;
        case PHY_EVENT:
-               ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id);
+               ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __func__, phy_id);
                asd_phy_event_tasklet(ascb, dl);
                break;
        case LINK_RESET_ERROR:
-               ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__,
+               ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __func__,
                            phy_id);
                asd_link_reset_err_tasklet(ascb, dl, phy_id);
                break;
        case TIMER_EVENT:
                ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n",
-                           __FUNCTION__, phy_id);
+                           __func__, phy_id);
                asd_turn_led(asd_ha, phy_id, 0);
                /* the device is gone */
                sas_phy_disconnected(sas_phy);
@@ -587,7 +587,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
                sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT);
                break;
        default:
-               ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__,
+               ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __func__,
                            phy_id, sb_opcode);
                ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
                            edb, dl->opcode);
@@ -654,7 +654,7 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb,
 
        if (status != 0) {
                ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n",
-                           __FUNCTION__, phy_id, status);
+                           __func__, phy_id, status);
                goto out;
        }
 
@@ -663,7 +663,7 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb,
                asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id);
                asd_turn_led(asd_ha, phy_id, 0);
                asd_control_led(asd_ha, phy_id, 0);
-               ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id);
+               ASD_DPRINTK("%s: disable phy%d\n", __func__, phy_id);
                break;
 
        case ENABLE_PHY:
@@ -673,40 +673,40 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb,
                        get_lrate_mode(phy, oob_mode);
                        asd_turn_led(asd_ha, phy_id, 1);
                        ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n",
-                                   __FUNCTION__, phy_id,phy->sas_phy.linkrate,
+                                   __func__, phy_id,phy->sas_phy.linkrate,
                                    phy->sas_phy.iproto);
                } else if (oob_status & CURRENT_SPINUP_HOLD) {
                        asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
                        asd_turn_led(asd_ha, phy_id, 1);
-                       ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__,
+                       ASD_DPRINTK("%s: phy%d, spinup hold\n", __func__,
                                    phy_id);
                } else if (oob_status & CURRENT_ERR_MASK) {
                        asd_turn_led(asd_ha, phy_id, 0);
                        ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n",
-                                   __FUNCTION__, phy_id, oob_status);
+                                   __func__, phy_id, oob_status);
                } else if (oob_status & (CURRENT_HOT_PLUG_CNCT
                                         | CURRENT_DEVICE_PRESENT))  {
                        asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
                        asd_turn_led(asd_ha, phy_id, 1);
                        ASD_DPRINTK("%s: phy%d: hot plug or device present\n",
-                                   __FUNCTION__, phy_id);
+                                   __func__, phy_id);
                } else {
                        asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
                        asd_turn_led(asd_ha, phy_id, 0);
                        ASD_DPRINTK("%s: phy%d: no device present: "
                                    "oob_status:0x%x\n",
-                                   __FUNCTION__, phy_id, oob_status);
+                                   __func__, phy_id, oob_status);
                }
                break;
        case RELEASE_SPINUP_HOLD:
        case PHY_NO_OP:
        case EXECUTE_HARD_RESET:
-               ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__,
+               ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __func__,
                            phy_id, control_phy->sub_func);
                /* XXX finish */
                break;
        default:
-               ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__,
+               ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __func__,
                            phy_id, control_phy->sub_func);
                break;
        }
index 326765c9caf869a00e7fd000dc075075eb03cfce..75d20f72501feaaf7c631a6cf791da9284ea93f5 100644 (file)
@@ -320,7 +320,7 @@ Again:
        case TC_RESUME:
        case TC_PARTIAL_SG_LIST:
        default:
-               ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode);
+               ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __func__, opcode);
                break;
        }
 
index 633ff40c736a69f0b9ebc8ad2abd97adb14b981f..d4640ef6d44fcc00e56b95d88ad9906e8934bb4b 100644 (file)
@@ -75,12 +75,12 @@ static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
                                             struct done_list_struct *dl)
 {
        struct tasklet_completion_status *tcs = ascb->uldd_task;
-       ASD_DPRINTK("%s: here\n", __FUNCTION__);
+       ASD_DPRINTK("%s: here\n", __func__);
        if (!del_timer(&ascb->timer)) {
-               ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__);
+               ASD_DPRINTK("%s: couldn't delete timer\n", __func__);
                return;
        }
-       ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode);
+       ASD_DPRINTK("%s: opcode: 0x%x\n", __func__, dl->opcode);
        tcs->dl_opcode = dl->opcode;
        complete(ascb->completion);
        asd_ascb_free(ascb);
@@ -91,7 +91,7 @@ static void asd_clear_nexus_timedout(unsigned long data)
        struct asd_ascb *ascb = (void *)data;
        struct tasklet_completion_status *tcs = ascb->uldd_task;
 
-       ASD_DPRINTK("%s: here\n", __FUNCTION__);
+       ASD_DPRINTK("%s: here\n", __func__);
        tcs->dl_opcode = TMF_RESP_FUNC_FAILED;
        complete(ascb->completion);
 }
@@ -103,7 +103,7 @@ static void asd_clear_nexus_timedout(unsigned long data)
        DECLARE_COMPLETION_ONSTACK(completion); \
        DECLARE_TCS(tcs); \
                \
-       ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \
+       ASD_DPRINTK("%s: PRE\n", __func__); \
         res = 1;                \
        ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
        if (!ascb)              \
@@ -115,12 +115,12 @@ static void asd_clear_nexus_timedout(unsigned long data)
        scb->header.opcode = CLEAR_NEXUS
 
 #define CLEAR_NEXUS_POST        \
-       ASD_DPRINTK("%s: POST\n", __FUNCTION__); \
+       ASD_DPRINTK("%s: POST\n", __func__); \
        res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
                                   asd_clear_nexus_timedout);              \
        if (res)                \
                goto out_err;   \
-       ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \
+       ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __func__); \
        wait_for_completion(&completion); \
        res = tcs.dl_opcode; \
        if (res == TC_NO_ERROR) \
@@ -417,7 +417,7 @@ int asd_abort_task(struct sas_task *task)
        if (task->task_state_flags & SAS_TASK_STATE_DONE) {
                spin_unlock_irqrestore(&task->task_state_lock, flags);
                res = TMF_RESP_FUNC_COMPLETE;
-               ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
+               ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
                goto out_done;
        }
        spin_unlock_irqrestore(&task->task_state_lock, flags);
@@ -481,7 +481,7 @@ int asd_abort_task(struct sas_task *task)
        if (task->task_state_flags & SAS_TASK_STATE_DONE) {
                spin_unlock_irqrestore(&task->task_state_lock, flags);
                res = TMF_RESP_FUNC_COMPLETE;
-               ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
+               ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
                goto out_done;
        }
        spin_unlock_irqrestore(&task->task_state_lock, flags);
index a715632e19d472ec61956f87c12a1c61f8fbc4a5..477542602284661dbcb7d0d65d65223d612d3ec4 100644 (file)
@@ -240,7 +240,7 @@ static void __fas216_checkmagic(FAS216_Info *info, const char *func)
                panic("scsi memory space corrupted in %s", func);
        }
 }
-#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__)
+#define fas216_checkmagic(info) __fas216_checkmagic((info), __func__)
 #else
 #define fas216_checkmagic(info)
 #endif
@@ -2658,7 +2658,7 @@ int fas216_eh_host_reset(struct scsi_cmnd *SCpnt)
        fas216_checkmagic(info);
 
        printk("scsi%d.%c: %s: resetting host\n",
-               info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__);
+               info->host->host_no, '0' + SCpnt->device->id, __func__);
 
        /*
         * Reset the SCSI chip.
index aa2011b6468387609993e2ab7643b8e40740c403..3c257fe0893e51c9740f226b752a559321cb95a4 100644 (file)
@@ -930,6 +930,7 @@ static int ch_probe(struct device *dev)
        if (init)
                ch_init_elem(ch);
 
+       dev_set_drvdata(dev, ch);
        sdev_printk(KERN_INFO, sd, "Attached scsi changer %s\n", ch->name);
 
        return 0;
index 2adc0f666b682fbd8e738299a4b385a8505761bc..67070257919fcbca074c23b177e97ba6484e6217 100644 (file)
@@ -30,3 +30,11 @@ config SCSI_DH_EMC
        depends on SCSI_DH
        help
        If you have a EMC CLARiiON select y. Otherwise, say N.
+
+config SCSI_DH_ALUA
+       tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)"
+       depends on SCSI_DH && EXPERIMENTAL
+       help
+         SCSI Device handler for generic SPC-3 Asymmetric Logical Unit
+         Access (ALUA).
+
index 35272e93b1c82578c624a8213c692f221eaa9426..e1d2ea083e159d0585d783a8c8757acb93ad0300 100644 (file)
@@ -5,3 +5,4 @@ obj-$(CONFIG_SCSI_DH)           += scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)     += scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)    += scsi_dh_hp_sw.o
 obj-$(CONFIG_SCSI_DH_EMC)      += scsi_dh_emc.o
+obj-$(CONFIG_SCSI_DH_ALUA)     += scsi_dh_alua.o
index ab6c21cd9689c6c3ff27987fc53cacd138861dbd..a518f2eff19af1a7dcf86383f287940487ce80f3 100644 (file)
 #include <scsi/scsi_dh.h>
 #include "../scsi_priv.h"
 
+struct scsi_dh_devinfo_list {
+       struct list_head node;
+       char vendor[9];
+       char model[17];
+       struct scsi_device_handler *handler;
+};
+
 static DEFINE_SPINLOCK(list_lock);
 static LIST_HEAD(scsi_dh_list);
+static LIST_HEAD(scsi_dh_dev_list);
 
 static struct scsi_device_handler *get_device_handler(const char *name)
 {
@@ -33,7 +41,7 @@ static struct scsi_device_handler *get_device_handler(const char *name)
 
        spin_lock(&list_lock);
        list_for_each_entry(tmp, &scsi_dh_list, list) {
-               if (!strcmp(tmp->name, name)) {
+               if (!strncmp(tmp->name, name, strlen(tmp->name))) {
                        found = tmp;
                        break;
                }
@@ -42,11 +50,307 @@ static struct scsi_device_handler *get_device_handler(const char *name)
        return found;
 }
 
+
+static struct scsi_device_handler *
+scsi_dh_cache_lookup(struct scsi_device *sdev)
+{
+       struct scsi_dh_devinfo_list *tmp;
+       struct scsi_device_handler *found_dh = NULL;
+
+       spin_lock(&list_lock);
+       list_for_each_entry(tmp, &scsi_dh_dev_list, node) {
+               if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) &&
+                   !strncmp(sdev->model, tmp->model, strlen(tmp->model))) {
+                       found_dh = tmp->handler;
+                       break;
+               }
+       }
+       spin_unlock(&list_lock);
+
+       return found_dh;
+}
+
+static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh,
+                                 struct scsi_device *sdev)
+{
+       int i, found = 0;
+
+       for(i = 0; scsi_dh->devlist[i].vendor; i++) {
+               if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor,
+                            strlen(scsi_dh->devlist[i].vendor)) &&
+                   !strncmp(sdev->model, scsi_dh->devlist[i].model,
+                            strlen(scsi_dh->devlist[i].model))) {
+                       found = 1;
+                       break;
+               }
+       }
+       return found;
+}
+
+/*
+ * device_handler_match - Attach a device handler to a device
+ * @scsi_dh - The device handler to match against or NULL
+ * @sdev - SCSI device to be tested against @scsi_dh
+ *
+ * Tests @sdev against the device handler @scsi_dh or against
+ * all registered device_handler if @scsi_dh == NULL.
+ * Returns the found device handler or NULL if not found.
+ */
+static struct scsi_device_handler *
+device_handler_match(struct scsi_device_handler *scsi_dh,
+                    struct scsi_device *sdev)
+{
+       struct scsi_device_handler *found_dh = NULL;
+       struct scsi_dh_devinfo_list *tmp;
+
+       found_dh = scsi_dh_cache_lookup(sdev);
+       if (found_dh)
+               return found_dh;
+
+       if (scsi_dh) {
+               if (scsi_dh_handler_lookup(scsi_dh, sdev))
+                       found_dh = scsi_dh;
+       } else {
+               struct scsi_device_handler *tmp_dh;
+
+               spin_lock(&list_lock);
+               list_for_each_entry(tmp_dh, &scsi_dh_list, list) {
+                       if (scsi_dh_handler_lookup(tmp_dh, sdev))
+                               found_dh = tmp_dh;
+               }
+               spin_unlock(&list_lock);
+       }
+
+       if (found_dh) { /* If device is found, add it to the cache */
+               tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+               if (tmp) {
+                       strncpy(tmp->vendor, sdev->vendor, 8);
+                       strncpy(tmp->model, sdev->model, 16);
+                       tmp->vendor[8] = '\0';
+                       tmp->model[16] = '\0';
+                       tmp->handler = found_dh;
+                       spin_lock(&list_lock);
+                       list_add(&tmp->node, &scsi_dh_dev_list);
+                       spin_unlock(&list_lock);
+               } else {
+                       found_dh = NULL;
+               }
+       }
+
+       return found_dh;
+}
+
+/*
+ * scsi_dh_handler_attach - Attach a device handler to a device
+ * @sdev - SCSI device the device handler should attach to
+ * @scsi_dh - The device handler to attach
+ */
+static int scsi_dh_handler_attach(struct scsi_device *sdev,
+                                 struct scsi_device_handler *scsi_dh)
+{
+       int err = 0;
+
+       if (sdev->scsi_dh_data) {
+               if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
+                       err = -EBUSY;
+       } else if (scsi_dh->attach)
+               err = scsi_dh->attach(sdev);
+
+       return err;
+}
+
+/*
+ * scsi_dh_handler_detach - Detach a device handler from a device
+ * @sdev - SCSI device the device handler should be detached from
+ * @scsi_dh - Device handler to be detached
+ *
+ * Detach from a device handler. If a device handler is specified,
+ * only detach if the currently attached handler matches @scsi_dh.
+ */
+static void scsi_dh_handler_detach(struct scsi_device *sdev,
+                                  struct scsi_device_handler *scsi_dh)
+{
+       if (!sdev->scsi_dh_data)
+               return;
+
+       if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh)
+               return;
+
+       if (!scsi_dh)
+               scsi_dh = sdev->scsi_dh_data->scsi_dh;
+
+       if (scsi_dh && scsi_dh->detach)
+               scsi_dh->detach(sdev);
+}
+
+/*
+ * Functions for sysfs attribute 'dh_state'
+ */
+static ssize_t
+store_dh_state(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       struct scsi_device_handler *scsi_dh;
+       int err = -EINVAL;
+
+       if (!sdev->scsi_dh_data) {
+               /*
+                * Attach to a device handler
+                */
+               if (!(scsi_dh = get_device_handler(buf)))
+                       return err;
+               err = scsi_dh_handler_attach(sdev, scsi_dh);
+       } else {
+               scsi_dh = sdev->scsi_dh_data->scsi_dh;
+               if (!strncmp(buf, "detach", 6)) {
+                       /*
+                        * Detach from a device handler
+                        */
+                       scsi_dh_handler_detach(sdev, scsi_dh);
+                       err = 0;
+               } else if (!strncmp(buf, "activate", 8)) {
+                       /*
+                        * Activate a device handler
+                        */
+                       if (scsi_dh->activate)
+                               err = scsi_dh->activate(sdev);
+                       else
+                               err = 0;
+               }
+       }
+
+       return err<0?err:count;
+}
+
+static ssize_t
+show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+
+       if (!sdev->scsi_dh_data)
+               return snprintf(buf, 20, "detached\n");
+
+       return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name);
+}
+
+static struct device_attribute scsi_dh_state_attr =
+       __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
+              store_dh_state);
+
+/*
+ * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh
+ */
+static int scsi_dh_sysfs_attr_add(struct device *dev, void *data)
+{
+       struct scsi_device *sdev;
+       int err;
+
+       if (!scsi_is_sdev_device(dev))
+               return 0;
+
+       sdev = to_scsi_device(dev);
+
+       err = device_create_file(&sdev->sdev_gendev,
+                                &scsi_dh_state_attr);
+
+       return 0;
+}
+
+/*
+ * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh
+ */
+static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data)
+{
+       struct scsi_device *sdev;
+
+       if (!scsi_is_sdev_device(dev))
+               return 0;
+
+       sdev = to_scsi_device(dev);
+
+       device_remove_file(&sdev->sdev_gendev,
+                          &scsi_dh_state_attr);
+
+       return 0;
+}
+
+/*
+ * scsi_dh_notifier - notifier chain callback
+ */
+static int scsi_dh_notifier(struct notifier_block *nb,
+                           unsigned long action, void *data)
+{
+       struct device *dev = data;
+       struct scsi_device *sdev;
+       int err = 0;
+       struct scsi_device_handler *devinfo = NULL;
+
+       if (!scsi_is_sdev_device(dev))
+               return 0;
+
+       sdev = to_scsi_device(dev);
+
+       if (action == BUS_NOTIFY_ADD_DEVICE) {
+               devinfo = device_handler_match(NULL, sdev);
+               if (!devinfo)
+                       goto out;
+
+               err = scsi_dh_handler_attach(sdev, devinfo);
+               if (!err)
+                       err = device_create_file(dev, &scsi_dh_state_attr);
+       } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+               device_remove_file(dev, &scsi_dh_state_attr);
+               scsi_dh_handler_detach(sdev, NULL);
+       }
+out:
+       return err;
+}
+
+/*
+ * scsi_dh_notifier_add - Callback for scsi_register_device_handler
+ */
 static int scsi_dh_notifier_add(struct device *dev, void *data)
 {
        struct scsi_device_handler *scsi_dh = data;
+       struct scsi_device *sdev;
+
+       if (!scsi_is_sdev_device(dev))
+               return 0;
+
+       if (!get_device(dev))
+               return 0;
+
+       sdev = to_scsi_device(dev);
+
+       if (device_handler_match(scsi_dh, sdev))
+               scsi_dh_handler_attach(sdev, scsi_dh);
+
+       put_device(dev);
+
+       return 0;
+}
+
+/*
+ * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
+ */
+static int scsi_dh_notifier_remove(struct device *dev, void *data)
+{
+       struct scsi_device_handler *scsi_dh = data;
+       struct scsi_device *sdev;
+
+       if (!scsi_is_sdev_device(dev))
+               return 0;
+
+       if (!get_device(dev))
+               return 0;
+
+       sdev = to_scsi_device(dev);
+
+       scsi_dh_handler_detach(sdev, scsi_dh);
+
+       put_device(dev);
 
-       scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
        return 0;
 }
 
@@ -59,33 +363,19 @@ static int scsi_dh_notifier_add(struct device *dev, void *data)
  */
 int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
 {
-       int ret = -EBUSY;
-       struct scsi_device_handler *tmp;
+       if (get_device_handler(scsi_dh->name))
+               return -EBUSY;
 
-       tmp = get_device_handler(scsi_dh->name);
-       if (tmp)
-               goto done;
-
-       ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
-
-       bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
        spin_lock(&list_lock);
        list_add(&scsi_dh->list, &scsi_dh_list);
        spin_unlock(&list_lock);
+       bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
+       printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
 
-done:
-       return ret;
+       return SCSI_DH_OK;
 }
 EXPORT_SYMBOL_GPL(scsi_register_device_handler);
 
-static int scsi_dh_notifier_remove(struct device *dev, void *data)
-{
-       struct scsi_device_handler *scsi_dh = data;
-
-       scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
-       return 0;
-}
-
 /*
  * scsi_unregister_device_handler - register a device handler personality
  *      module.
@@ -95,23 +385,26 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data)
  */
 int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
 {
-       int ret = -ENODEV;
-       struct scsi_device_handler *tmp;
-
-       tmp = get_device_handler(scsi_dh->name);
-       if (!tmp)
-               goto done;
+       struct scsi_dh_devinfo_list *tmp, *pos;
 
-       ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
+       if (!get_device_handler(scsi_dh->name))
+               return -ENODEV;
 
        bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
-                                       scsi_dh_notifier_remove);
+                        scsi_dh_notifier_remove);
+
        spin_lock(&list_lock);
        list_del(&scsi_dh->list);
+       list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) {
+               if (pos->handler == scsi_dh) {
+                       list_del(&pos->node);
+                       kfree(pos);
+               }
+       }
        spin_unlock(&list_lock);
+       printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
 
-done:
-       return ret;
+       return SCSI_DH_OK;
 }
 EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
 
@@ -157,6 +450,97 @@ int scsi_dh_handler_exist(const char *name)
 }
 EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
 
+/*
+ * scsi_dh_handler_attach - Attach device handler
+ * @sdev - sdev the handler should be attached to
+ * @name - name of the handler to attach
+ */
+int scsi_dh_attach(struct request_queue *q, const char *name)
+{
+       unsigned long flags;
+       struct scsi_device *sdev;
+       struct scsi_device_handler *scsi_dh;
+       int err = 0;
+
+       scsi_dh = get_device_handler(name);
+       if (!scsi_dh)
+               return -EINVAL;
+
+       spin_lock_irqsave(q->queue_lock, flags);
+       sdev = q->queuedata;
+       if (!sdev || !get_device(&sdev->sdev_gendev))
+               err = -ENODEV;
+       spin_unlock_irqrestore(q->queue_lock, flags);
+
+       if (!err) {
+               err = scsi_dh_handler_attach(sdev, scsi_dh);
+
+               put_device(&sdev->sdev_gendev);
+       }
+       return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_attach);
+
+/*
+ * scsi_dh_handler_detach - Detach device handler
+ * @sdev - sdev the handler should be detached from
+ *
+ * This function will detach the device handler only
+ * if the sdev is not part of the internal list, ie
+ * if it has been attached manually.
+ */
+void scsi_dh_detach(struct request_queue *q)
+{
+       unsigned long flags;
+       struct scsi_device *sdev;
+       struct scsi_device_handler *scsi_dh = NULL;
+
+       spin_lock_irqsave(q->queue_lock, flags);
+       sdev = q->queuedata;
+       if (!sdev || !get_device(&sdev->sdev_gendev))
+               sdev = NULL;
+       spin_unlock_irqrestore(q->queue_lock, flags);
+
+       if (!sdev)
+               return;
+
+       if (sdev->scsi_dh_data) {
+               /* if sdev is not on internal list, detach */
+               scsi_dh = sdev->scsi_dh_data->scsi_dh;
+               if (!device_handler_match(scsi_dh, sdev))
+                       scsi_dh_handler_detach(sdev, scsi_dh);
+       }
+       put_device(&sdev->sdev_gendev);
+}
+EXPORT_SYMBOL_GPL(scsi_dh_detach);
+
+static struct notifier_block scsi_dh_nb = {
+       .notifier_call = scsi_dh_notifier
+};
+
+static int __init scsi_dh_init(void)
+{
+       int r;
+
+       r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
+
+       if (!r)
+               bus_for_each_dev(&scsi_bus_type, NULL, NULL,
+                                scsi_dh_sysfs_attr_add);
+
+       return r;
+}
+
+static void __exit scsi_dh_exit(void)
+{
+       bus_for_each_dev(&scsi_bus_type, NULL, NULL,
+                        scsi_dh_sysfs_attr_remove);
+       bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
+}
+
+module_init(scsi_dh_init);
+module_exit(scsi_dh_exit);
+
 MODULE_DESCRIPTION("SCSI device handler");
 MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
 MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
new file mode 100644 (file)
index 0000000..fcdd73f
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+ * Generic SCSI-3 ALUA SCSI Device Handler
+ *
+ * Copyright (C) 2007, 2008 Hannes Reinecke, SUSE Linux Products GmbH.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define ALUA_DH_NAME "alua"
+#define ALUA_DH_VER "1.2"
+
+#define TPGS_STATE_OPTIMIZED           0x0
+#define TPGS_STATE_NONOPTIMIZED                0x1
+#define TPGS_STATE_STANDBY             0x2
+#define TPGS_STATE_UNAVAILABLE         0x3
+#define TPGS_STATE_OFFLINE             0xe
+#define TPGS_STATE_TRANSITIONING       0xf
+
+#define TPGS_SUPPORT_NONE              0x00
+#define TPGS_SUPPORT_OPTIMIZED         0x01
+#define TPGS_SUPPORT_NONOPTIMIZED      0x02
+#define TPGS_SUPPORT_STANDBY           0x04
+#define TPGS_SUPPORT_UNAVAILABLE       0x08
+#define TPGS_SUPPORT_OFFLINE           0x40
+#define TPGS_SUPPORT_TRANSITION                0x80
+
+#define TPGS_MODE_UNINITIALIZED                 -1
+#define TPGS_MODE_NONE                 0x0
+#define TPGS_MODE_IMPLICIT             0x1
+#define TPGS_MODE_EXPLICIT             0x2
+
+#define ALUA_INQUIRY_SIZE              36
+#define ALUA_FAILOVER_TIMEOUT          (60 * HZ)
+#define ALUA_FAILOVER_RETRIES          5
+
+struct alua_dh_data {
+       int                     group_id;
+       int                     rel_port;
+       int                     tpgs;
+       int                     state;
+       unsigned char           inq[ALUA_INQUIRY_SIZE];
+       unsigned char           *buff;
+       int                     bufflen;
+       unsigned char           sense[SCSI_SENSE_BUFFERSIZE];
+       int                     senselen;
+};
+
+#define ALUA_POLICY_SWITCH_CURRENT     0
+#define ALUA_POLICY_SWITCH_ALL         1
+
+static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
+{
+       struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+       BUG_ON(scsi_dh_data == NULL);
+       return ((struct alua_dh_data *) scsi_dh_data->buf);
+}
+
+static int realloc_buffer(struct alua_dh_data *h, unsigned len)
+{
+       if (h->buff && h->buff != h->inq)
+               kfree(h->buff);
+
+       h->buff = kmalloc(len, GFP_NOIO);
+       if (!h->buff) {
+               h->buff = h->inq;
+               h->bufflen = ALUA_INQUIRY_SIZE;
+               return 1;
+       }
+       h->bufflen = len;
+       return 0;
+}
+
+static struct request *get_alua_req(struct scsi_device *sdev,
+                                   void *buffer, unsigned buflen, int rw)
+{
+       struct request *rq;
+       struct request_queue *q = sdev->request_queue;
+
+       rq = blk_get_request(q, rw, GFP_NOIO);
+
+       if (!rq) {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: blk_get_request failed\n", __func__);
+               return NULL;
+       }
+
+       if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) {
+               blk_put_request(rq);
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: blk_rq_map_kern failed\n", __func__);
+               return NULL;
+       }
+
+       rq->cmd_type = REQ_TYPE_BLOCK_PC;
+       rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
+       rq->retries = ALUA_FAILOVER_RETRIES;
+       rq->timeout = ALUA_FAILOVER_TIMEOUT;
+
+       return rq;
+}
+
+/*
+ * submit_std_inquiry - Issue a standard INQUIRY command
+ * @sdev: sdev the command should be send to
+ */
+static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       struct request *rq;
+       int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+       rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ);
+       if (!rq)
+               goto done;
+
+       /* Prepare the command. */
+       rq->cmd[0] = INQUIRY;
+       rq->cmd[1] = 0;
+       rq->cmd[2] = 0;
+       rq->cmd[4] = ALUA_INQUIRY_SIZE;
+       rq->cmd_len = COMMAND_SIZE(INQUIRY);
+
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = h->senselen = 0;
+
+       err = blk_execute_rq(rq->q, NULL, rq, 1);
+       if (err == -EIO) {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: std inquiry failed with %x\n",
+                           ALUA_DH_NAME, rq->errors);
+               h->senselen = rq->sense_len;
+               err = SCSI_DH_IO;
+       }
+       blk_put_request(rq);
+done:
+       return err;
+}
+
+/*
+ * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command
+ * @sdev: sdev the command should be sent to
+ */
+static int submit_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       struct request *rq;
+       int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+       rq = get_alua_req(sdev, h->buff, h->bufflen, READ);
+       if (!rq)
+               goto done;
+
+       /* Prepare the command. */
+       rq->cmd[0] = INQUIRY;
+       rq->cmd[1] = 1;
+       rq->cmd[2] = 0x83;
+       rq->cmd[4] = h->bufflen;
+       rq->cmd_len = COMMAND_SIZE(INQUIRY);
+
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = h->senselen = 0;
+
+       err = blk_execute_rq(rq->q, NULL, rq, 1);
+       if (err == -EIO) {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: evpd inquiry failed with %x\n",
+                           ALUA_DH_NAME, rq->errors);
+               h->senselen = rq->sense_len;
+               err = SCSI_DH_IO;
+       }
+       blk_put_request(rq);
+done:
+       return err;
+}
+
+/*
+ * submit_rtpg - Issue a REPORT TARGET GROUP STATES command
+ * @sdev: sdev the command should be sent to
+ */
+static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       struct request *rq;
+       int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+       rq = get_alua_req(sdev, h->buff, h->bufflen, READ);
+       if (!rq)
+               goto done;
+
+       /* Prepare the command. */
+       rq->cmd[0] = MAINTENANCE_IN;
+       rq->cmd[1] = MI_REPORT_TARGET_PGS;
+       rq->cmd[6] = (h->bufflen >> 24) & 0xff;
+       rq->cmd[7] = (h->bufflen >> 16) & 0xff;
+       rq->cmd[8] = (h->bufflen >>  8) & 0xff;
+       rq->cmd[9] = h->bufflen & 0xff;
+       rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN);
+
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = h->senselen = 0;
+
+       err = blk_execute_rq(rq->q, NULL, rq, 1);
+       if (err == -EIO) {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: rtpg failed with %x\n",
+                           ALUA_DH_NAME, rq->errors);
+               h->senselen = rq->sense_len;
+               err = SCSI_DH_IO;
+       }
+       blk_put_request(rq);
+done:
+       return err;
+}
+
+/*
+ * submit_stpg - Issue a SET TARGET GROUP STATES command
+ * @sdev: sdev the command should be sent to
+ *
+ * Currently we're only setting the current target port group state
+ * to 'active/optimized' and let the array firmware figure out
+ * the states of the remaining groups.
+ */
+static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       struct request *rq;
+       int err = SCSI_DH_RES_TEMP_UNAVAIL;
+       int stpg_len = 8;
+
+       /* Prepare the data buffer */
+       memset(h->buff, 0, stpg_len);
+       h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f;
+       h->buff[6] = (h->group_id >> 8) & 0x0f;
+       h->buff[7] = h->group_id & 0x0f;
+
+       rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
+       if (!rq)
+               goto done;
+
+       /* Prepare the command. */
+       rq->cmd[0] = MAINTENANCE_OUT;
+       rq->cmd[1] = MO_SET_TARGET_PGS;
+       rq->cmd[6] = (stpg_len >> 24) & 0xff;
+       rq->cmd[7] = (stpg_len >> 16) & 0xff;
+       rq->cmd[8] = (stpg_len >>  8) & 0xff;
+       rq->cmd[9] = stpg_len & 0xff;
+       rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT);
+
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = h->senselen = 0;
+
+       err = blk_execute_rq(rq->q, NULL, rq, 1);
+       if (err == -EIO) {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: stpg failed with %x\n",
+                           ALUA_DH_NAME, rq->errors);
+               h->senselen = rq->sense_len;
+               err = SCSI_DH_IO;
+       }
+       blk_put_request(rq);
+done:
+       return err;
+}
+
+/*
+ * alua_std_inquiry - Evaluate standard INQUIRY command
+ * @sdev: device to be checked
+ *
+ * Just extract the TPGS setting to find out if ALUA
+ * is supported.
+ */
+static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       int err;
+
+       err = submit_std_inquiry(sdev, h);
+
+       if (err != SCSI_DH_OK)
+               return err;
+
+       /* Check TPGS setting */
+       h->tpgs = (h->inq[5] >> 4) & 0x3;
+       switch (h->tpgs) {
+       case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: supports implicit and explicit TPGS\n",
+                           ALUA_DH_NAME);
+               break;
+       case TPGS_MODE_EXPLICIT:
+               sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n",
+                           ALUA_DH_NAME);
+               break;
+       case TPGS_MODE_IMPLICIT:
+               sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n",
+                           ALUA_DH_NAME);
+               break;
+       default:
+               h->tpgs = TPGS_MODE_NONE;
+               sdev_printk(KERN_INFO, sdev, "%s: not supported\n",
+                           ALUA_DH_NAME);
+               err = SCSI_DH_DEV_UNSUPP;
+               break;
+       }
+
+       return err;
+}
+
+/*
+ * alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83
+ * @sdev: device to be checked
+ *
+ * Extract the relative target port and the target port group
+ * descriptor from the list of identificators.
+ */
+static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       int len;
+       unsigned err;
+       unsigned char *d;
+
+ retry:
+       err = submit_vpd_inquiry(sdev, h);
+
+       if (err != SCSI_DH_OK)
+               return err;
+
+       /* Check if vpd page exceeds initial buffer */
+       len = (h->buff[2] << 8) + h->buff[3] + 4;
+       if (len > h->bufflen) {
+               /* Resubmit with the correct length */
+               if (realloc_buffer(h, len)) {
+                       sdev_printk(KERN_WARNING, sdev,
+                                   "%s: kmalloc buffer failed\n",
+                                   ALUA_DH_NAME);
+                       /* Temporary failure, bypass */
+                       return SCSI_DH_DEV_TEMP_BUSY;
+               }
+               goto retry;
+       }
+
+       /*
+        * Now look for the correct descriptor.
+        */
+       d = h->buff + 4;
+       while (d < h->buff + len) {
+               switch (d[1] & 0xf) {
+               case 0x4:
+                       /* Relative target port */
+                       h->rel_port = (d[6] << 8) + d[7];
+                       break;
+               case 0x5:
+                       /* Target port group */
+                       h->group_id = (d[6] << 8) + d[7];
+                       break;
+               default:
+                       break;
+               }
+               d += d[3] + 4;
+       }
+
+       if (h->group_id == -1) {
+               /*
+                * Internal error; TPGS supported but required
+                * VPD identification descriptors not present.
+                * Disable ALUA support
+                */
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: No target port descriptors found\n",
+                           ALUA_DH_NAME);
+               h->state = TPGS_STATE_OPTIMIZED;
+               h->tpgs = TPGS_MODE_NONE;
+               err = SCSI_DH_DEV_UNSUPP;
+       } else {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: port group %02x rel port %02x\n",
+                           ALUA_DH_NAME, h->group_id, h->rel_port);
+       }
+
+       return err;
+}
+
+static char print_alua_state(int state)
+{
+       switch (state) {
+       case TPGS_STATE_OPTIMIZED:
+               return 'A';
+       case TPGS_STATE_NONOPTIMIZED:
+               return 'N';
+       case TPGS_STATE_STANDBY:
+               return 'S';
+       case TPGS_STATE_UNAVAILABLE:
+               return 'U';
+       case TPGS_STATE_OFFLINE:
+               return 'O';
+       case TPGS_STATE_TRANSITIONING:
+               return 'T';
+       default:
+               return 'X';
+       }
+}
+
+static int alua_check_sense(struct scsi_device *sdev,
+                           struct scsi_sense_hdr *sense_hdr)
+{
+       switch (sense_hdr->sense_key) {
+       case NOT_READY:
+               if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a)
+                       /*
+                        * LUN Not Accessible - ALUA state transition
+                        */
+                       return NEEDS_RETRY;
+               if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0b)
+                       /*
+                        * LUN Not Accessible -- Target port in standby state
+                        */
+                       return SUCCESS;
+               if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0c)
+                       /*
+                        * LUN Not Accessible -- Target port in unavailable state
+                        */
+                       return SUCCESS;
+               if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x12)
+                       /*
+                        * LUN Not Ready -- Offline
+                        */
+                       return SUCCESS;
+               break;
+       case UNIT_ATTENTION:
+               if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+                       /*
+                        * Power On, Reset, or Bus Device Reset, just retry.
+                        */
+                       return NEEDS_RETRY;
+               if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) {
+                       /*
+                        * ALUA state changed
+                        */
+                       return NEEDS_RETRY;
+               }
+               if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) {
+                       /*
+                        * Implicit ALUA state transition failed
+                        */
+                       return NEEDS_RETRY;
+               }
+               break;
+       }
+
+       return SCSI_RETURN_NOT_HANDLED;
+}
+
+/*
+ * alua_stpg - Evaluate SET TARGET GROUP STATES
+ * @sdev: the device to be evaluated
+ * @state: the new target group state
+ *
+ * Send a SET TARGET GROUP STATES command to the device.
+ * We only have to test here if we should resubmit the command;
+ * any other error is assumed as a failure.
+ */
+static int alua_stpg(struct scsi_device *sdev, int state,
+                    struct alua_dh_data *h)
+{
+       struct scsi_sense_hdr sense_hdr;
+       unsigned err;
+       int retry = ALUA_FAILOVER_RETRIES;
+
+ retry:
+       err = submit_stpg(sdev, h);
+       if (err == SCSI_DH_IO && h->senselen > 0) {
+               err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+                                          &sense_hdr);
+               if (!err)
+                       return SCSI_DH_IO;
+               err = alua_check_sense(sdev, &sense_hdr);
+               if (retry > 0 && err == NEEDS_RETRY) {
+                       retry--;
+                       goto retry;
+               }
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: stpg sense code: %02x/%02x/%02x\n",
+                           ALUA_DH_NAME, sense_hdr.sense_key,
+                           sense_hdr.asc, sense_hdr.ascq);
+               err = SCSI_DH_IO;
+       }
+       if (err == SCSI_DH_OK) {
+               h->state = state;
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: port group %02x switched to state %c\n",
+                           ALUA_DH_NAME, h->group_id,
+                           print_alua_state(h->state) );
+       }
+       return err;
+}
+
+/*
+ * alua_rtpg - Evaluate REPORT TARGET GROUP STATES
+ * @sdev: the device to be evaluated.
+ *
+ * Evaluate the Target Port Group State.
+ * Returns SCSI_DH_DEV_OFFLINED if the path is
+ * found to be unuseable.
+ */
+static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       struct scsi_sense_hdr sense_hdr;
+       int len, k, off, valid_states = 0;
+       char *ucp;
+       unsigned err;
+
+ retry:
+       err = submit_rtpg(sdev, h);
+
+       if (err == SCSI_DH_IO && h->senselen > 0) {
+               err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+                                          &sense_hdr);
+               if (!err)
+                       return SCSI_DH_IO;
+
+               err = alua_check_sense(sdev, &sense_hdr);
+               if (err == NEEDS_RETRY)
+                       goto retry;
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: rtpg sense code %02x/%02x/%02x\n",
+                           ALUA_DH_NAME, sense_hdr.sense_key,
+                           sense_hdr.asc, sense_hdr.ascq);
+               err = SCSI_DH_IO;
+       }
+       if (err != SCSI_DH_OK)
+               return err;
+
+       len = (h->buff[0] << 24) + (h->buff[1] << 16) +
+               (h->buff[2] << 8) + h->buff[3] + 4;
+
+       if (len > h->bufflen) {
+               /* Resubmit with the correct length */
+               if (realloc_buffer(h, len)) {
+                       sdev_printk(KERN_WARNING, sdev,
+                                   "%s: kmalloc buffer failed\n",__func__);
+                       /* Temporary failure, bypass */
+                       return SCSI_DH_DEV_TEMP_BUSY;
+               }
+               goto retry;
+       }
+
+       for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) {
+               if (h->group_id == (ucp[2] << 8) + ucp[3]) {
+                       h->state = ucp[0] & 0x0f;
+                       valid_states = ucp[1];
+               }
+               off = 8 + (ucp[7] * 4);
+       }
+
+       sdev_printk(KERN_INFO, sdev,
+                   "%s: port group %02x state %c supports %c%c%c%c%c%c\n",
+                   ALUA_DH_NAME, h->group_id, print_alua_state(h->state),
+                   valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',
+                   valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',
+                   valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u',
+                   valid_states&TPGS_SUPPORT_STANDBY?'S':'s',
+                   valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n',
+                   valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a');
+
+       if (h->tpgs & TPGS_MODE_EXPLICIT) {
+               switch (h->state) {
+               case TPGS_STATE_TRANSITIONING:
+                       /* State transition, retry */
+                       goto retry;
+                       break;
+               case TPGS_STATE_OFFLINE:
+                       /* Path is offline, fail */
+                       err = SCSI_DH_DEV_OFFLINED;
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               /* Only Implicit ALUA support */
+               if (h->state == TPGS_STATE_OPTIMIZED ||
+                   h->state == TPGS_STATE_NONOPTIMIZED ||
+                   h->state == TPGS_STATE_STANDBY)
+                       /* Useable path if active */
+                       err = SCSI_DH_OK;
+               else
+                       /* Path unuseable for unavailable/offline */
+                       err = SCSI_DH_DEV_OFFLINED;
+       }
+       return err;
+}
+
+/*
+ * alua_initialize - Initialize ALUA state
+ * @sdev: the device to be initialized
+ *
+ * For the prep_fn to work correctly we have
+ * to initialize the ALUA state for the device.
+ */
+static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h)
+{
+       int err;
+
+       err = alua_std_inquiry(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto out;
+
+       err = alua_vpd_inquiry(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto out;
+
+       err = alua_rtpg(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto out;
+
+out:
+       return err;
+}
+
+/*
+ * alua_activate - activate a path
+ * @sdev: device on the path to be activated
+ *
+ * We're currently switching the port group to be activated only and
+ * let the array figure out the rest.
+ * There may be other arrays which require us to switch all port groups
+ * based on a certain policy. But until we actually encounter them it
+ * should be okay.
+ */
+static int alua_activate(struct scsi_device *sdev)
+{
+       struct alua_dh_data *h = get_alua_data(sdev);
+       int err = SCSI_DH_OK;
+
+       if (h->group_id != -1) {
+               err = alua_rtpg(sdev, h);
+               if (err != SCSI_DH_OK)
+                       goto out;
+       }
+
+       if (h->tpgs == TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED)
+               err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h);
+
+out:
+       return err;
+}
+
+/*
+ * alua_prep_fn - request callback
+ *
+ * Fail I/O to all paths not in state
+ * active/optimized or active/non-optimized.
+ */
+static int alua_prep_fn(struct scsi_device *sdev, struct request *req)
+{
+       struct alua_dh_data *h = get_alua_data(sdev);
+       int ret = BLKPREP_OK;
+
+       if (h->state != TPGS_STATE_OPTIMIZED &&
+           h->state != TPGS_STATE_NONOPTIMIZED) {
+               ret = BLKPREP_KILL;
+               req->cmd_flags |= REQ_QUIET;
+       }
+       return ret;
+
+}
+
+const struct scsi_dh_devlist alua_dev_list[] = {
+       {"HP", "MSA VOLUME" },
+       {"HP", "HSV101" },
+       {"HP", "HSV111" },
+       {"HP", "HSV200" },
+       {"HP", "HSV210" },
+       {"HP", "HSV300" },
+       {"IBM", "2107900" },
+       {"IBM", "2145" },
+       {"Pillar", "Axiom" },
+       {NULL, NULL}
+};
+
+static int alua_bus_attach(struct scsi_device *sdev);
+static void alua_bus_detach(struct scsi_device *sdev);
+
+static struct scsi_device_handler alua_dh = {
+       .name = ALUA_DH_NAME,
+       .module = THIS_MODULE,
+       .devlist = alua_dev_list,
+       .attach = alua_bus_attach,
+       .detach = alua_bus_detach,
+       .prep_fn = alua_prep_fn,
+       .check_sense = alua_check_sense,
+       .activate = alua_activate,
+};
+
+/*
+ * alua_bus_attach - Attach device handler
+ * @sdev: device to be attached to
+ */
+static int alua_bus_attach(struct scsi_device *sdev)
+{
+       struct scsi_dh_data *scsi_dh_data;
+       struct alua_dh_data *h;
+       unsigned long flags;
+       int err = SCSI_DH_OK;
+
+       scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+                              + sizeof(*h) , GFP_KERNEL);
+       if (!scsi_dh_data) {
+               sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
+                           ALUA_DH_NAME);
+               return -ENOMEM;
+       }
+
+       scsi_dh_data->scsi_dh = &alua_dh;
+       h = (struct alua_dh_data *) scsi_dh_data->buf;
+       h->tpgs = TPGS_MODE_UNINITIALIZED;
+       h->state = TPGS_STATE_OPTIMIZED;
+       h->group_id = -1;
+       h->rel_port = -1;
+       h->buff = h->inq;
+       h->bufflen = ALUA_INQUIRY_SIZE;
+
+       err = alua_initialize(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto failed;
+
+       if (!try_module_get(THIS_MODULE))
+               goto failed;
+
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       sdev->scsi_dh_data = scsi_dh_data;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+       return 0;
+
+failed:
+       kfree(scsi_dh_data);
+       sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME);
+       return -EINVAL;
+}
+
+/*
+ * alua_bus_detach - Detach device handler
+ * @sdev: device to be detached from
+ */
+static void alua_bus_detach(struct scsi_device *sdev)
+{
+       struct scsi_dh_data *scsi_dh_data;
+       struct alua_dh_data *h;
+       unsigned long flags;
+
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       scsi_dh_data = sdev->scsi_dh_data;
+       sdev->scsi_dh_data = NULL;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+       h = (struct alua_dh_data *) scsi_dh_data->buf;
+       if (h->buff && h->inq != h->buff)
+               kfree(h->buff);
+       kfree(scsi_dh_data);
+       module_put(THIS_MODULE);
+       sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME);
+}
+
+static int __init alua_init(void)
+{
+       int r;
+
+       r = scsi_register_device_handler(&alua_dh);
+       if (r != 0)
+               printk(KERN_ERR "%s: Failed to register scsi device handler",
+                       ALUA_DH_NAME);
+       return r;
+}
+
+static void __exit alua_exit(void)
+{
+       scsi_unregister_device_handler(&alua_dh);
+}
+
+module_init(alua_init);
+module_exit(alua_exit);
+
+MODULE_DESCRIPTION("DM Multipath ALUA support");
+MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ALUA_DH_VER);
index f2467e936e554ea703c60621d42e3dceeaf6760f..aa46b131b20eb44eef41c17c3a086ed6935d24ed 100644 (file)
 #include <scsi/scsi_dh.h>
 #include <scsi/scsi_device.h>
 
-#define CLARIION_NAME                  "emc_clariion"
+#define CLARIION_NAME                  "emc"
 
 #define CLARIION_TRESPASS_PAGE         0x22
-#define CLARIION_BUFFER_SIZE           0x80
+#define CLARIION_BUFFER_SIZE           0xFC
 #define CLARIION_TIMEOUT               (60 * HZ)
 #define CLARIION_RETRIES               3
 #define CLARIION_UNBOUND_LU            -1
+#define CLARIION_SP_A                  0
+#define CLARIION_SP_B                  1
 
-static unsigned char long_trespass[] = {
-       0, 0, 0, 0,
-       CLARIION_TRESPASS_PAGE, /* Page code */
-       0x09,                   /* Page length - 2 */
-       0x81,                   /* Trespass code + Honor reservation bit */
-       0xff, 0xff,             /* Trespass target */
-       0, 0, 0, 0, 0, 0        /* Reserved bytes / unknown */
-};
+/* Flags */
+#define CLARIION_SHORT_TRESPASS                1
+#define CLARIION_HONOR_RESERVATIONS    2
 
-static unsigned char long_trespass_hr[] = {
-       0, 0, 0, 0,
+/* LUN states */
+#define CLARIION_LUN_UNINITIALIZED     -1
+#define CLARIION_LUN_UNBOUND           0
+#define CLARIION_LUN_BOUND             1
+#define CLARIION_LUN_OWNED             2
+
+static unsigned char long_trespass[] = {
+       0, 0, 0, 0, 0, 0, 0, 0,
        CLARIION_TRESPASS_PAGE, /* Page code */
        0x09,                   /* Page length - 2 */
-       0x01,                   /* Trespass code + Honor reservation bit */
+       0x01,                   /* Trespass code */
        0xff, 0xff,             /* Trespass target */
        0, 0, 0, 0, 0, 0        /* Reserved bytes / unknown */
 };
@@ -55,39 +58,56 @@ static unsigned char short_trespass[] = {
        0, 0, 0, 0,
        CLARIION_TRESPASS_PAGE, /* Page code */
        0x02,                   /* Page length - 2 */
-       0x81,                   /* Trespass code + Honor reservation bit */
+       0x01,                   /* Trespass code */
        0xff,                   /* Trespass target */
 };
 
-static unsigned char short_trespass_hr[] = {
-       0, 0, 0, 0,
-       CLARIION_TRESPASS_PAGE, /* Page code */
-       0x02,                   /* Page length - 2 */
-       0x01,                   /* Trespass code + Honor reservation bit */
-       0xff,                   /* Trespass target */
+static const char * lun_state[] =
+{
+    "not bound",
+    "bound",
+    "owned",
 };
 
 struct clariion_dh_data {
        /*
+        * Flags:
+        *  CLARIION_SHORT_TRESPASS
         * Use short trespass command (FC-series) or the long version
         * (default for AX/CX CLARiiON arrays).
-        */
-       unsigned short_trespass;
-       /*
+        *
+        *  CLARIION_HONOR_RESERVATIONS
         * Whether or not (default) to honor SCSI reservations when
         * initiating a switch-over.
         */
-       unsigned hr;
-       /* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+       unsigned flags;
+       /*
+        * I/O buffer for both MODE_SELECT and INQUIRY commands.
+        */
        char buffer[CLARIION_BUFFER_SIZE];
        /*
         * SCSI sense buffer for commands -- assumes serial issuance
         * and completion sequence of all commands for same multipath.
         */
        unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-       /* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+       unsigned int senselen;
+       /*
+        * LUN state
+        */
+       int lun_state;
+       /*
+        * SP Port number
+        */
+       int port;
+       /*
+        * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this
+        * path's mapped LUN
+        */
        int default_sp;
-       /* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+       /*
+        * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this
+        * path's mapped LUN
+        */
        int current_sp;
 };
 
@@ -102,19 +122,16 @@ static inline struct clariion_dh_data
 /*
  * Parse MODE_SELECT cmd reply.
  */
-static int trespass_endio(struct scsi_device *sdev, int result)
+static int trespass_endio(struct scsi_device *sdev, char *sense)
 {
-       int err = SCSI_DH_OK;
+       int err = SCSI_DH_IO;
        struct scsi_sense_hdr sshdr;
-       struct clariion_dh_data *csdev = get_clariion_data(sdev);
-       char *sense = csdev->sense;
 
-       if (status_byte(result) == CHECK_CONDITION &&
-           scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
-               sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+       if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+               sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, "
                            "0x%2x, 0x%2x while sending CLARiiON trespass "
-                           "command.\n", sshdr.sense_key, sshdr.asc,
-                            sshdr.ascq);
+                           "command.\n", CLARIION_NAME, sshdr.sense_key,
+                           sshdr.asc, sshdr.ascq);
 
                if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
                     (sshdr.ascq == 0x00)) {
@@ -122,9 +139,9 @@ static int trespass_endio(struct scsi_device *sdev, int result)
                         * Array based copy in progress -- do not send
                         * mode_select or copy will be aborted mid-stream.
                         */
-                       sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+                       sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in "
                                    "progress while sending CLARiiON trespass "
-                                   "command.\n");
+                                   "command.\n", CLARIION_NAME);
                        err = SCSI_DH_DEV_TEMP_BUSY;
                } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
                            (sshdr.ascq == 0x03)) {
@@ -132,160 +149,153 @@ static int trespass_endio(struct scsi_device *sdev, int result)
                         * LUN Not Ready - Manual Intervention Required
                         * indicates in-progress ucode upgrade (NDU).
                         */
-                       sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+                       sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress "
                                    "ucode upgrade NDU operation while sending "
-                                   "CLARiiON trespass command.\n");
+                                   "CLARiiON trespass command.\n", CLARIION_NAME);
                        err = SCSI_DH_DEV_TEMP_BUSY;
                } else
                        err = SCSI_DH_DEV_FAILED;
-       } else if (result) {
-               sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
-                           "CLARiiON trespass command.\n", result);
-               err = SCSI_DH_IO;
+       } else {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: failed to send MODE SELECT, no sense available\n",
+                           CLARIION_NAME);
        }
-
        return err;
 }
 
-static int parse_sp_info_reply(struct scsi_device *sdev, int result,
-               int *default_sp, int *current_sp, int *new_current_sp)
+static int parse_sp_info_reply(struct scsi_device *sdev,
+                              struct clariion_dh_data *csdev)
 {
        int err = SCSI_DH_OK;
-       struct clariion_dh_data *csdev = get_clariion_data(sdev);
 
-       if (result == 0) {
-               /* check for in-progress ucode upgrade (NDU) */
-               if (csdev->buffer[48] != 0) {
-                       sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
-                              "ucode upgrade NDU operation while finding "
-                              "current active SP.");
-                       err = SCSI_DH_DEV_TEMP_BUSY;
-               } else {
-                       *default_sp = csdev->buffer[5];
-
-                       if (csdev->buffer[4] == 2)
-                               /* SP for path is current */
-                               *current_sp = csdev->buffer[8];
-                       else {
-                               if (csdev->buffer[4] == 1)
-                                       /* SP for this path is NOT current */
-                                       if (csdev->buffer[8] == 0)
-                                               *current_sp = 1;
-                                       else
-                                               *current_sp = 0;
-                               else
-                                       /* unbound LU or LUNZ */
-                                       *current_sp = CLARIION_UNBOUND_LU;
-                       }
-                       *new_current_sp =  csdev->buffer[8];
-               }
-       } else {
-               struct scsi_sense_hdr sshdr;
-
-               err = SCSI_DH_IO;
-
-               if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
-                                                          &sshdr))
-                       sdev_printk(KERN_ERR, sdev, "Found valid sense data "
-                             "0x%2x, 0x%2x, 0x%2x while finding current "
-                             "active SP.", sshdr.sense_key, sshdr.asc,
-                             sshdr.ascq);
-               else
-                       sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
-                             "current active SP.", result);
+       /* check for in-progress ucode upgrade (NDU) */
+       if (csdev->buffer[48] != 0) {
+               sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress "
+                           "ucode upgrade NDU operation while finding "
+                           "current active SP.", CLARIION_NAME);
+               err = SCSI_DH_DEV_TEMP_BUSY;
+               goto out;
+       }
+       if (csdev->buffer[4] < 0 || csdev->buffer[4] > 2) {
+               /* Invalid buffer format */
+               sdev_printk(KERN_NOTICE, sdev,
+                           "%s: invalid VPD page 0xC0 format\n",
+                           CLARIION_NAME);
+               err = SCSI_DH_NOSYS;
+               goto out;
+       }
+       switch (csdev->buffer[28] & 0x0f) {
+       case 6:
+               sdev_printk(KERN_NOTICE, sdev,
+                           "%s: ALUA failover mode detected\n",
+                           CLARIION_NAME);
+               break;
+       case 4:
+               /* Linux failover */
+               break;
+       default:
+               sdev_printk(KERN_WARNING, sdev,
+                           "%s: Invalid failover mode %d\n",
+                           CLARIION_NAME, csdev->buffer[28] & 0x0f);
+               err = SCSI_DH_NOSYS;
+               goto out;
        }
 
+       csdev->default_sp = csdev->buffer[5];
+       csdev->lun_state = csdev->buffer[4];
+       csdev->current_sp = csdev->buffer[8];
+       csdev->port = csdev->buffer[7];
+
+out:
        return err;
 }
 
-static int sp_info_endio(struct scsi_device *sdev, int result,
-                                       int mode_select_sent, int *done)
+#define emc_default_str "FC (Legacy)"
+
+static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer)
 {
-       struct clariion_dh_data *csdev = get_clariion_data(sdev);
-       int err_flags, default_sp, current_sp, new_current_sp;
+       unsigned char len = buffer[4] + 5;
+       char *sp_model = NULL;
+       unsigned char sp_len, serial_len;
+
+       if (len < 160) {
+               sdev_printk(KERN_WARNING, sdev,
+                           "%s: Invalid information section length %d\n",
+                           CLARIION_NAME, len);
+               /* Check for old FC arrays */
+               if (!strncmp(buffer + 8, "DGC", 3)) {
+                       /* Old FC array, not supporting extended information */
+                       sp_model = emc_default_str;
+               }
+               goto out;
+       }
 
-       err_flags = parse_sp_info_reply(sdev, result, &default_sp,
-                                            &current_sp, &new_current_sp);
+       /*
+        * Parse extended information for SP model number
+        */
+       serial_len = buffer[160];
+       if (serial_len == 0 || serial_len + 161 > len) {
+               sdev_printk(KERN_WARNING, sdev,
+                           "%s: Invalid array serial number length %d\n",
+                           CLARIION_NAME, serial_len);
+               goto out;
+       }
+       sp_len = buffer[99];
+       if (sp_len == 0 || serial_len + sp_len + 161 > len) {
+               sdev_printk(KERN_WARNING, sdev,
+                           "%s: Invalid model number length %d\n",
+                           CLARIION_NAME, sp_len);
+               goto out;
+       }
+       sp_model = &buffer[serial_len + 161];
+       /* Strip whitespace at the end */
+       while (sp_len > 1 && sp_model[sp_len - 1] == ' ')
+               sp_len--;
 
-       if (err_flags != SCSI_DH_OK)
-               goto done;
+       sp_model[sp_len] = '\0';
 
-       if (mode_select_sent) {
-               csdev->default_sp = default_sp;
-               csdev->current_sp = current_sp;
-       } else {
-               /*
-                * Issue the actual module_selec request IFF either
-                * (1) we do not know the identity of the current SP OR
-                * (2) what we think we know is actually correct.
-                */
-               if ((current_sp != CLARIION_UNBOUND_LU) &&
-                   (new_current_sp != current_sp)) {
-
-                       csdev->default_sp = default_sp;
-                       csdev->current_sp = current_sp;
-
-                       sdev_printk(KERN_INFO, sdev, "Ignoring path group "
-                              "switch-over command for CLARiiON SP%s since "
-                              " mapped device is already initialized.",
-                              current_sp ? "B" : "A");
-                       if (done)
-                               *done = 1; /* as good as doing it */
-               }
-       }
-done:
-       return err_flags;
+out:
+       return sp_model;
 }
 
 /*
-* Get block request for REQ_BLOCK_PC command issued to path.  Currently
-* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
-*
-* Uses data and sense buffers in hardware handler context structure and
-* assumes serial servicing of commands, both issuance and completion.
-*/
-static struct request *get_req(struct scsi_device *sdev, int cmd)
+ * Get block request for REQ_BLOCK_PC command issued to path.  Currently
+ * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+ *
+ * Uses data and sense buffers in hardware handler context structure and
+ * assumes serial servicing of commands, both issuance and completion.
+ */
+static struct request *get_req(struct scsi_device *sdev, int cmd,
+                               unsigned char *buffer)
 {
-       struct clariion_dh_data *csdev = get_clariion_data(sdev);
        struct request *rq;
-       unsigned char *page22;
        int len = 0;
 
        rq = blk_get_request(sdev->request_queue,
-                       (cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+                       (cmd == MODE_SELECT) ? WRITE : READ, GFP_NOIO);
        if (!rq) {
                sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
                return NULL;
        }
 
-       memset(&rq->cmd, 0, BLK_MAX_CDB);
+       memset(rq->cmd, 0, BLK_MAX_CDB);
+       rq->cmd_len = COMMAND_SIZE(cmd);
        rq->cmd[0] = cmd;
-       rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
 
        switch (cmd) {
        case MODE_SELECT:
-               if (csdev->short_trespass) {
-                       page22 = csdev->hr ? short_trespass_hr : short_trespass;
-                       len = sizeof(short_trespass);
-               } else {
-                       page22 = csdev->hr ? long_trespass_hr : long_trespass;
-                       len = sizeof(long_trespass);
-               }
-               /*
-                * Can't DMA from kernel BSS -- must copy selected trespass
-                * command mode page contents to context buffer which is
-                * allocated by kmalloc.
-                */
-               BUG_ON((len > CLARIION_BUFFER_SIZE));
-               memcpy(csdev->buffer, page22, len);
+               len = sizeof(short_trespass);
+               rq->cmd_flags |= REQ_RW;
+               rq->cmd[1] = 0x10;
+               break;
+       case MODE_SELECT_10:
+               len = sizeof(long_trespass);
                rq->cmd_flags |= REQ_RW;
                rq->cmd[1] = 0x10;
                break;
        case INQUIRY:
-               rq->cmd[1] = 0x1;
-               rq->cmd[2] = 0xC0;
                len = CLARIION_BUFFER_SIZE;
-               memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+               memset(buffer, 0, len);
                break;
        default:
                BUG_ON(1);
@@ -298,47 +308,94 @@ static struct request *get_req(struct scsi_device *sdev, int cmd)
        rq->timeout = CLARIION_TIMEOUT;
        rq->retries = CLARIION_RETRIES;
 
-       rq->sense = csdev->sense;
-       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
-       rq->sense_len = 0;
-
-       if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
-                                                       len, GFP_ATOMIC)) {
-               __blk_put_request(rq->q, rq);
+       if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) {
+               blk_put_request(rq);
                return NULL;
        }
 
        return rq;
 }
 
-static int send_cmd(struct scsi_device *sdev, int cmd)
+static int send_inquiry_cmd(struct scsi_device *sdev, int page,
+                           struct clariion_dh_data *csdev)
 {
-       struct request *rq = get_req(sdev, cmd);
+       struct request *rq = get_req(sdev, INQUIRY, csdev->buffer);
+       int err;
 
        if (!rq)
                return SCSI_DH_RES_TEMP_UNAVAIL;
 
-       return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+       rq->sense = csdev->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = csdev->senselen = 0;
+
+       rq->cmd[0] = INQUIRY;
+       if (page != 0) {
+               rq->cmd[1] = 1;
+               rq->cmd[2] = page;
+       }
+       err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+       if (err == -EIO) {
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: failed to send %s INQUIRY: %x\n",
+                           CLARIION_NAME, page?"EVPD":"standard",
+                           rq->errors);
+               csdev->senselen = rq->sense_len;
+               err = SCSI_DH_IO;
+       }
+
+       blk_put_request(rq);
+
+       return err;
 }
 
-static int clariion_activate(struct scsi_device *sdev)
+static int send_trespass_cmd(struct scsi_device *sdev,
+                           struct clariion_dh_data *csdev)
 {
-       int result, done = 0;
+       struct request *rq;
+       unsigned char *page22;
+       int err, len, cmd;
+
+       if (csdev->flags & CLARIION_SHORT_TRESPASS) {
+               page22 = short_trespass;
+               if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
+                       /* Set Honor Reservations bit */
+                       page22[6] |= 0x80;
+               len = sizeof(short_trespass);
+               cmd = MODE_SELECT;
+       } else {
+               page22 = long_trespass;
+               if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
+                       /* Set Honor Reservations bit */
+                       page22[10] |= 0x80;
+               len = sizeof(long_trespass);
+               cmd = MODE_SELECT_10;
+       }
+       BUG_ON((len > CLARIION_BUFFER_SIZE));
+       memcpy(csdev->buffer, page22, len);
 
-       result = send_cmd(sdev, INQUIRY);
-       result = sp_info_endio(sdev, result, 0, &done);
-       if (result || done)
-               goto done;
+       rq = get_req(sdev, cmd, csdev->buffer);
+       if (!rq)
+               return SCSI_DH_RES_TEMP_UNAVAIL;
 
-       result = send_cmd(sdev, MODE_SELECT);
-       result = trespass_endio(sdev, result);
-       if (result)
-               goto done;
+       rq->sense = csdev->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = csdev->senselen = 0;
 
-       result = send_cmd(sdev, INQUIRY);
-       result = sp_info_endio(sdev, result, 1, NULL);
-done:
-       return result;
+       err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+       if (err == -EIO) {
+               if (rq->sense_len) {
+                       err = trespass_endio(sdev, csdev->sense);
+               } else {
+                       sdev_printk(KERN_INFO, sdev,
+                                   "%s: failed to send MODE SELECT: %x\n",
+                                   CLARIION_NAME, rq->errors);
+               }
+       }
+
+       blk_put_request(rq);
+
+       return err;
 }
 
 static int clariion_check_sense(struct scsi_device *sdev,
@@ -386,99 +443,215 @@ static int clariion_check_sense(struct scsi_device *sdev,
                break;
        }
 
-       /* success just means we do not care what scsi-ml does */
-       return SUCCESS;
+       return SCSI_RETURN_NOT_HANDLED;
+}
+
+static int clariion_prep_fn(struct scsi_device *sdev, struct request *req)
+{
+       struct clariion_dh_data *h = get_clariion_data(sdev);
+       int ret = BLKPREP_OK;
+
+       if (h->lun_state != CLARIION_LUN_OWNED) {
+               ret = BLKPREP_KILL;
+               req->cmd_flags |= REQ_QUIET;
+       }
+       return ret;
+
+}
+
+static int clariion_std_inquiry(struct scsi_device *sdev,
+                               struct clariion_dh_data *csdev)
+{
+       int err;
+       char *sp_model;
+
+       err = send_inquiry_cmd(sdev, 0, csdev);
+       if (err != SCSI_DH_OK && csdev->senselen) {
+               struct scsi_sense_hdr sshdr;
+
+               if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+                                        &sshdr)) {
+                       sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
+                                   "%02x/%02x/%02x\n", CLARIION_NAME,
+                                   sshdr.sense_key, sshdr.asc, sshdr.ascq);
+               }
+               err = SCSI_DH_IO;
+               goto out;
+       }
+
+       sp_model = parse_sp_model(sdev, csdev->buffer);
+       if (!sp_model) {
+               err = SCSI_DH_DEV_UNSUPP;
+               goto out;
+       }
+
+       /*
+        * FC Series arrays do not support long trespass
+        */
+       if (!strlen(sp_model) || !strncmp(sp_model, "FC",2))
+               csdev->flags |= CLARIION_SHORT_TRESPASS;
+
+       sdev_printk(KERN_INFO, sdev,
+                   "%s: detected Clariion %s, flags %x\n",
+                   CLARIION_NAME, sp_model, csdev->flags);
+out:
+       return err;
 }
 
-static const struct {
-       char *vendor;
-       char *model;
-} clariion_dev_list[] = {
+static int clariion_send_inquiry(struct scsi_device *sdev,
+                                struct clariion_dh_data *csdev)
+{
+       int err, retry = CLARIION_RETRIES;
+
+retry:
+       err = send_inquiry_cmd(sdev, 0xC0, csdev);
+       if (err != SCSI_DH_OK && csdev->senselen) {
+               struct scsi_sense_hdr sshdr;
+
+               err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+                                          &sshdr);
+               if (!err)
+                       return SCSI_DH_IO;
+
+               err = clariion_check_sense(sdev, &sshdr);
+               if (retry > 0 && err == NEEDS_RETRY) {
+                       retry--;
+                       goto retry;
+               }
+               sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
+                           "%02x/%02x/%02x\n", CLARIION_NAME,
+                             sshdr.sense_key, sshdr.asc, sshdr.ascq);
+               err = SCSI_DH_IO;
+       } else {
+               err = parse_sp_info_reply(sdev, csdev);
+       }
+       return err;
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+       struct clariion_dh_data *csdev = get_clariion_data(sdev);
+       int result;
+
+       result = clariion_send_inquiry(sdev, csdev);
+       if (result != SCSI_DH_OK)
+               goto done;
+
+       if (csdev->lun_state == CLARIION_LUN_OWNED)
+               goto done;
+
+       result = send_trespass_cmd(sdev, csdev);
+       if (result != SCSI_DH_OK)
+               goto done;
+       sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n",
+                   CLARIION_NAME,
+                   csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" );
+
+       /* Update status */
+       result = clariion_send_inquiry(sdev, csdev);
+       if (result != SCSI_DH_OK)
+               goto done;
+
+done:
+       sdev_printk(KERN_INFO, sdev,
+                   "%s: at SP %c Port %d (%s, default SP %c)\n",
+                   CLARIION_NAME, csdev->current_sp + 'A',
+                   csdev->port, lun_state[csdev->lun_state],
+                   csdev->default_sp + 'A');
+
+       return result;
+}
+
+const struct scsi_dh_devlist clariion_dev_list[] = {
        {"DGC", "RAID"},
        {"DGC", "DISK"},
+       {"DGC", "VRAID"},
        {NULL, NULL},
 };
 
-static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+static int clariion_bus_attach(struct scsi_device *sdev);
+static void clariion_bus_detach(struct scsi_device *sdev);
 
 static struct scsi_device_handler clariion_dh = {
        .name           = CLARIION_NAME,
        .module         = THIS_MODULE,
-       .nb.notifier_call = clariion_bus_notify,
+       .devlist        = clariion_dev_list,
+       .attach         = clariion_bus_attach,
+       .detach         = clariion_bus_detach,
        .check_sense    = clariion_check_sense,
        .activate       = clariion_activate,
+       .prep_fn        = clariion_prep_fn,
 };
 
 /*
  * TODO: need some interface so we can set trespass values
  */
-static int clariion_bus_notify(struct notifier_block *nb,
-                               unsigned long action, void *data)
+static int clariion_bus_attach(struct scsi_device *sdev)
 {
-       struct device *dev = data;
-       struct scsi_device *sdev;
        struct scsi_dh_data *scsi_dh_data;
        struct clariion_dh_data *h;
-       int i, found = 0;
        unsigned long flags;
+       int err;
 
-       if (!scsi_is_sdev_device(dev))
-               return 0;
+       scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+                              + sizeof(*h) , GFP_KERNEL);
+       if (!scsi_dh_data) {
+               sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
+                           CLARIION_NAME);
+               return -ENOMEM;
+       }
 
-       sdev = to_scsi_device(dev);
+       scsi_dh_data->scsi_dh = &clariion_dh;
+       h = (struct clariion_dh_data *) scsi_dh_data->buf;
+       h->lun_state = CLARIION_LUN_UNINITIALIZED;
+       h->default_sp = CLARIION_UNBOUND_LU;
+       h->current_sp = CLARIION_UNBOUND_LU;
 
-       if (action == BUS_NOTIFY_ADD_DEVICE) {
-               for (i = 0; clariion_dev_list[i].vendor; i++) {
-                       if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor,
-                                    strlen(clariion_dev_list[i].vendor)) &&
-                           !strncmp(sdev->model, clariion_dev_list[i].model,
-                                    strlen(clariion_dev_list[i].model))) {
-                               found = 1;
-                               break;
-                       }
-               }
-               if (!found)
-                       goto out;
-
-               scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
-                               + sizeof(*h) , GFP_KERNEL);
-               if (!scsi_dh_data) {
-                       sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
-                                   CLARIION_NAME);
-                       goto out;
-               }
+       err = clariion_std_inquiry(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto failed;
 
-               scsi_dh_data->scsi_dh = &clariion_dh;
-               h = (struct clariion_dh_data *) scsi_dh_data->buf;
-               h->default_sp = CLARIION_UNBOUND_LU;
-               h->current_sp = CLARIION_UNBOUND_LU;
+       err = clariion_send_inquiry(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto failed;
 
-               spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
-               sdev->scsi_dh_data = scsi_dh_data;
-               spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+       if (!try_module_get(THIS_MODULE))
+               goto failed;
 
-               sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME);
-               try_module_get(THIS_MODULE);
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       sdev->scsi_dh_data = scsi_dh_data;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
 
-       } else if (action == BUS_NOTIFY_DEL_DEVICE) {
-               if (sdev->scsi_dh_data == NULL ||
-                               sdev->scsi_dh_data->scsi_dh != &clariion_dh)
-                       goto out;
+       sdev_printk(KERN_INFO, sdev,
+                   "%s: connected to SP %c Port %d (%s, default SP %c)\n",
+                   CLARIION_NAME, h->current_sp + 'A',
+                   h->port, lun_state[h->lun_state],
+                   h->default_sp + 'A');
 
-               spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
-               scsi_dh_data = sdev->scsi_dh_data;
-               sdev->scsi_dh_data = NULL;
-               spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+       return 0;
 
-               sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
-                           CLARIION_NAME);
+failed:
+       kfree(scsi_dh_data);
+       sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
+                   CLARIION_NAME);
+       return -EINVAL;
+}
 
-               kfree(scsi_dh_data);
-               module_put(THIS_MODULE);
-       }
+static void clariion_bus_detach(struct scsi_device *sdev)
+{
+       struct scsi_dh_data *scsi_dh_data;
+       unsigned long flags;
 
-out:
-       return 0;
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       scsi_dh_data = sdev->scsi_dh_data;
+       sdev->scsi_dh_data = NULL;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+       sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n",
+                   CLARIION_NAME);
+
+       kfree(scsi_dh_data);
+       module_put(THIS_MODULE);
 }
 
 static int __init clariion_init(void)
@@ -487,7 +660,8 @@ static int __init clariion_init(void)
 
        r = scsi_register_device_handler(&clariion_dh);
        if (r != 0)
-               printk(KERN_ERR "Failed to register scsi device handler.");
+               printk(KERN_ERR "%s: Failed to register scsi device handler.",
+                       CLARIION_NAME);
        return r;
 }
 
index ae6be87d6a8327d8b523236c0d3aadf1622f63c1..9c7a1f8ebb722df09e88c67b49235448ef006a60 100644 (file)
@@ -4,6 +4,7 @@
  *
  * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
  * Copyright (C) 2006 Mike Christie
+ * Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_dh.h>
 
-#define HP_SW_NAME     "hp_sw"
+#define HP_SW_NAME                     "hp_sw"
 
-#define HP_SW_TIMEOUT (60 * HZ)
-#define HP_SW_RETRIES 3
+#define HP_SW_TIMEOUT                  (60 * HZ)
+#define HP_SW_RETRIES                  3
+
+#define HP_SW_PATH_UNINITIALIZED       -1
+#define HP_SW_PATH_ACTIVE              0
+#define HP_SW_PATH_PASSIVE             1
 
 struct hp_sw_dh_data {
        unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+       int path_state;
        int retries;
 };
 
@@ -42,51 +48,161 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
        return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
 }
 
-static int hp_sw_done(struct scsi_device *sdev)
+/*
+ * tur_done - Handle TEST UNIT READY return status
+ * @sdev: sdev the command has been sent to
+ * @errors: blk error code
+ *
+ * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
+ */
+static int tur_done(struct scsi_device *sdev, unsigned char *sense)
 {
-       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
        struct scsi_sense_hdr sshdr;
-       int rc;
-
-       sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
+       int ret;
 
-       rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
-       if (!rc)
+       ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
+       if (!ret) {
+               sdev_printk(KERN_WARNING, sdev,
+                           "%s: sending tur failed, no sense available\n",
+                           HP_SW_NAME);
+               ret = SCSI_DH_IO;
                goto done;
+       }
        switch (sshdr.sense_key) {
+       case UNIT_ATTENTION:
+               ret = SCSI_DH_IMM_RETRY;
+               break;
        case NOT_READY:
-               if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
-                       rc = SCSI_DH_RETRY;
-                       h->retries++;
+               if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) {
+                       /*
+                        * LUN not ready - Initialization command required
+                        *
+                        * This is the passive path
+                        */
+                       ret = SCSI_DH_DEV_OFFLINED;
                        break;
                }
-               /* fall through */
+               /* Fallthrough */
        default:
-               h->retries++;
-               rc = SCSI_DH_IMM_RETRY;
+               sdev_printk(KERN_WARNING, sdev,
+                          "%s: sending tur failed, sense %x/%x/%x\n",
+                          HP_SW_NAME, sshdr.sense_key, sshdr.asc,
+                          sshdr.ascq);
+               break;
        }
 
 done:
-       if (rc == SCSI_DH_OK || rc == SCSI_DH_IO)
-               h->retries = 0;
-       else if (h->retries > HP_SW_RETRIES) {
-               h->retries = 0;
+       return ret;
+}
+
+/*
+ * hp_sw_tur - Send TEST UNIT READY
+ * @sdev: sdev command should be sent to
+ *
+ * Use the TEST UNIT READY command to determine
+ * the path state.
+ */
+static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
+{
+       struct request *req;
+       int ret;
+
+       req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
+       if (!req)
+               return SCSI_DH_RES_TEMP_UNAVAIL;
+
+       req->cmd_type = REQ_TYPE_BLOCK_PC;
+       req->cmd_flags |= REQ_FAILFAST;
+       req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY);
+       memset(req->cmd, 0, MAX_COMMAND_SIZE);
+       req->cmd[0] = TEST_UNIT_READY;
+       req->timeout = HP_SW_TIMEOUT;
+       req->sense = h->sense;
+       memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       req->sense_len = 0;
+
+retry:
+       ret = blk_execute_rq(req->q, NULL, req, 1);
+       if (ret == -EIO) {
+               if (req->sense_len > 0) {
+                       ret = tur_done(sdev, h->sense);
+               } else {
+                       sdev_printk(KERN_WARNING, sdev,
+                                   "%s: sending tur failed with %x\n",
+                                   HP_SW_NAME, req->errors);
+                       ret = SCSI_DH_IO;
+               }
+       } else {
+               h->path_state = HP_SW_PATH_ACTIVE;
+               ret = SCSI_DH_OK;
+       }
+       if (ret == SCSI_DH_IMM_RETRY)
+               goto retry;
+       if (ret == SCSI_DH_DEV_OFFLINED) {
+               h->path_state = HP_SW_PATH_PASSIVE;
+               ret = SCSI_DH_OK;
+       }
+
+       blk_put_request(req);
+
+       return ret;
+}
+
+/*
+ * start_done - Handle START STOP UNIT return status
+ * @sdev: sdev the command has been sent to
+ * @errors: blk error code
+ */
+static int start_done(struct scsi_device *sdev, unsigned char *sense)
+{
+       struct scsi_sense_hdr sshdr;
+       int rc;
+
+       rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
+       if (!rc) {
+               sdev_printk(KERN_WARNING, sdev,
+                           "%s: sending start_stop_unit failed, "
+                           "no sense available\n",
+                           HP_SW_NAME);
+               return SCSI_DH_IO;
+       }
+       switch (sshdr.sense_key) {
+       case NOT_READY:
+               if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
+                       /*
+                        * LUN not ready - manual intervention required
+                        *
+                        * Switch-over in progress, retry.
+                        */
+                       rc = SCSI_DH_RETRY;
+                       break;
+               }
+               /* fall through */
+       default:
+               sdev_printk(KERN_WARNING, sdev,
+                          "%s: sending start_stop_unit failed, sense %x/%x/%x\n",
+                          HP_SW_NAME, sshdr.sense_key, sshdr.asc,
+                          sshdr.ascq);
                rc = SCSI_DH_IO;
        }
+
        return rc;
 }
 
-static int hp_sw_activate(struct scsi_device *sdev)
+/*
+ * hp_sw_start_stop - Send START STOP UNIT command
+ * @sdev: sdev command should be sent to
+ *
+ * Sending START STOP UNIT activates the SP.
+ */
+static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
 {
-       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
        struct request *req;
-       int ret = SCSI_DH_RES_TEMP_UNAVAIL;
+       int ret, retry;
 
-       req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC);
+       req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
        if (!req)
-               goto done;
-
-       sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
+               return SCSI_DH_RES_TEMP_UNAVAIL;
 
        req->cmd_type = REQ_TYPE_BLOCK_PC;
        req->cmd_flags |= REQ_FAILFAST;
@@ -98,95 +214,153 @@ static int hp_sw_activate(struct scsi_device *sdev)
        req->sense = h->sense;
        memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
        req->sense_len = 0;
+       retry = h->retries;
 
+retry:
        ret = blk_execute_rq(req->q, NULL, req, 1);
-       if (!ret) /* SUCCESS */
-               ret = hp_sw_done(sdev);
-       else
+       if (ret == -EIO) {
+               if (req->sense_len > 0) {
+                       ret = start_done(sdev, h->sense);
+               } else {
+                       sdev_printk(KERN_WARNING, sdev,
+                                   "%s: sending start_stop_unit failed with %x\n",
+                                   HP_SW_NAME, req->errors);
+                       ret = SCSI_DH_IO;
+               }
+       } else
+               ret = SCSI_DH_OK;
+
+       if (ret == SCSI_DH_RETRY) {
+               if (--retry)
+                       goto retry;
                ret = SCSI_DH_IO;
-done:
+       }
+
+       blk_put_request(req);
+
+       return ret;
+}
+
+static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
+{
+       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+       int ret = BLKPREP_OK;
+
+       if (h->path_state != HP_SW_PATH_ACTIVE) {
+               ret = BLKPREP_KILL;
+               req->cmd_flags |= REQ_QUIET;
+       }
+       return ret;
+
+}
+
+/*
+ * hp_sw_activate - Activate a path
+ * @sdev: sdev on the path to be activated
+ *
+ * The HP Active/Passive firmware is pretty simple;
+ * the passive path reports NOT READY with sense codes
+ * 0x04/0x02; a START STOP UNIT command will then
+ * activate the passive path (and deactivate the
+ * previously active one).
+ */
+static int hp_sw_activate(struct scsi_device *sdev)
+{
+       int ret = SCSI_DH_OK;
+       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+
+       ret = hp_sw_tur(sdev, h);
+
+       if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
+               ret = hp_sw_start_stop(sdev, h);
+               if (ret == SCSI_DH_OK)
+                       sdev_printk(KERN_INFO, sdev,
+                                   "%s: activated path\n",
+                                   HP_SW_NAME);
+       }
+
        return ret;
 }
 
-static const struct {
-       char *vendor;
-       char *model;
-} hp_sw_dh_data_list[] = {
-       {"COMPAQ", "MSA"},
-       {"HP", "HSV"},
+const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
+       {"COMPAQ", "MSA1000 VOLUME"},
+       {"COMPAQ", "HSV110"},
+       {"HP", "HSV100"},
        {"DEC", "HSG80"},
        {NULL, NULL},
 };
 
-static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *);
+static int hp_sw_bus_attach(struct scsi_device *sdev);
+static void hp_sw_bus_detach(struct scsi_device *sdev);
 
 static struct scsi_device_handler hp_sw_dh = {
        .name           = HP_SW_NAME,
        .module         = THIS_MODULE,
-       .nb.notifier_call = hp_sw_bus_notify,
+       .devlist        = hp_sw_dh_data_list,
+       .attach         = hp_sw_bus_attach,
+       .detach         = hp_sw_bus_detach,
        .activate       = hp_sw_activate,
+       .prep_fn        = hp_sw_prep_fn,
 };
 
-static int hp_sw_bus_notify(struct notifier_block *nb,
-                           unsigned long action, void *data)
+static int hp_sw_bus_attach(struct scsi_device *sdev)
 {
-       struct device *dev = data;
-       struct scsi_device *sdev;
        struct scsi_dh_data *scsi_dh_data;
-       int i, found = 0;
+       struct hp_sw_dh_data *h;
        unsigned long flags;
+       int ret;
 
-       if (!scsi_is_sdev_device(dev))
+       scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+                              + sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
+       if (!scsi_dh_data) {
+               sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
+                           HP_SW_NAME);
                return 0;
+       }
 
-       sdev = to_scsi_device(dev);
-
-       if (action == BUS_NOTIFY_ADD_DEVICE) {
-               for (i = 0; hp_sw_dh_data_list[i].vendor; i++) {
-                       if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor,
-                                    strlen(hp_sw_dh_data_list[i].vendor)) &&
-                           !strncmp(sdev->model, hp_sw_dh_data_list[i].model,
-                                    strlen(hp_sw_dh_data_list[i].model))) {
-                               found = 1;
-                               break;
-                       }
-               }
-               if (!found)
-                       goto out;
+       scsi_dh_data->scsi_dh = &hp_sw_dh;
+       h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
+       h->path_state = HP_SW_PATH_UNINITIALIZED;
+       h->retries = HP_SW_RETRIES;
 
-               scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
-                               + sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
-               if (!scsi_dh_data) {
-                       sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n",
-                                   HP_SW_NAME);
-                       goto out;
-               }
+       ret = hp_sw_tur(sdev, h);
+       if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
+               goto failed;
 
-               scsi_dh_data->scsi_dh = &hp_sw_dh;
-               spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
-               sdev->scsi_dh_data = scsi_dh_data;
-               spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-               try_module_get(THIS_MODULE);
+       if (!try_module_get(THIS_MODULE))
+               goto failed;
 
-               sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME);
-       } else if (action == BUS_NOTIFY_DEL_DEVICE) {
-               if (sdev->scsi_dh_data == NULL ||
-                               sdev->scsi_dh_data->scsi_dh != &hp_sw_dh)
-                       goto out;
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       sdev->scsi_dh_data = scsi_dh_data;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
 
-               spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
-               scsi_dh_data = sdev->scsi_dh_data;
-               sdev->scsi_dh_data = NULL;
-               spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-               module_put(THIS_MODULE);
+       sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
+                   HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
+                   "active":"passive");
 
-               sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME);
+       return 0;
 
-               kfree(scsi_dh_data);
-       }
+failed:
+       kfree(scsi_dh_data);
+       sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
+                   HP_SW_NAME);
+       return -EINVAL;
+}
 
-out:
-       return 0;
+static void hp_sw_bus_detach( struct scsi_device *sdev )
+{
+       struct scsi_dh_data *scsi_dh_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       scsi_dh_data = sdev->scsi_dh_data;
+       sdev->scsi_dh_data = NULL;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+       module_put(THIS_MODULE);
+
+       sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME);
+
+       kfree(scsi_dh_data);
 }
 
 static int __init hp_sw_init(void)
@@ -202,6 +376,6 @@ static void __exit hp_sw_exit(void)
 module_init(hp_sw_init);
 module_exit(hp_sw_exit);
 
-MODULE_DESCRIPTION("HP MSA 1000");
+MODULE_DESCRIPTION("HP Active/Passive driver");
 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
 MODULE_LICENSE("GPL");
index fdf34b0ec6e1770951181d82a314380a27934d50..b093a501f8ae673b980ca896d6d73d4ed407a78e 100644 (file)
@@ -173,6 +173,11 @@ struct rdac_dh_data {
 #define RDAC_STATE_ACTIVE      0
 #define RDAC_STATE_PASSIVE     1
        unsigned char           state;
+
+#define RDAC_LUN_UNOWNED       0
+#define RDAC_LUN_OWNED         1
+#define RDAC_LUN_AVT           2
+       char                    lun_state;
        unsigned char           sense[SCSI_SENSE_BUFFERSIZE];
        union                   {
                struct c2_inquiry c2;
@@ -182,6 +187,13 @@ struct rdac_dh_data {
        } inq;
 };
 
+static const char *lun_state[] =
+{
+       "unowned",
+       "owned",
+       "owned (AVT mode)",
+};
+
 static LIST_HEAD(ctlr_list);
 static DEFINE_SPINLOCK(list_lock);
 
@@ -197,9 +209,8 @@ static struct request *get_rdac_req(struct scsi_device *sdev,
 {
        struct request *rq;
        struct request_queue *q = sdev->request_queue;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
 
-       rq = blk_get_request(q, rw, GFP_KERNEL);
+       rq = blk_get_request(q, rw, GFP_NOIO);
 
        if (!rq) {
                sdev_printk(KERN_INFO, sdev,
@@ -207,17 +218,14 @@ static struct request *get_rdac_req(struct scsi_device *sdev,
                return NULL;
        }
 
-       if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
+       if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) {
                blk_put_request(rq);
                sdev_printk(KERN_INFO, sdev,
                                "get_rdac_req: blk_rq_map_kern failed.\n");
                return NULL;
        }
 
-       memset(&rq->cmd, 0, BLK_MAX_CDB);
-       rq->sense = h->sense;
-       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
-       rq->sense_len = 0;
+       memset(rq->cmd, 0, BLK_MAX_CDB);
 
        rq->cmd_type = REQ_TYPE_BLOCK_PC;
        rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
@@ -227,12 +235,12 @@ static struct request *get_rdac_req(struct scsi_device *sdev,
        return rq;
 }
 
-static struct request *rdac_failover_get(struct scsi_device *sdev)
+static struct request *rdac_failover_get(struct scsi_device *sdev,
+                                        struct rdac_dh_data *h)
 {
        struct request *rq;
        struct rdac_mode_common *common;
        unsigned data_size;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
 
        if (h->ctlr->use_ms10) {
                struct rdac_pg_expanded *rdac_pg;
@@ -277,6 +285,10 @@ static struct request *rdac_failover_get(struct scsi_device *sdev)
        }
        rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
 
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = 0;
+
        return rq;
 }
 
@@ -321,11 +333,10 @@ done:
 }
 
 static int submit_inquiry(struct scsi_device *sdev, int page_code,
-               unsigned int len)
+                         unsigned int len, struct rdac_dh_data *h)
 {
        struct request *rq;
        struct request_queue *q = sdev->request_queue;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
        int err = SCSI_DH_RES_TEMP_UNAVAIL;
 
        rq = get_rdac_req(sdev, &h->inq, len, READ);
@@ -338,59 +349,68 @@ static int submit_inquiry(struct scsi_device *sdev, int page_code,
        rq->cmd[2] = page_code;
        rq->cmd[4] = len;
        rq->cmd_len = COMMAND_SIZE(INQUIRY);
+
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = 0;
+
        err = blk_execute_rq(q, NULL, rq, 1);
        if (err == -EIO)
                err = SCSI_DH_IO;
+
+       blk_put_request(rq);
 done:
        return err;
 }
 
-static int get_lun(struct scsi_device *sdev)
+static int get_lun(struct scsi_device *sdev, struct rdac_dh_data *h)
 {
        int err;
        struct c8_inquiry *inqp;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
 
-       err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry));
+       err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry), h);
        if (err == SCSI_DH_OK) {
                inqp = &h->inq.c8;
-               h->lun = inqp->lun[7]; /* currently it uses only one byte */
+               if (inqp->page_code != 0xc8)
+                       return SCSI_DH_NOSYS;
+               if (inqp->page_id[0] != 'e' || inqp->page_id[1] != 'd' ||
+                   inqp->page_id[2] != 'i' || inqp->page_id[3] != 'd')
+                       return SCSI_DH_NOSYS;
+               h->lun = scsilun_to_int((struct scsi_lun *)inqp->lun);
        }
        return err;
 }
 
-#define RDAC_OWNED     0
-#define RDAC_UNOWNED   1
-#define RDAC_FAILED    2
-static int check_ownership(struct scsi_device *sdev)
+static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
 {
        int err;
        struct c9_inquiry *inqp;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
 
-       err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry));
+       err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h);
        if (err == SCSI_DH_OK) {
-               err = RDAC_UNOWNED;
                inqp = &h->inq.c9;
-               /*
-                * If in AVT mode or if the path already owns the LUN,
-                * return RDAC_OWNED;
-                */
-               if (((inqp->avte_cvp >> 7) == 0x1) ||
-                                ((inqp->avte_cvp & 0x1) != 0))
-                       err = RDAC_OWNED;
-       } else
-               err = RDAC_FAILED;
+               if ((inqp->avte_cvp >> 7) == 0x1) {
+                       /* LUN in AVT mode */
+                       sdev_printk(KERN_NOTICE, sdev,
+                                   "%s: AVT mode detected\n",
+                                   RDAC_NAME);
+                       h->lun_state = RDAC_LUN_AVT;
+               } else if ((inqp->avte_cvp & 0x1) != 0) {
+                       /* LUN was owned by the controller */
+                       h->lun_state = RDAC_LUN_OWNED;
+               }
+       }
+
        return err;
 }
 
-static int initialize_controller(struct scsi_device *sdev)
+static int initialize_controller(struct scsi_device *sdev,
+                                struct rdac_dh_data *h)
 {
        int err;
        struct c4_inquiry *inqp;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
 
-       err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry));
+       err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h);
        if (err == SCSI_DH_OK) {
                inqp = &h->inq.c4;
                h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id);
@@ -400,13 +420,12 @@ static int initialize_controller(struct scsi_device *sdev)
        return err;
 }
 
-static int set_mode_select(struct scsi_device *sdev)
+static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
 {
        int err;
        struct c2_inquiry *inqp;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
 
-       err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry));
+       err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry), h);
        if (err == SCSI_DH_OK) {
                inqp = &h->inq.c2;
                /*
@@ -421,13 +440,13 @@ static int set_mode_select(struct scsi_device *sdev)
        return err;
 }
 
-static int mode_select_handle_sense(struct scsi_device *sdev)
+static int mode_select_handle_sense(struct scsi_device *sdev,
+                                   unsigned char *sensebuf)
 {
        struct scsi_sense_hdr sense_hdr;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
        int sense, err = SCSI_DH_IO, ret;
 
-       ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+       ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
        if (!ret)
                goto done;
 
@@ -451,14 +470,13 @@ done:
        return err;
 }
 
-static int send_mode_select(struct scsi_device *sdev)
+static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
 {
        struct request *rq;
        struct request_queue *q = sdev->request_queue;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
        int err = SCSI_DH_RES_TEMP_UNAVAIL;
 
-       rq = rdac_failover_get(sdev);
+       rq = rdac_failover_get(sdev, h);
        if (!rq)
                goto done;
 
@@ -466,9 +484,11 @@ static int send_mode_select(struct scsi_device *sdev)
 
        err = blk_execute_rq(q, NULL, rq, 1);
        if (err != SCSI_DH_OK)
-               err = mode_select_handle_sense(sdev);
+               err = mode_select_handle_sense(sdev, h->sense);
        if (err == SCSI_DH_OK)
                h->state = RDAC_STATE_ACTIVE;
+
+       blk_put_request(rq);
 done:
        return err;
 }
@@ -478,38 +498,23 @@ static int rdac_activate(struct scsi_device *sdev)
        struct rdac_dh_data *h = get_rdac_data(sdev);
        int err = SCSI_DH_OK;
 
-       if (h->lun == UNINITIALIZED_LUN) {
-               err = get_lun(sdev);
-               if (err != SCSI_DH_OK)
-                       goto done;
-       }
-
-       err = check_ownership(sdev);
-       switch (err) {
-       case RDAC_UNOWNED:
-               break;
-       case RDAC_OWNED:
-               err = SCSI_DH_OK;
-               goto done;
-       case RDAC_FAILED:
-       default:
-               err = SCSI_DH_IO;
+       err = check_ownership(sdev, h);
+       if (err != SCSI_DH_OK)
                goto done;
-       }
 
        if (!h->ctlr) {
-               err = initialize_controller(sdev);
+               err = initialize_controller(sdev, h);
                if (err != SCSI_DH_OK)
                        goto done;
        }
 
        if (h->ctlr->use_ms10 == -1) {
-               err = set_mode_select(sdev);
+               err = set_mode_select(sdev, h);
                if (err != SCSI_DH_OK)
                        goto done;
        }
-
-       err = send_mode_select(sdev);
+       if (h->lun_state == RDAC_LUN_UNOWNED)
+               err = send_mode_select(sdev, h);
 done:
        return err;
 }
@@ -569,10 +574,7 @@ static int rdac_check_sense(struct scsi_device *sdev,
        return SCSI_RETURN_NOT_HANDLED;
 }
 
-static const struct {
-       char *vendor;
-       char *model;
-} rdac_dev_list[] = {
+const struct scsi_dh_devlist rdac_dev_list[] = {
        {"IBM", "1722"},
        {"IBM", "1724"},
        {"IBM", "1726"},
@@ -590,89 +592,89 @@ static const struct {
        {NULL, NULL},
 };
 
-static int rdac_bus_notify(struct notifier_block *, unsigned long, void *);
+static int rdac_bus_attach(struct scsi_device *sdev);
+static void rdac_bus_detach(struct scsi_device *sdev);
 
 static struct scsi_device_handler rdac_dh = {
        .name = RDAC_NAME,
        .module = THIS_MODULE,
-       .nb.notifier_call = rdac_bus_notify,
+       .devlist = rdac_dev_list,
        .prep_fn = rdac_prep_fn,
        .check_sense = rdac_check_sense,
+       .attach = rdac_bus_attach,
+       .detach = rdac_bus_detach,
        .activate = rdac_activate,
 };
 
-/*
- * TODO: need some interface so we can set trespass values
- */
-static int rdac_bus_notify(struct notifier_block *nb,
-                           unsigned long action, void *data)
+static int rdac_bus_attach(struct scsi_device *sdev)
 {
-       struct device *dev = data;
-       struct scsi_device *sdev;
        struct scsi_dh_data *scsi_dh_data;
        struct rdac_dh_data *h;
-       int i, found = 0;
        unsigned long flags;
+       int err;
 
-       if (!scsi_is_sdev_device(dev))
+       scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+                              + sizeof(*h) , GFP_KERNEL);
+       if (!scsi_dh_data) {
+               sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
+                           RDAC_NAME);
                return 0;
+       }
 
-       sdev = to_scsi_device(dev);
-
-       if (action == BUS_NOTIFY_ADD_DEVICE) {
-               for (i = 0; rdac_dev_list[i].vendor; i++) {
-                       if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor,
-                                    strlen(rdac_dev_list[i].vendor)) &&
-                           !strncmp(sdev->model, rdac_dev_list[i].model,
-                                    strlen(rdac_dev_list[i].model))) {
-                               found = 1;
-                               break;
-                       }
-               }
-               if (!found)
-                       goto out;
+       scsi_dh_data->scsi_dh = &rdac_dh;
+       h = (struct rdac_dh_data *) scsi_dh_data->buf;
+       h->lun = UNINITIALIZED_LUN;
+       h->state = RDAC_STATE_ACTIVE;
 
-               scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
-                               + sizeof(*h) , GFP_KERNEL);
-               if (!scsi_dh_data) {
-                       sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
-                                   RDAC_NAME);
-                       goto out;
-               }
+       err = get_lun(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto failed;
 
-               scsi_dh_data->scsi_dh = &rdac_dh;
-               h = (struct rdac_dh_data *) scsi_dh_data->buf;
-               h->lun = UNINITIALIZED_LUN;
-               h->state = RDAC_STATE_ACTIVE;
-               spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
-               sdev->scsi_dh_data = scsi_dh_data;
-               spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-               try_module_get(THIS_MODULE);
-
-               sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME);
-
-       } else if (action == BUS_NOTIFY_DEL_DEVICE) {
-               if (sdev->scsi_dh_data == NULL ||
-                               sdev->scsi_dh_data->scsi_dh != &rdac_dh)
-                       goto out;
-
-               spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
-               scsi_dh_data = sdev->scsi_dh_data;
-               sdev->scsi_dh_data = NULL;
-               spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
-               h = (struct rdac_dh_data *) scsi_dh_data->buf;
-               if (h->ctlr)
-                       kref_put(&h->ctlr->kref, release_controller);
-               kfree(scsi_dh_data);
-               module_put(THIS_MODULE);
-               sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME);
-       }
+       err = check_ownership(sdev, h);
+       if (err != SCSI_DH_OK)
+               goto failed;
+
+       if (!try_module_get(THIS_MODULE))
+               goto failed;
+
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       sdev->scsi_dh_data = scsi_dh_data;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+       sdev_printk(KERN_NOTICE, sdev,
+                   "%s: LUN %d (%s)\n",
+                   RDAC_NAME, h->lun, lun_state[(int)h->lun_state]);
 
-out:
        return 0;
+
+failed:
+       kfree(scsi_dh_data);
+       sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
+                   RDAC_NAME);
+       return -EINVAL;
+}
+
+static void rdac_bus_detach( struct scsi_device *sdev )
+{
+       struct scsi_dh_data *scsi_dh_data;
+       struct rdac_dh_data *h;
+       unsigned long flags;
+
+       spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+       scsi_dh_data = sdev->scsi_dh_data;
+       sdev->scsi_dh_data = NULL;
+       spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+       h = (struct rdac_dh_data *) scsi_dh_data->buf;
+       if (h->ctlr)
+               kref_put(&h->ctlr->kref, release_controller);
+       kfree(scsi_dh_data);
+       module_put(THIS_MODULE);
+       sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME);
 }
 
+
+
 static int __init rdac_init(void)
 {
        int r;
index 61f8fdea2d96e24824bb2926e1f4cd126912377f..ae560bc04f9d4df009ad8aef36abfa288dfca2f2 100644 (file)
@@ -521,9 +521,10 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
 static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
 {
        if (vhost->action == IBMVFC_HOST_ACTION_NONE) {
-               scsi_block_requests(vhost->host);
-               ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING);
-               ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+               if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+                       scsi_block_requests(vhost->host);
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+               }
        } else
                vhost->reinit = 1;
 
@@ -854,39 +855,41 @@ static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
 }
 
 /**
- * __ibmvfc_find_target - Find the specified scsi_target (no locking)
+ * __ibmvfc_get_target - Find the specified scsi_target (no locking)
  * @starget:   scsi target struct
  *
  * Return value:
  *     ibmvfc_target struct / NULL if not found
  **/
-static struct ibmvfc_target *__ibmvfc_find_target(struct scsi_target *starget)
+static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
 {
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
        struct ibmvfc_host *vhost = shost_priv(shost);
        struct ibmvfc_target *tgt;
 
        list_for_each_entry(tgt, &vhost->targets, queue)
-               if (tgt->target_id == starget->id)
+               if (tgt->target_id == starget->id) {
+                       kref_get(&tgt->kref);
                        return tgt;
+               }
        return NULL;
 }
 
 /**
- * ibmvfc_find_target - Find the specified scsi_target
+ * ibmvfc_get_target - Find the specified scsi_target
  * @starget:   scsi target struct
  *
  * Return value:
  *     ibmvfc_target struct / NULL if not found
  **/
-static struct ibmvfc_target *ibmvfc_find_target(struct scsi_target *starget)
+static struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget)
 {
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
        struct ibmvfc_target *tgt;
        unsigned long flags;
 
        spin_lock_irqsave(shost->host_lock, flags);
-       tgt = __ibmvfc_find_target(starget);
+       tgt = __ibmvfc_get_target(starget);
        spin_unlock_irqrestore(shost->host_lock, flags);
        return tgt;
 }
@@ -963,6 +966,9 @@ static void ibmvfc_get_host_port_state(struct Scsi_Host *shost)
        case IBMVFC_HALTED:
                fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED;
                break;
+       case IBMVFC_NO_CRQ:
+               fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+               break;
        default:
                ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state);
                fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
@@ -987,6 +993,17 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
                rport->dev_loss_tmo = 1;
 }
 
+/**
+ * ibmvfc_release_tgt - Free memory allocated for a target
+ * @kref:              kref struct
+ *
+ **/
+static void ibmvfc_release_tgt(struct kref *kref)
+{
+       struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
+       kfree(tgt);
+}
+
 /**
  * ibmvfc_get_starget_node_name - Get SCSI target's node name
  * @starget:   scsi target struct
@@ -996,8 +1013,10 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
  **/
 static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
 {
-       struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+       struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
        fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0;
+       if (tgt)
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 /**
@@ -1009,8 +1028,10 @@ static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
  **/
 static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
 {
-       struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+       struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
        fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0;
+       if (tgt)
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 /**
@@ -1022,8 +1043,10 @@ static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
  **/
 static void ibmvfc_get_starget_port_id(struct scsi_target *starget)
 {
-       struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+       struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
        fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1;
+       if (tgt)
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 /**
@@ -1113,7 +1136,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
        login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
        login_info->capabilities = IBMVFC_CAN_MIGRATE;
        login_info->async.va = vhost->async_crq.msg_token;
-       login_info->async.len = vhost->async_crq.size;
+       login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs);
        strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
        strncpy(login_info->device_name,
                vhost->host->shost_gendev.bus_id, IBMVFC_MAX_NAME);
@@ -1404,7 +1427,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
                err = cmd_status[index].name;
        }
 
-       if (!logerr && (vhost->log_level <= IBMVFC_DEFAULT_LOG_LEVEL))
+       if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1)))
                return;
 
        if (rsp->flags & FCP_RSP_LEN_VALID)
@@ -2054,7 +2077,7 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
 {
        const char *desc = ibmvfc_get_ae_desc(crq->event);
 
-       ibmvfc_log(vhost, 2, "%s event received\n", desc);
+       ibmvfc_log(vhost, 3, "%s event received\n", desc);
 
        switch (crq->event) {
        case IBMVFC_AE_LINK_UP:
@@ -2647,17 +2670,6 @@ static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
                ibmvfc_init_tgt(tgt, job_step);
 }
 
-/**
- * ibmvfc_release_tgt - Free memory allocated for a target
- * @kref:              kref struct
- *
- **/
-static void ibmvfc_release_tgt(struct kref *kref)
-{
-       struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
-       kfree(tgt);
-}
-
 /**
  * ibmvfc_tgt_prli_done - Completion handler for Process Login
  * @evt:       ibmvfc event struct
@@ -2901,6 +2913,139 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
                tgt_dbg(tgt, "Sent Implicit Logout\n");
 }
 
+/**
+ * ibmvfc_adisc_needs_plogi - Does device need PLOGI?
+ * @mad:       ibmvfc passthru mad struct
+ * @tgt:       ibmvfc target struct
+ *
+ * Returns:
+ *     1 if PLOGI needed / 0 if PLOGI not needed
+ **/
+static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad,
+                                   struct ibmvfc_target *tgt)
+{
+       if (memcmp(&mad->fc_iu.response[2], &tgt->ids.port_name,
+                  sizeof(tgt->ids.port_name)))
+               return 1;
+       if (memcmp(&mad->fc_iu.response[4], &tgt->ids.node_name,
+                  sizeof(tgt->ids.node_name)))
+               return 1;
+       if (mad->fc_iu.response[6] != tgt->scsi_id)
+               return 1;
+       return 0;
+}
+
+/**
+ * ibmvfc_tgt_adisc_done - Completion handler for ADISC
+ * @evt:       ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
+{
+       struct ibmvfc_target *tgt = evt->tgt;
+       struct ibmvfc_host *vhost = evt->vhost;
+       struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
+       u32 status = mad->common.status;
+       u8 fc_reason, fc_explain;
+
+       vhost->discovery_threads--;
+       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+
+       switch (status) {
+       case IBMVFC_MAD_SUCCESS:
+               tgt_dbg(tgt, "ADISC succeeded\n");
+               if (ibmvfc_adisc_needs_plogi(mad, tgt))
+                       tgt->need_login = 1;
+               break;
+       case IBMVFC_MAD_DRIVER_FAILED:
+               break;
+       case IBMVFC_MAD_FAILED:
+       default:
+               tgt->need_login = 1;
+               fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
+               fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
+               tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+                        ibmvfc_get_cmd_error(mad->iu.status, mad->iu.error),
+                        mad->iu.status, mad->iu.error,
+                        ibmvfc_get_fc_type(fc_reason), fc_reason,
+                        ibmvfc_get_ls_explain(fc_explain), fc_explain, status);
+               break;
+       };
+
+       kref_put(&tgt->kref, ibmvfc_release_tgt);
+       ibmvfc_free_event(evt);
+       wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_init_passthru - Initialize an event struct for FC passthru
+ * @evt:               ibmvfc event struct
+ *
+ **/
+static void ibmvfc_init_passthru(struct ibmvfc_event *evt)
+{
+       struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
+
+       memset(mad, 0, sizeof(*mad));
+       mad->common.version = 1;
+       mad->common.opcode = IBMVFC_PASSTHRU;
+       mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
+       mad->cmd_ioba.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, iu);
+       mad->cmd_ioba.len = sizeof(mad->iu);
+       mad->iu.cmd_len = sizeof(mad->fc_iu.payload);
+       mad->iu.rsp_len = sizeof(mad->fc_iu.response);
+       mad->iu.cmd.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, fc_iu) +
+               offsetof(struct ibmvfc_passthru_fc_iu, payload);
+       mad->iu.cmd.len = sizeof(mad->fc_iu.payload);
+       mad->iu.rsp.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, fc_iu) +
+               offsetof(struct ibmvfc_passthru_fc_iu, response);
+       mad->iu.rsp.len = sizeof(mad->fc_iu.response);
+}
+
+/**
+ * ibmvfc_tgt_adisc - Initiate an ADISC for specified target
+ * @tgt:               ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
+{
+       struct ibmvfc_passthru_mad *mad;
+       struct ibmvfc_host *vhost = tgt->vhost;
+       struct ibmvfc_event *evt;
+
+       if (vhost->discovery_threads >= disc_threads)
+               return;
+
+       kref_get(&tgt->kref);
+       evt = ibmvfc_get_event(vhost);
+       vhost->discovery_threads++;
+       ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT);
+       evt->tgt = tgt;
+
+       ibmvfc_init_passthru(evt);
+       mad = &evt->iu.passthru;
+       mad->iu.flags = IBMVFC_FC_ELS;
+       mad->iu.scsi_id = tgt->scsi_id;
+
+       mad->fc_iu.payload[0] = IBMVFC_ADISC;
+       memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name,
+              sizeof(vhost->login_buf->resp.port_name));
+       memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name,
+              sizeof(vhost->login_buf->resp.node_name));
+       mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff;
+
+       ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+       if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+               vhost->discovery_threads--;
+               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
+       } else
+               tgt_dbg(tgt, "Sent ADISC\n");
+}
+
 /**
  * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD
  * @evt:       ibmvfc event struct
@@ -2921,6 +3066,8 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
                tgt->new_scsi_id = rsp->scsi_id;
                if (rsp->scsi_id != tgt->scsi_id)
                        ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+               else
+                       ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc);
                break;
        case IBMVFC_MAD_DRIVER_FAILED:
                break;
@@ -3336,6 +3483,7 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
                tgt_dbg(tgt, "rport add succeeded\n");
                rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
                rport->supported_classes = 0;
+               tgt->target_id = rport->scsi_target_id;
                if (tgt->service_parms.class1_parms[0] & 0x80000000)
                        rport->supported_classes |= FC_COS_CLASS1;
                if (tgt->service_parms.class2_parms[0] & 0x80000000)
@@ -3800,10 +3948,12 @@ static int ibmvfc_remove(struct vio_dev *vdev)
 
        ENTER;
        ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+       ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+       ibmvfc_wait_while_resetting(vhost);
+       ibmvfc_release_crq_queue(vhost);
        kthread_stop(vhost->work_thread);
        fc_remove_host(vhost->host);
        scsi_remove_host(vhost->host);
-       ibmvfc_release_crq_queue(vhost);
 
        spin_lock_irqsave(vhost->host->host_lock, flags);
        ibmvfc_purge_requests(vhost, DID_ERROR);
index 057f3c01ed6106f1cbc146cd75b67aa1a85d1052..4bf6e374f076b854babc1eae897bd3500cdbed8a 100644 (file)
@@ -29,8 +29,8 @@
 #include "viosrp.h"
 
 #define IBMVFC_NAME    "ibmvfc"
-#define IBMVFC_DRIVER_VERSION          "1.0.0"
-#define IBMVFC_DRIVER_DATE             "(July 1, 2008)"
+#define IBMVFC_DRIVER_VERSION          "1.0.1"
+#define IBMVFC_DRIVER_DATE             "(July 11, 2008)"
 
 #define IBMVFC_DEFAULT_TIMEOUT 15
 #define IBMVFC_INIT_TIMEOUT            30
@@ -119,6 +119,7 @@ enum ibmvfc_mad_types {
        IBMVFC_PROCESS_LOGIN    = 0x0008,
        IBMVFC_QUERY_TARGET     = 0x0010,
        IBMVFC_IMPLICIT_LOGOUT  = 0x0040,
+       IBMVFC_PASSTHRU         = 0x0200,
        IBMVFC_TMF_MAD          = 0x0100,
 };
 
@@ -439,6 +440,37 @@ struct ibmvfc_cmd {
        struct ibmvfc_fcp_rsp rsp;
 }__attribute__((packed, aligned (8)));
 
+struct ibmvfc_passthru_fc_iu {
+       u32 payload[7];
+#define IBMVFC_ADISC   0x52000000
+       u32 response[7];
+};
+
+struct ibmvfc_passthru_iu {
+       u64 task_tag;
+       u32 cmd_len;
+       u32 rsp_len;
+       u16 status;
+       u16 error;
+       u32 flags;
+#define IBMVFC_FC_ELS          0x01
+       u32 cancel_key;
+       u32 reserved;
+       struct srp_direct_buf cmd;
+       struct srp_direct_buf rsp;
+       u64 correlation;
+       u64 scsi_id;
+       u64 tag;
+       u64 reserved2[2];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_passthru_mad {
+       struct ibmvfc_mad_common common;
+       struct srp_direct_buf cmd_ioba;
+       struct ibmvfc_passthru_iu iu;
+       struct ibmvfc_passthru_fc_iu fc_iu;
+}__attribute__((packed, aligned (8)));
+
 struct ibmvfc_trace_start_entry {
        u32 xfer_len;
 }__attribute__((packed));
@@ -531,6 +563,7 @@ union ibmvfc_iu {
        struct ibmvfc_implicit_logout implicit_logout;
        struct ibmvfc_tmf tmf;
        struct ibmvfc_cmd cmd;
+       struct ibmvfc_passthru_mad passthru;
 }__attribute__((packed, aligned (8)));
 
 enum ibmvfc_target_action {
@@ -656,6 +689,9 @@ struct ibmvfc_host {
 #define tgt_dbg(t, fmt, ...)                   \
        DBG_CMD(dev_info((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__))
 
+#define tgt_info(t, fmt, ...)          \
+       dev_info((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__)
+
 #define tgt_err(t, fmt, ...)           \
        dev_err((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__)
 
@@ -668,8 +704,8 @@ struct ibmvfc_host {
                        dev_err((vhost)->dev, ##__VA_ARGS__); \
        } while (0)
 
-#define ENTER DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Entering %s\n", __FUNCTION__))
-#define LEAVE DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Leaving %s\n", __FUNCTION__))
+#define ENTER DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Entering %s\n", __func__))
+#define LEAVE DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Leaving %s\n", __func__))
 
 #ifdef CONFIG_SCSI_IBMVFC_TRACE
 #define ibmvfc_create_trace_file(kobj, attr) sysfs_create_bin_file(kobj, attr)
index 2e13ec00172aca14ccdea9da8daf6ed339e179ef..2a5b29d12172e4a72bb774453d2662b7d29192ee 100644 (file)
@@ -55,7 +55,7 @@
 /* tmp - will replace with SCSI logging stuff */
 #define eprintk(fmt, args...)                                  \
 do {                                                           \
-       printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);  \
+       printk("%s(%d) " fmt, __func__, __LINE__, ##args);      \
 } while (0)
 /* #define dprintk eprintk */
 #define dprintk(fmt, args...)
index f97d172844be8e084cca57e3750752be77529816..c2a9a13d788f2bb6f68e7c921b71270a5dc6d20b 100644 (file)
@@ -163,7 +163,7 @@ static int imm_proc_info(struct Scsi_Host *host, char *buffer, char **start,
 
 #if IMM_DEBUG > 0
 #define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\
-          y, __FUNCTION__, __LINE__); imm_fail_func(x,y);
+          y, __func__, __LINE__); imm_fail_func(x,y);
 static inline void
 imm_fail_func(imm_struct *dev, int error_code)
 #else
index d93156671e93d7155505098242f821a46c1ed973..4871dd1f258296aa7fac16db51bc38c24fa104d5 100644 (file)
@@ -1403,10 +1403,10 @@ struct ipr_ucode_image_header {
 }
 
 #define ipr_trace ipr_dbg("%s: %s: Line: %d\n",\
-       __FILE__, __FUNCTION__, __LINE__)
+       __FILE__, __func__, __LINE__)
 
-#define ENTER IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Entering %s\n", __FUNCTION__))
-#define LEAVE IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Leaving %s\n", __FUNCTION__))
+#define ENTER IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Entering %s\n", __func__))
+#define LEAVE IPR_DBG_CMD(printk(KERN_INFO IPR_NAME": Leaving %s\n", __func__))
 
 #define ipr_err_separator \
 ipr_err("----------------------------------------------------------\n")
index 744f06d04a363225cc9859b08df208a2cb0156ae..48ee8c7f5bdd270351bcdf0dae895e0ae572a3f2 100644 (file)
@@ -74,7 +74,7 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)
                case SAS_OPEN_TO:
                case SAS_OPEN_REJECT:
                        SAS_DPRINTK("%s: Saw error %d.  What to do?\n",
-                                   __FUNCTION__, ts->stat);
+                                   __func__, ts->stat);
                        return AC_ERR_OTHER;
 
                case SAS_ABORTED_TASK:
@@ -115,7 +115,7 @@ static void sas_ata_task_done(struct sas_task *task)
        } else if (stat->stat != SAM_STAT_GOOD) {
                ac = sas_to_ata_err(stat);
                if (ac) {
-                       SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__,
+                       SAS_DPRINTK("%s: SAS error %x\n", __func__,
                                    stat->stat);
                        /* We saw a SAS error. Send a vague error. */
                        qc->err_mask = ac;
@@ -244,20 +244,20 @@ static void sas_ata_phy_reset(struct ata_port *ap)
                res = i->dft->lldd_I_T_nexus_reset(dev);
 
        if (res != TMF_RESP_FUNC_COMPLETE)
-               SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__);
+               SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__);
 
        switch (dev->sata_dev.command_set) {
                case ATA_COMMAND_SET:
-                       SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__);
+                       SAS_DPRINTK("%s: Found ATA device.\n", __func__);
                        ap->link.device[0].class = ATA_DEV_ATA;
                        break;
                case ATAPI_COMMAND_SET:
-                       SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__);
+                       SAS_DPRINTK("%s: Found ATAPI device.\n", __func__);
                        ap->link.device[0].class = ATA_DEV_ATAPI;
                        break;
                default:
                        SAS_DPRINTK("%s: Unknown SATA command set: %d.\n",
-                                   __FUNCTION__,
+                                   __func__,
                                    dev->sata_dev.command_set);
                        ap->link.device[0].class = ATA_DEV_UNKNOWN;
                        break;
@@ -299,7 +299,7 @@ static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in,
 {
        struct domain_device *dev = ap->private_data;
 
-       SAS_DPRINTK("STUB %s\n", __FUNCTION__);
+       SAS_DPRINTK("STUB %s\n", __func__);
        switch (sc_reg_in) {
                case SCR_STATUS:
                        dev->sata_dev.sstatus = val;
@@ -324,7 +324,7 @@ static int sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in,
 {
        struct domain_device *dev = ap->private_data;
 
-       SAS_DPRINTK("STUB %s\n", __FUNCTION__);
+       SAS_DPRINTK("STUB %s\n", __func__);
        switch (sc_reg_in) {
                case SCR_STATUS:
                        *val = dev->sata_dev.sstatus;
index aefd865a578862e3bf17b4a8d6a7f8a4a8aaffef..3da02e4367884b4f8fcc1015811b55b0bf88a968 100644 (file)
@@ -121,7 +121,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
                        break;
                } else {
                        SAS_DPRINTK("%s: task to dev %016llx response: 0x%x "
-                                   "status 0x%x\n", __FUNCTION__,
+                                   "status 0x%x\n", __func__,
                                    SAS_ADDR(dev->sas_addr),
                                    task->task_status.resp,
                                    task->task_status.stat);
@@ -1279,7 +1279,7 @@ static int sas_configure_present(struct domain_device *dev, int phy_id,
                        goto out;
                } else if (res != SMP_RESP_FUNC_ACC) {
                        SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x "
-                                   "result 0x%x\n", __FUNCTION__,
+                                   "result 0x%x\n", __func__,
                                    SAS_ADDR(dev->sas_addr), phy_id, i, res);
                        goto out;
                }
@@ -1901,7 +1901,7 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
 
        if (!rsp) {
                printk("%s: space for a smp response is missing\n",
-                      __FUNCTION__);
+                      __func__);
                return -EINVAL;
        }
 
@@ -1914,20 +1914,20 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        if (type != SAS_EDGE_EXPANDER_DEVICE &&
            type != SAS_FANOUT_EXPANDER_DEVICE) {
                printk("%s: can we send a smp request to a device?\n",
-                      __FUNCTION__);
+                      __func__);
                return -EINVAL;
        }
 
        dev = sas_find_dev_by_rphy(rphy);
        if (!dev) {
-               printk("%s: fail to find a domain_device?\n", __FUNCTION__);
+               printk("%s: fail to find a domain_device?\n", __func__);
                return -EINVAL;
        }
 
        /* do we need to support multiple segments? */
        if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
                printk("%s: multiple segments req %u %u, rsp %u %u\n",
-                      __FUNCTION__, req->bio->bi_vcnt, req->data_len,
+                      __func__, req->bio->bi_vcnt, req->data_len,
                       rsp->bio->bi_vcnt, rsp->data_len);
                return -EINVAL;
        }
index 39ae68a3b0ef57a14eefcff9112e9668deb6fe1f..139935a121b42a8eab5f1d5535db8adb8eca498f 100644 (file)
@@ -50,7 +50,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
                        sas_deform_port(phy);
                else {
                        SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",
-                                   __FUNCTION__, phy->id, phy->port->id,
+                                   __func__, phy->id, phy->port->id,
                                    phy->port->num_phys);
                        return;
                }
@@ -78,7 +78,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
 
        if (i >= sas_ha->num_phys) {
                printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
-                      __FUNCTION__);
+                      __func__);
                spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
                return;
        }
index 601ec5b6a7f6ab1a240ff130b0dbc6f989b3ec4e..a8e3ef309070ae9e0da26381668e0bd847b68e0b 100644 (file)
@@ -343,7 +343,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
                                                       flags);
                                SAS_DPRINTK("%s: task 0x%p aborted from "
                                            "task_queue\n",
-                                           __FUNCTION__, task);
+                                           __func__, task);
                                return TASK_IS_ABORTED;
                        }
                }
@@ -351,13 +351,13 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
        }
 
        for (i = 0; i < 5; i++) {
-               SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task);
+               SAS_DPRINTK("%s: aborting task 0x%p\n", __func__, task);
                res = si->dft->lldd_abort_task(task);
 
                spin_lock_irqsave(&task->task_state_lock, flags);
                if (task->task_state_flags & SAS_TASK_STATE_DONE) {
                        spin_unlock_irqrestore(&task->task_state_lock, flags);
-                       SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__,
+                       SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
                                    task);
                        return TASK_IS_DONE;
                }
@@ -365,24 +365,24 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
 
                if (res == TMF_RESP_FUNC_COMPLETE) {
                        SAS_DPRINTK("%s: task 0x%p is aborted\n",
-                                   __FUNCTION__, task);
+                                   __func__, task);
                        return TASK_IS_ABORTED;
                } else if (si->dft->lldd_query_task) {
                        SAS_DPRINTK("%s: querying task 0x%p\n",
-                                   __FUNCTION__, task);
+                                   __func__, task);
                        res = si->dft->lldd_query_task(task);
                        switch (res) {
                        case TMF_RESP_FUNC_SUCC:
                                SAS_DPRINTK("%s: task 0x%p at LU\n",
-                                           __FUNCTION__, task);
+                                           __func__, task);
                                return TASK_IS_AT_LU;
                        case TMF_RESP_FUNC_COMPLETE:
                                SAS_DPRINTK("%s: task 0x%p not at LU\n",
-                                           __FUNCTION__, task);
+                                           __func__, task);
                                return TASK_IS_NOT_AT_LU;
                        case TMF_RESP_FUNC_FAILED:
                                 SAS_DPRINTK("%s: task 0x%p failed to abort\n",
-                                                __FUNCTION__, task);
+                                                __func__, task);
                                 return TASK_ABORT_FAILED;
                         }
 
@@ -545,7 +545,7 @@ Again:
 
                if (need_reset) {
                        SAS_DPRINTK("%s: task 0x%p requests reset\n",
-                                   __FUNCTION__, task);
+                                   __func__, task);
                        goto reset;
                }
 
@@ -556,13 +556,13 @@ Again:
 
                switch (res) {
                case TASK_IS_DONE:
-                       SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__,
+                       SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
                                    task);
                        sas_eh_finish_cmd(cmd);
                        continue;
                case TASK_IS_ABORTED:
                        SAS_DPRINTK("%s: task 0x%p is aborted\n",
-                                   __FUNCTION__, task);
+                                   __func__, task);
                        sas_eh_finish_cmd(cmd);
                        continue;
                case TASK_IS_AT_LU:
@@ -633,7 +633,7 @@ Again:
        }
        return list_empty(work_q);
 clear_q:
-       SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__);
+       SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__);
        list_for_each_entry_safe(cmd, n, work_q, eh_entry)
                sas_eh_finish_cmd(cmd);
 
@@ -650,7 +650,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost)
        list_splice_init(&shost->eh_cmd_q, &eh_work_q);
        spin_unlock_irqrestore(shost->host_lock, flags);
 
-       SAS_DPRINTK("Enter %s\n", __FUNCTION__);
+       SAS_DPRINTK("Enter %s\n", __func__);
        /*
         * Deal with commands that still have SAS tasks (i.e. they didn't
         * complete via the normal sas_task completion mechanism)
@@ -669,7 +669,7 @@ void sas_scsi_recover_host(struct Scsi_Host *shost)
 
 out:
        scsi_eh_flush_done_q(&ha->eh_done_q);
-       SAS_DPRINTK("--- Exit %s\n", __FUNCTION__);
+       SAS_DPRINTK("--- Exit %s\n", __func__);
        return;
 }
 
@@ -990,7 +990,7 @@ int __sas_task_abort(struct sas_task *task)
        if (task->task_state_flags & SAS_TASK_STATE_ABORTED ||
            task->task_state_flags & SAS_TASK_STATE_DONE) {
                spin_unlock_irqrestore(&task->task_state_lock, flags);
-               SAS_DPRINTK("%s: Task %p already finished.\n", __FUNCTION__,
+               SAS_DPRINTK("%s: Task %p already finished.\n", __func__,
                            task);
                return 0;
        }
index 6d6a76e65a6c3fc9e391c17b2fe34cf61176f718..15e2d132e8b9e07b99d39c4079557a3d16604cc3 100644 (file)
@@ -39,7 +39,7 @@ enum srp_task_attributes {
 /* tmp - will replace with SCSI logging stuff */
 #define eprintk(fmt, args...)                                  \
 do {                                                           \
-       printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);  \
+       printk("%s(%d) " fmt, __func__, __LINE__, ##args);      \
 } while (0)
 /* #define dprintk eprintk */
 #define dprintk(fmt, args...)
index 5b6e5395c8eb0313c1d23524a4bfd2b381ce3ed9..d51a2a4b43eb662f61a6603a21b3b5feb2337385 100644 (file)
@@ -2083,7 +2083,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
                if (iocbq_entry == NULL) {
                        printk(KERN_ERR "%s: only allocated %d iocbs of "
                                "expected %d count. Unloading driver.\n",
-                               __FUNCTION__, i, LPFC_IOCB_LIST_CNT);
+                               __func__, i, LPFC_IOCB_LIST_CNT);
                        error = -ENOMEM;
                        goto out_free_iocbq;
                }
@@ -2093,7 +2093,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
                        kfree (iocbq_entry);
                        printk(KERN_ERR "%s: failed to allocate IOTAG. "
                               "Unloading driver.\n",
-                               __FUNCTION__);
+                               __func__);
                        error = -ENOMEM;
                        goto out_free_iocbq;
                }
index c94da4f2b8a6eb8dc196ec1ee6b674242a020eb5..1bcebbd3dfacf8a3588abbeece892e19f44eadd3 100644 (file)
@@ -341,7 +341,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
                if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
                        printk(KERN_ERR "%s: Too many sg segments from "
                               "dma_map_sg.  Config %d, seg_cnt %d",
-                              __FUNCTION__, phba->cfg_sg_seg_cnt,
+                              __func__, phba->cfg_sg_seg_cnt,
                               lpfc_cmd->seg_cnt);
                        scsi_dma_unmap(scsi_cmnd);
                        return 1;
index f40aa7b905f716e335b4d30b33e1859ed478b214..50fe07646738cc20043de2d56452063f64673296 100644 (file)
@@ -219,7 +219,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
        case CMD_IOCB_LOGENTRY_CN:
        case CMD_IOCB_LOGENTRY_ASYNC_CN:
                printk("%s - Unhandled SLI-3 Command x%x\n",
-                               __FUNCTION__, iocb_cmnd);
+                               __func__, iocb_cmnd);
                type = LPFC_UNKNOWN_IOCB;
                break;
        default:
@@ -1715,7 +1715,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
                rspiocbp = __lpfc_sli_get_iocbq(phba);
                if (rspiocbp == NULL) {
                        printk(KERN_ERR "%s: out of buffers! Failing "
-                              "completion.\n", __FUNCTION__);
+                              "completion.\n", __func__);
                        break;
                }
 
@@ -3793,7 +3793,7 @@ lpfc_sli_validate_fcp_iocb(struct lpfc_iocbq *iocbq, struct lpfc_vport *vport,
                break;
        default:
                printk(KERN_ERR "%s: Unknown context cmd type, value %d\n",
-                       __FUNCTION__, ctx_cmd);
+                       __func__, ctx_cmd);
                break;
        }
 
index f62ed468ada0bc5ce44ce371a42f708310f3d9d7..5ead1283a84424cfad432a6247e4020899ef7bdd 100644 (file)
@@ -265,7 +265,7 @@ typedef struct {
 #define ASSERT(expression)                                             \
        if (!(expression)) {                                            \
        ASSERT_ACTION("assertion failed:(%s), file: %s, line: %d:%s\n", \
-                       #expression, __FILE__, __LINE__, __FUNCTION__); \
+                       #expression, __FILE__, __LINE__, __func__);     \
        }
 #else
 #define ASSERT(expression)
index 70a0f11f48b235d769b1efb088d1c9bd2c98f949..805bb61dde1871946b5c4cce24c0314e7efa79e5 100644 (file)
@@ -458,7 +458,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        if (adapter == NULL) {
                con_log(CL_ANN, (KERN_WARNING
-               "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__));
+               "megaraid: out of memory, %s %d.\n", __func__, __LINE__));
 
                goto out_probe_one;
        }
@@ -1002,7 +1002,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
 
        if (!raid_dev->una_mbox64) {
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
                return -1;
        }
@@ -1030,7 +1030,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
        if (!adapter->ibuf) {
 
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
 
                goto out_free_common_mbox;
@@ -1052,7 +1052,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
 
        if (adapter->kscb_list == NULL) {
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
                goto out_free_ibuf;
        }
@@ -1060,7 +1060,7 @@ megaraid_alloc_cmd_packets(adapter_t *adapter)
        // memory allocation for our command packets
        if (megaraid_mbox_setup_dma_pools(adapter) != 0) {
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
                goto out_free_scb_list;
        }
@@ -2981,7 +2981,7 @@ megaraid_mbox_product_info(adapter_t *adapter)
 
        if (pinfo == NULL) {
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
 
                return -1;
@@ -3508,7 +3508,7 @@ megaraid_cmm_register(adapter_t *adapter)
 
        if (adapter->uscb_list == NULL) {
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
                return -1;
        }
@@ -3879,7 +3879,7 @@ megaraid_sysfs_alloc_resources(adapter_t *adapter)
                !raid_dev->sysfs_buffer) {
 
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid: out of memory, %s %d\n", __func__,
                        __LINE__));
 
                rval = -ENOMEM;
index ac3b280c2a72abb7a310834b4ae45a924327db32..f680561d2c6f6101bac31fb4ad5ef7dcf2ded20c 100644 (file)
@@ -929,7 +929,7 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
                        !adapter->pthru_dma_pool) {
 
                con_log(CL_ANN, (KERN_WARNING
-                       "megaraid cmm: out of memory, %s %d\n", __FUNCTION__,
+                       "megaraid cmm: out of memory, %s %d\n", __func__,
                        __LINE__));
 
                rval = (-ENOMEM);
@@ -957,7 +957,7 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp)
 
                        con_log(CL_ANN, (KERN_WARNING
                                "megaraid cmm: out of memory, %s %d\n",
-                                       __FUNCTION__, __LINE__));
+                                       __func__, __LINE__));
 
                        rval = (-ENOMEM);
 
index 7fed35372150afe68d619d61d4faa4a2a4588437..edf9fdb3cb3c7dda1faa3059defdd66501eefe58 100644 (file)
@@ -299,9 +299,9 @@ static struct scsi_host_template nsp32_template = {
 #else
 # define NSP32_DEBUG_MASK            0xffffff
 # define nsp32_msg(type, args...) \
-       nsp32_message (__FUNCTION__, __LINE__, (type), args)
+       nsp32_message (__func__, __LINE__, (type), args)
 # define nsp32_dbg(mask, args...) \
-       nsp32_dmessage(__FUNCTION__, __LINE__, (mask), args)
+       nsp32_dmessage(__func__, __LINE__, (mask), args)
 #endif
 
 #define NSP32_DEBUG_QUEUECOMMAND       BIT(0)
index ef3c59cbcff601b6a1274e07349699c98efc240b..2fb3fb58858da6888accef3073f29f41423ff03d 100644 (file)
@@ -88,7 +88,7 @@ static void print_commandk (unsigned char *command)
        int i,s;
 //     printk(KERN_DEBUG);
        print_opcodek(command[0]);
-       /*printk(KERN_DEBUG "%s ", __FUNCTION__);*/
+       /*printk(KERN_DEBUG "%s ", __func__);*/
        if ((command[0] >> 5) == 6 ||
            (command[0] >> 5) == 7 ) {
                s = 12; /* vender specific */
index 5082ca3c6876035df3818035873a47f1e6d181f9..a221b6ef9fa991dac4e685c4e8ac290a6cb50be8 100644 (file)
@@ -107,9 +107,9 @@ static nsp_hw_data nsp_data_base; /* attach <-> detect glue */
 #else
 # define NSP_DEBUG_MASK                0xffffff
 # define nsp_msg(type, args...) \
-       nsp_cs_message (__FUNCTION__, __LINE__, (type), args)
+       nsp_cs_message (__func__, __LINE__, (type), args)
 # define nsp_dbg(mask, args...) \
-       nsp_cs_dmessage(__FUNCTION__, __LINE__, (mask), args)
+       nsp_cs_dmessage(__func__, __LINE__, (mask), args)
 #endif
 
 #define NSP_DEBUG_QUEUECOMMAND         BIT(0)
index 2f75fe6e35a732d5eb10d3f2f174207c2f56add4..3c6ef64fcbff709f233181411b81f50e23221e1b 100644 (file)
@@ -90,7 +90,7 @@ static void print_commandk (unsigned char *command)
        int i, s;
        printk(KERN_DEBUG);
        print_opcodek(command[0]);
-       /*printk(KERN_DEBUG "%s ", __FUNCTION__);*/
+       /*printk(KERN_DEBUG "%s ", __func__);*/
        if ((command[0] >> 5) == 6 ||
            (command[0] >> 5) == 7 ) {
                s = 12; /* vender specific */
index f655ae320b483cfc418a30691b601f23a271b328..8aa0bd987e29e95d2f18578e8d64c9900e59062e 100644 (file)
@@ -171,7 +171,7 @@ static int device_check(ppa_struct *dev);
 
 #if PPA_DEBUG > 0
 #define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\
-          y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);
+          y, __func__, __LINE__); ppa_fail_func(x,y);
 static inline void ppa_fail_func(ppa_struct *dev, int error_code)
 #else
 static inline void ppa_fail(ppa_struct *dev, int error_code)
index 3754ab87f89a0d89ca534db70d371527abd41c9c..37f9ba0cd798cf975a0139e5bf1fae99f5508923 100644 (file)
@@ -1695,7 +1695,7 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha)
        risc_code_size = *ql1280_board_tbl[ha->devnum].fwlen;
 
        dprintk(1, "%s: DMA RISC code (%i) words\n",
-                       __FUNCTION__, risc_code_size);
+                       __func__, risc_code_size);
 
        num = 0;
        while (risc_code_size > 0) {
@@ -1721,7 +1721,7 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha)
                mb[7] = pci_dma_hi32(ha->request_dma) & 0xffff;
                mb[6] = pci_dma_hi32(ha->request_dma) >> 16;
                dprintk(2, "%s: op=%d  0x%p = 0x%4x,0x%4x,0x%4x,0x%4x\n",
-                               __FUNCTION__, mb[0],
+                               __func__, mb[0],
                                (void *)(long)ha->request_dma,
                                mb[6], mb[7], mb[2], mb[3]);
                err = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | BIT_2 |
@@ -1753,10 +1753,10 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha)
                        if (tbuf[i] != sp[i] && warn++ < 10) {
                                printk(KERN_ERR "%s: FW compare error @ "
                                                "byte(0x%x) loop#=%x\n",
-                                               __FUNCTION__, i, num);
+                                               __func__, i, num);
                                printk(KERN_ERR "%s: FWbyte=%x  "
                                                "FWfromChip=%x\n",
-                                               __FUNCTION__, sp[i], tbuf[i]);
+                                               __func__, sp[i], tbuf[i]);
                                /*break; */
                        }
                }
@@ -1781,7 +1781,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha)
        int err;
 
        dprintk(1, "%s: Verifying checksum of loaded RISC code.\n",
-                       __FUNCTION__);
+                       __func__);
 
        /* Verify checksum of loaded RISC code. */
        mb[0] = MBC_VERIFY_CHECKSUM;
@@ -1794,7 +1794,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha)
        }
 
        /* Start firmware execution. */
-       dprintk(1, "%s: start firmware running.\n", __FUNCTION__);
+       dprintk(1, "%s: start firmware running.\n", __func__);
        mb[0] = MBC_EXECUTE_FIRMWARE;
        mb[1] = *ql1280_board_tbl[ha->devnum].fwstart;
        err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]);
index 8dd88fc1244a60e688ed9e18a217707ee3976905..7a4409ab30eaaab0b751226f4e0713b5469166d5 100644 (file)
@@ -20,18 +20,12 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj,
 {
        struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
            struct device, kobj)));
-       char *rbuf = (char *)ha->fw_dump;
 
        if (ha->fw_dump_reading == 0)
                return 0;
-       if (off > ha->fw_dump_len)
-                return 0;
-       if (off + count > ha->fw_dump_len)
-               count = ha->fw_dump_len - off;
 
-       memcpy(buf, &rbuf[off], count);
-
-       return (count);
+       return memory_read_from_buffer(buf, count, &off, ha->fw_dump,
+                                       ha->fw_dump_len);
 }
 
 static ssize_t
@@ -94,20 +88,13 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj,
 {
        struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
            struct device, kobj)));
-       int             size = ha->nvram_size;
-       char            *nvram_cache = ha->nvram;
 
-       if (!capable(CAP_SYS_ADMIN) || off > size || count == 0)
+       if (!capable(CAP_SYS_ADMIN))
                return 0;
-       if (off + count > size) {
-               size -= off;
-               count = size;
-       }
 
        /* Read NVRAM data from cache. */
-       memcpy(buf, &nvram_cache[off], count);
-
-       return count;
+       return memory_read_from_buffer(buf, count, &off, ha->nvram,
+                                       ha->nvram_size);
 }
 
 static ssize_t
@@ -175,14 +162,9 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj,
 
        if (ha->optrom_state != QLA_SREADING)
                return 0;
-       if (off > ha->optrom_region_size)
-               return 0;
-       if (off + count > ha->optrom_region_size)
-               count = ha->optrom_region_size - off;
-
-       memcpy(buf, &ha->optrom_buffer[off], count);
 
-       return count;
+       return memory_read_from_buffer(buf, count, &off, ha->optrom_buffer,
+                                       ha->optrom_region_size);
 }
 
 static ssize_t
@@ -374,20 +356,12 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj,
 {
        struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
            struct device, kobj)));
-       int           size = ha->vpd_size;
-       char          *vpd_cache = ha->vpd;
 
-       if (!capable(CAP_SYS_ADMIN) || off > size || count == 0)
+       if (!capable(CAP_SYS_ADMIN))
                return 0;
-       if (off + count > size) {
-               size -= off;
-               count = size;
-       }
 
        /* Read NVRAM data from cache. */
-       memcpy(buf, &vpd_cache[off], count);
-
-       return count;
+       return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
 }
 
 static ssize_t
@@ -557,8 +531,10 @@ qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr,
        scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
        uint32_t sn;
 
-       if (IS_FWI2_CAPABLE(ha))
-               return snprintf(buf, PAGE_SIZE, "\n");
+       if (IS_FWI2_CAPABLE(ha)) {
+               qla2xxx_get_vpd_field(ha, "SN", buf, PAGE_SIZE);
+               return snprintf(buf, PAGE_SIZE, "%s\n", buf);
+       }
 
        sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1;
        return snprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000,
@@ -809,6 +785,16 @@ qla2x00_optrom_fw_version_show(struct device *dev,
            ha->fw_revision[3]);
 }
 
+static ssize_t
+qla2x00_total_isp_aborts_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+           ha->qla_stats.total_isp_aborts);
+}
+
 static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
 static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
 static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
@@ -831,6 +817,8 @@ static DEVICE_ATTR(optrom_fcode_version, S_IRUGO,
                   qla2x00_optrom_fcode_version_show, NULL);
 static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show,
                   NULL);
+static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show,
+                  NULL);
 
 struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_driver_version,
@@ -849,6 +837,7 @@ struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_optrom_efi_version,
        &dev_attr_optrom_fcode_version,
        &dev_attr_optrom_fw_version,
+       &dev_attr_total_isp_aborts,
        NULL,
 };
 
@@ -972,26 +961,39 @@ qla2x00_get_starget_port_id(struct scsi_target *starget)
 }
 
 static void
-qla2x00_get_rport_loss_tmo(struct fc_rport *rport)
+qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
 {
-       struct Scsi_Host *host = rport_to_shost(rport);
-       scsi_qla_host_t *ha = shost_priv(host);
-
-       rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+       if (timeout)
+               rport->dev_loss_tmo = timeout;
+       else
+               rport->dev_loss_tmo = 1;
 }
 
 static void
-qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport)
 {
        struct Scsi_Host *host = rport_to_shost(rport);
-       scsi_qla_host_t *ha = shost_priv(host);
+       fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+
+       qla2x00_abort_fcport_cmds(fcport);
+
+       /*
+        * Transport has effectively 'deleted' the rport, clear
+        * all local references.
+        */
+       spin_lock_irq(host->host_lock);
+       fcport->rport = NULL;
+       *((fc_port_t **)rport->dd_data) = NULL;
+       spin_unlock_irq(host->host_lock);
+}
 
-       if (timeout)
-               ha->port_down_retry_count = timeout;
-       else
-               ha->port_down_retry_count = 1;
+static void
+qla2x00_terminate_rport_io(struct fc_rport *rport)
+{
+       fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
 
-       rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+       qla2x00_abort_fcport_cmds(fcport);
+       scsi_target_unblock(&rport->dev);
 }
 
 static int
@@ -1045,6 +1047,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
        pfc_host_stat->invalid_tx_word_count = stats->inval_xmit_word_cnt;
        pfc_host_stat->invalid_crc_count = stats->inval_crc_cnt;
        if (IS_FWI2_CAPABLE(ha)) {
+               pfc_host_stat->lip_count = stats->lip_cnt;
                pfc_host_stat->tx_frames = stats->tx_frames;
                pfc_host_stat->rx_frames = stats->rx_frames;
                pfc_host_stat->dumped_frames = stats->dumped_frames;
@@ -1173,17 +1176,16 @@ vport_create_failed_2:
 static int
 qla24xx_vport_delete(struct fc_vport *fc_vport)
 {
-       scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
        scsi_qla_host_t *vha = fc_vport->dd_data;
+       scsi_qla_host_t *pha = to_qla_parent(vha);
+
+       while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) ||
+           test_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags))
+               msleep(1000);
 
        qla24xx_disable_vp(vha);
        qla24xx_deallocate_vp_id(vha);
 
-       mutex_lock(&ha->vport_lock);
-       ha->cur_vport_count--;
-       clear_bit(vha->vp_idx, ha->vp_idx_map);
-       mutex_unlock(&ha->vport_lock);
-
        kfree(vha->node_name);
        kfree(vha->port_name);
 
@@ -1248,11 +1250,12 @@ struct fc_function_template qla2xxx_transport_functions = {
        .get_starget_port_id  = qla2x00_get_starget_port_id,
        .show_starget_port_id = 1,
 
-       .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo,
        .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
        .show_rport_dev_loss_tmo = 1,
 
        .issue_fc_host_lip = qla2x00_issue_lip,
+       .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
+       .terminate_rport_io = qla2x00_terminate_rport_io,
        .get_fc_host_stats = qla2x00_get_fc_host_stats,
 
        .vport_create = qla24xx_vport_create,
@@ -1291,11 +1294,12 @@ struct fc_function_template qla2xxx_transport_vport_functions = {
        .get_starget_port_id  = qla2x00_get_starget_port_id,
        .show_starget_port_id = 1,
 
-       .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo,
        .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
        .show_rport_dev_loss_tmo = 1,
 
        .issue_fc_host_lip = qla2x00_issue_lip,
+       .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
+       .terminate_rport_io = qla2x00_terminate_rport_io,
        .get_fc_host_stats = qla2x00_get_fc_host_stats,
 };
 
index cbef785765cf2ead72b64899a260a75bfca849d2..510ba64bc286e185cd85346c5728761bc09d7fa5 100644 (file)
@@ -216,7 +216,7 @@ qla24xx_soft_reset(scsi_qla_host_t *ha)
 
 static int
 qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram,
-    uint16_t ram_words, void **nxt)
+    uint32_t ram_words, void **nxt)
 {
        int rval;
        uint32_t cnt, stat, timer, words, idx;
index 8dd600013bd1ca4d8c87dcf4b73b1b254a810474..6da31ba94404f7bf6b48f418a3ba18829569adb8 100644 (file)
@@ -864,7 +864,8 @@ struct link_statistics {
        uint32_t prim_seq_err_cnt;
        uint32_t inval_xmit_word_cnt;
        uint32_t inval_crc_cnt;
-       uint32_t unused1[0x1b];
+       uint32_t lip_cnt;
+       uint32_t unused1[0x1a];
        uint32_t tx_frames;
        uint32_t rx_frames;
        uint32_t dumped_frames;
@@ -1544,7 +1545,6 @@ typedef struct fc_port {
        int login_retry;
        atomic_t port_down_timer;
 
-       spinlock_t rport_lock;
        struct fc_rport *rport, *drport;
        u32 supported_classes;
 
@@ -2155,6 +2155,10 @@ struct qla_chip_state_84xx {
        uint32_t gold_fw_version;
 };
 
+struct qla_statistics {
+       uint32_t total_isp_aborts;
+};
+
 /*
  * Linux Host Adapter structure
  */
@@ -2166,7 +2170,6 @@ typedef struct scsi_qla_host {
        struct pci_dev  *pdev;
 
        unsigned long   host_no;
-       unsigned long   instance;
 
        volatile struct {
                uint32_t        init_done               :1;
@@ -2515,7 +2518,7 @@ typedef struct scsi_qla_host {
 
        uint8_t         model_number[16+1];
 #define BINZERO                "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-       char            *model_desc;
+       char            model_desc[80];
        uint8_t         adapter_id[16+1];
 
        uint8_t         *node_name;
@@ -2596,6 +2599,7 @@ typedef struct scsi_qla_host {
        int             cur_vport_count;
 
        struct qla_chip_state_84xx *cs84xx;
+       struct qla_statistics qla_stats;
 } scsi_qla_host_t;
 
 
index 9b4bebee6879fe6f19bcaad2f3433338659b40b2..0b156735e9a65ee8356b520004d3302b034d261e 100644 (file)
@@ -62,7 +62,7 @@ extern int ql2xfdmienable;
 extern int ql2xallocfwdump;
 extern int ql2xextended_error_logging;
 extern int ql2xqfullrampup;
-extern int num_hosts;
+extern int ql2xiidmaenable;
 
 extern int qla2x00_loop_reset(scsi_qla_host_t *);
 extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -71,6 +71,8 @@ extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum
 extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t,
     uint16_t, uint16_t);
 
+extern void qla2x00_abort_fcport_cmds(fc_port_t *);
+
 /*
  * Global Functions in qla_mid.c source file.
  */
@@ -312,6 +314,7 @@ extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t,
     uint16_t, uint16_t);
 
 extern void qla2xxx_get_flash_info(scsi_qla_host_t *);
+extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t);
 
 /*
  * Global Function Prototypes in qla_dbg.c source file.
index 4cb80b476c85c69c65277d5de2709f863f13a958..c2a4bfbcb05bc2ecf4ffa7a20d763ea7d86bf4b9 100644 (file)
@@ -1661,6 +1661,12 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha)
 {
        int rval;
 
+       if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+               DEBUG2(printk("scsi(%ld): FDMI unsupported on "
+                   "ISP2100/ISP2200.\n", ha->host_no));
+               return QLA_SUCCESS;
+       }
+
        rval = qla2x00_mgmt_svr_login(ha);
        if (rval)
                return rval;
index bbbc5a632a1d22f2334761283bfa1db4254a5ff2..601a6b29750c5beec1d52cc1b5638b396073f7f2 100644 (file)
@@ -334,6 +334,8 @@ static int
 qla2x00_isp_firmware(scsi_qla_host_t *ha)
 {
        int  rval;
+       uint16_t loop_id, topo, sw_cap;
+       uint8_t domain, area, al_pa;
 
        /* Assume loading risc code */
        rval = QLA_FUNCTION_FAILED;
@@ -345,6 +347,11 @@ qla2x00_isp_firmware(scsi_qla_host_t *ha)
 
                /* Verify checksum of loaded RISC code. */
                rval = qla2x00_verify_checksum(ha, ha->fw_srisc_address);
+               if (rval == QLA_SUCCESS) {
+                       /* And, verify we are not in ROM code. */
+                       rval = qla2x00_get_adapter_id(ha, &loop_id, &al_pa,
+                           &area, &domain, &topo, &sw_cap);
+               }
        }
 
        if (rval) {
@@ -722,7 +729,7 @@ qla24xx_chip_diag(scsi_qla_host_t *ha)
        /* Perform RISC reset. */
        qla24xx_reset_risc(ha);
 
-       ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024;
+       ha->fw_transfer_size = REQUEST_ENTRY_SIZE * ha->request_q_length;
 
        rval = qla2x00_mbx_reg_test(ha);
        if (rval) {
@@ -768,42 +775,16 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
                mem_size = (ha->fw_memory_size - 0x100000 + 1) *
                    sizeof(uint32_t);
 
-               /* Allocate memory for Extended Trace Buffer. */
-               tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma,
-                   GFP_KERNEL);
-               if (!tc) {
-                       qla_printk(KERN_WARNING, ha, "Unable to allocate "
-                           "(%d KB) for EFT.\n", EFT_SIZE / 1024);
-                       goto cont_alloc;
-               }
-
-               memset(tc, 0, EFT_SIZE);
-               rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS);
-               if (rval) {
-                       qla_printk(KERN_WARNING, ha, "Unable to initialize "
-                           "EFT (%d).\n", rval);
-                       dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc,
-                           tc_dma);
-                       goto cont_alloc;
-               }
-
-               qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n",
-                   EFT_SIZE / 1024);
-
-               eft_size = EFT_SIZE;
-               ha->eft_dma = tc_dma;
-               ha->eft = tc;
-
                /* Allocate memory for Fibre Channel Event Buffer. */
                if (!IS_QLA25XX(ha))
-                       goto cont_alloc;
+                       goto try_eft;
 
                tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma,
                    GFP_KERNEL);
                if (!tc) {
                        qla_printk(KERN_WARNING, ha, "Unable to allocate "
                            "(%d KB) for FCE.\n", FCE_SIZE / 1024);
-                       goto cont_alloc;
+                       goto try_eft;
                }
 
                memset(tc, 0, FCE_SIZE);
@@ -815,7 +796,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
                        dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc,
                            tc_dma);
                        ha->flags.fce_enabled = 0;
-                       goto cont_alloc;
+                       goto try_eft;
                }
 
                qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n",
@@ -825,6 +806,32 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
                ha->flags.fce_enabled = 1;
                ha->fce_dma = tc_dma;
                ha->fce = tc;
+try_eft:
+               /* Allocate memory for Extended Trace Buffer. */
+               tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma,
+                   GFP_KERNEL);
+               if (!tc) {
+                       qla_printk(KERN_WARNING, ha, "Unable to allocate "
+                           "(%d KB) for EFT.\n", EFT_SIZE / 1024);
+                       goto cont_alloc;
+               }
+
+               memset(tc, 0, EFT_SIZE);
+               rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS);
+               if (rval) {
+                       qla_printk(KERN_WARNING, ha, "Unable to initialize "
+                           "EFT (%d).\n", rval);
+                       dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc,
+                           tc_dma);
+                       goto cont_alloc;
+               }
+
+               qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n",
+                   EFT_SIZE / 1024);
+
+               eft_size = EFT_SIZE;
+               ha->eft_dma = tc_dma;
+               ha->eft = tc;
        }
 cont_alloc:
        req_q_size = ha->request_q_length * sizeof(request_t);
@@ -1501,18 +1508,25 @@ qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *de
                index = (ha->pdev->subsystem_device & 0xff);
                if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
                    index < QLA_MODEL_NAMES)
-                       ha->model_desc = qla2x00_model_name[index * 2 + 1];
+                       strncpy(ha->model_desc,
+                           qla2x00_model_name[index * 2 + 1],
+                           sizeof(ha->model_desc) - 1);
        } else {
                index = (ha->pdev->subsystem_device & 0xff);
                if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
                    index < QLA_MODEL_NAMES) {
                        strcpy(ha->model_number,
                            qla2x00_model_name[index * 2]);
-                       ha->model_desc = qla2x00_model_name[index * 2 + 1];
+                       strncpy(ha->model_desc,
+                           qla2x00_model_name[index * 2 + 1],
+                           sizeof(ha->model_desc) - 1);
                } else {
                        strcpy(ha->model_number, def);
                }
        }
+       if (IS_FWI2_CAPABLE(ha))
+               qla2xxx_get_vpd_field(ha, "\x82", ha->model_desc,
+                   sizeof(ha->model_desc));
 }
 
 /* On sparc systems, obtain port and node WWN from firmware
@@ -1864,12 +1878,11 @@ qla2x00_rport_del(void *data)
 {
        fc_port_t *fcport = data;
        struct fc_rport *rport;
-       unsigned long flags;
 
-       spin_lock_irqsave(&fcport->rport_lock, flags);
+       spin_lock_irq(fcport->ha->host->host_lock);
        rport = fcport->drport;
        fcport->drport = NULL;
-       spin_unlock_irqrestore(&fcport->rport_lock, flags);
+       spin_unlock_irq(fcport->ha->host->host_lock);
        if (rport)
                fc_remote_port_delete(rport);
 }
@@ -1898,7 +1911,6 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
        atomic_set(&fcport->state, FCS_UNCONFIGURED);
        fcport->flags = FCF_RLC_SUPPORT;
        fcport->supported_classes = FC_COS_UNSPECIFIED;
-       spin_lock_init(&fcport->rport_lock);
 
        return fcport;
 }
@@ -2007,8 +2019,10 @@ qla2x00_configure_loop(scsi_qla_host_t *ha)
        if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
                if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
                        set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
-               if (test_bit(RSCN_UPDATE, &save_flags))
+               if (test_bit(RSCN_UPDATE, &save_flags)) {
+                       ha->flags.rscn_queue_overflow = 1;
                        set_bit(RSCN_UPDATE, &ha->dpc_flags);
+               }
        }
 
        return (rval);
@@ -2243,28 +2257,24 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
 {
        struct fc_rport_identifiers rport_ids;
        struct fc_rport *rport;
-       unsigned long flags;
 
        if (fcport->drport)
                qla2x00_rport_del(fcport);
-       if (fcport->rport)
-               return;
 
        rport_ids.node_name = wwn_to_u64(fcport->node_name);
        rport_ids.port_name = wwn_to_u64(fcport->port_name);
        rport_ids.port_id = fcport->d_id.b.domain << 16 |
            fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
        rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
-       rport = fc_remote_port_add(ha->host, 0, &rport_ids);
+       fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids);
        if (!rport) {
                qla_printk(KERN_WARNING, ha,
                    "Unable to allocate fc remote port!\n");
                return;
        }
-       spin_lock_irqsave(&fcport->rport_lock, flags);
-       fcport->rport = rport;
+       spin_lock_irq(fcport->ha->host->host_lock);
        *((fc_port_t **)rport->dd_data) = fcport;
-       spin_unlock_irqrestore(&fcport->rport_lock, flags);
+       spin_unlock_irq(fcport->ha->host->host_lock);
 
        rport->supported_classes = fcport->supported_classes;
 
@@ -2565,7 +2575,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
                } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) {
                        kfree(swl);
                        swl = NULL;
-               } else if (qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) {
+               } else if (ql2xiidmaenable &&
+                   qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) {
                        qla2x00_gpsc(ha, swl);
                }
        }
@@ -3220,7 +3231,8 @@ qla2x00_update_fcports(scsi_qla_host_t *ha)
 
        /* Go with deferred removal of rport references. */
        list_for_each_entry(fcport, &ha->fcports, list)
-               if (fcport->drport)
+               if (fcport->drport &&
+                   atomic_read(&fcport->state) != FCS_UNCONFIGURED)
                        qla2x00_rport_del(fcport);
 }
 
@@ -3243,6 +3255,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
        if (ha->flags.online) {
                ha->flags.online = 0;
                clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+               ha->qla_stats.total_isp_aborts++;
 
                qla_printk(KERN_INFO, ha,
                    "Performing ISP error recovery - ha= %p.\n", ha);
@@ -3283,17 +3296,6 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
                        ha->isp_abort_cnt = 0;
                        clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
 
-                       if (ha->eft) {
-                               memset(ha->eft, 0, EFT_SIZE);
-                               rval = qla2x00_enable_eft_trace(ha,
-                                   ha->eft_dma, EFT_NUM_BUFFERS);
-                               if (rval) {
-                                       qla_printk(KERN_WARNING, ha,
-                                           "Unable to reinitialize EFT "
-                                           "(%d).\n", rval);
-                               }
-                       }
-
                        if (ha->fce) {
                                ha->flags.fce_enabled = 1;
                                memset(ha->fce, 0,
@@ -3308,6 +3310,17 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
                                        ha->flags.fce_enabled = 0;
                                }
                        }
+
+                       if (ha->eft) {
+                               memset(ha->eft, 0, EFT_SIZE);
+                               rval = qla2x00_enable_eft_trace(ha,
+                                   ha->eft_dma, EFT_NUM_BUFFERS);
+                               if (rval) {
+                                       qla_printk(KERN_WARNING, ha,
+                                           "Unable to reinitialize EFT "
+                                           "(%d).\n", rval);
+                               }
+                       }
                } else {        /* failed the ISP abort */
                        ha->flags.online = 1;
                        if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) {
@@ -4026,8 +4039,8 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha)
        ret = qla2x00_stop_firmware(ha);
        for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT &&
            retries ; retries--) {
-               qla2x00_reset_chip(ha);
-               if (qla2x00_chip_diag(ha) != QLA_SUCCESS)
+               ha->isp_ops->reset_chip(ha);
+               if (ha->isp_ops->chip_diag(ha) != QLA_SUCCESS)
                        continue;
                if (qla2x00_setup_chip(ha) != QLA_SUCCESS)
                        continue;
@@ -4049,7 +4062,7 @@ qla24xx_configure_vhba(scsi_qla_host_t *ha)
        rval = qla2x00_fw_ready(ha->parent);
        if (rval == QLA_SUCCESS) {
                clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
-               qla2x00_marker(ha->parent, 0, 0, MK_SYNC_ALL);
+               qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
        }
 
        ha->flags.management_server_logged_in = 0;
index 5489d5024673a326b5345f5545151a7dab37a533..d57669aa4615d33d80fe87cfe1f9550555466518 100644 (file)
@@ -454,10 +454,11 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
 {
        int ret;
        unsigned long flags = 0;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
-       spin_lock_irqsave(&ha->hardware_lock, flags);
+       spin_lock_irqsave(&pha->hardware_lock, flags);
        ret = __qla2x00_marker(ha, loop_id, lun, type);
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
 
        return (ret);
 }
@@ -672,7 +673,7 @@ qla24xx_start_scsi(srb_t *sp)
 {
        int             ret, nseg;
        unsigned long   flags;
-       scsi_qla_host_t *ha;
+       scsi_qla_host_t *ha, *pha;
        struct scsi_cmnd *cmd;
        uint32_t        *clr_ptr;
        uint32_t        index;
@@ -686,6 +687,7 @@ qla24xx_start_scsi(srb_t *sp)
        /* Setup device pointers. */
        ret = 0;
        ha = sp->ha;
+       pha = to_qla_parent(ha);
        reg = &ha->iobase->isp24;
        cmd = sp->cmd;
        /* So we know we haven't pci_map'ed anything yet */
@@ -700,7 +702,7 @@ qla24xx_start_scsi(srb_t *sp)
        }
 
        /* Acquire ring specific lock */
-       spin_lock_irqsave(&ha->hardware_lock, flags);
+       spin_lock_irqsave(&pha->hardware_lock, flags);
 
        /* Check for room in outstanding command list. */
        handle = ha->current_outstanding_cmd;
@@ -795,14 +797,14 @@ qla24xx_start_scsi(srb_t *sp)
            ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
                qla24xx_process_response_queue(ha);
 
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
        return QLA_SUCCESS;
 
 queuing_error:
        if (tot_dsds)
                scsi_dma_unmap(cmd);
 
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
 
        return QLA_FUNCTION_FAILED;
 }
index ec63b79f900ae4216b02f95ec7ebb2b812d2541b..874d802edb7de482c79774bd760bdf61a7210ca4 100644 (file)
@@ -542,10 +542,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
                break;
 
        case MBA_PORT_UPDATE:           /* Port database update */
-               /* Only handle SCNs for our Vport index. */
-               if (ha->parent && ha->vp_idx != (mb[3] & 0xff))
-                       break;
-
                /*
                 * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET
                 * event etc. earlier indicating loop is down) then process
index 250d2f604397b02aca0840e5a8b4100f2378cb63..bc90d6b8d0a0d940bf1114c79c818cebc7ee81c5 100644 (file)
@@ -918,6 +918,8 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
        rval = qla2x00_mailbox_command(ha, mcp);
        if (mcp->mb[0] == MBS_COMMAND_ERROR)
                rval = QLA_COMMAND_ERROR;
+       else if (mcp->mb[0] == MBS_INVALID_COMMAND)
+               rval = QLA_INVALID_COMMAND;
 
        /* Return data. */
        *id = mcp->mb[1];
@@ -2161,17 +2163,18 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
        struct abort_entry_24xx *abt;
        dma_addr_t      abt_dma;
        uint32_t        handle;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
 
        fcport = sp->fcport;
 
-       spin_lock_irqsave(&ha->hardware_lock, flags);
+       spin_lock_irqsave(&pha->hardware_lock, flags);
        for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
-               if (ha->outstanding_cmds[handle] == sp)
+               if (pha->outstanding_cmds[handle] == sp)
                        break;
        }
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
        if (handle == MAX_OUTSTANDING_COMMANDS) {
                /* Command not found. */
                return QLA_FUNCTION_FAILED;
index 62a3ad6e8ecb0d1a0c457e3617019b39298344ba..50baf6a1d67cbc732eac8ec5578d36ea20b59fba 100644 (file)
@@ -43,6 +43,7 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
 
        set_bit(vp_id, ha->vp_idx_map);
        ha->num_vhosts++;
+       ha->cur_vport_count++;
        vha->vp_idx = vp_id;
        list_add_tail(&vha->vp_list, &ha->vp_list);
        mutex_unlock(&ha->vport_lock);
@@ -58,6 +59,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
        mutex_lock(&ha->vport_lock);
        vp_id = vha->vp_idx;
        ha->num_vhosts--;
+       ha->cur_vport_count--;
        clear_bit(vp_id, ha->vp_idx_map);
        list_del(&vha->vp_list);
        mutex_unlock(&ha->vport_lock);
@@ -103,8 +105,8 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
                    "loop_id=0x%04x :%x\n",
                    vha->host_no, fcport->loop_id, fcport->vp_idx));
 
-               atomic_set(&fcport->state, FCS_DEVICE_DEAD);
                qla2x00_mark_device_lost(vha, fcport, 0, 0);
+               atomic_set(&fcport->state, FCS_UNCONFIGURED);
        }
 }
 
@@ -276,7 +278,8 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
                clear_bit(RESET_ACTIVE, &vha->dpc_flags);
        }
 
-       if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+       if (atomic_read(&vha->vp_state) == VP_ACTIVE &&
+           test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
                if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) {
                        qla2x00_loop_resync(vha);
                        clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags);
@@ -390,7 +393,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
        vha->parent = ha;
        vha->fc_vport = fc_vport;
        vha->device_flags = 0;
-       vha->instance = num_hosts;
        vha->vp_idx = qla24xx_allocate_vp_id(vha);
        if (vha->vp_idx > ha->max_npiv_vports) {
                DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n",
@@ -428,7 +430,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
        host->max_cmd_len = MAX_CMDSZ;
        host->max_channel = MAX_BUSES - 1;
        host->max_lun = MAX_LUNS;
-       host->unique_id = vha->instance;
+       host->unique_id = host->host_no;
        host->max_id = MAX_TARGETS_2200;
        host->transportt = qla2xxx_transport_vport_template;
 
@@ -436,12 +438,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
            vha->host_no, vha));
 
        vha->flags.init_done = 1;
-       num_hosts++;
-
-       mutex_lock(&ha->vport_lock);
-       set_bit(vha->vp_idx, ha->vp_idx_map);
-       ha->cur_vport_count++;
-       mutex_unlock(&ha->vport_lock);
 
        return vha;
 
index 48eaa3bb5433892b2167f18215e6fe35e1cbe113..7c8af7ed2a5dc0d841fa797050d82fd5baefdfb1 100644 (file)
@@ -27,7 +27,6 @@ char qla2x00_version_str[40];
  */
 static struct kmem_cache *srb_cachep;
 
-int num_hosts;
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
 MODULE_PARM_DESC(ql2xlogintimeout,
@@ -87,6 +86,13 @@ MODULE_PARM_DESC(ql2xqfullrampup,
                "depth for a device after a queue-full condition has been "
                "detected.  Default is 120 seconds.");
 
+int ql2xiidmaenable=1;
+module_param(ql2xiidmaenable, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xiidmaenable,
+               "Enables iIDMA settings "
+               "Default is 1 - perform iIDMA. 0 - no iIDMA.");
+
+
 /*
  * SCSI host template entry points
  */
@@ -388,7 +394,7 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        }
 
        /* Close window on fcport/rport state-transitioning. */
-       if (!*(fc_port_t **)rport->dd_data) {
+       if (fcport->drport) {
                cmd->result = DID_IMM_RETRY << 16;
                goto qc_fail_command;
        }
@@ -443,7 +449,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        int rval;
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
-       if (unlikely(pci_channel_offline(ha->pdev))) {
+       if (unlikely(pci_channel_offline(pha->pdev))) {
                cmd->result = DID_REQUEUE << 16;
                goto qc24_fail_command;
        }
@@ -455,7 +461,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        }
 
        /* Close window on fcport/rport state-transitioning. */
-       if (!*(fc_port_t **)rport->dd_data) {
+       if (fcport->drport) {
                cmd->result = DID_IMM_RETRY << 16;
                goto qc24_fail_command;
        }
@@ -617,6 +623,40 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
        return (return_status);
 }
 
+void
+qla2x00_abort_fcport_cmds(fc_port_t *fcport)
+{
+       int cnt;
+       unsigned long flags;
+       srb_t *sp;
+       scsi_qla_host_t *ha = fcport->ha;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
+
+       spin_lock_irqsave(&pha->hardware_lock, flags);
+       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+               sp = pha->outstanding_cmds[cnt];
+               if (!sp)
+                       continue;
+               if (sp->fcport != fcport)
+                       continue;
+
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
+               if (ha->isp_ops->abort_command(ha, sp)) {
+                       DEBUG2(qla_printk(KERN_WARNING, ha,
+                           "Abort failed --  %lx\n", sp->cmd->serial_number));
+               } else {
+                       if (qla2x00_eh_wait_on_command(ha, sp->cmd) !=
+                           QLA_SUCCESS)
+                               DEBUG2(qla_printk(KERN_WARNING, ha,
+                                   "Abort failed while waiting --  %lx\n",
+                                   sp->cmd->serial_number));
+
+               }
+               spin_lock_irqsave(&pha->hardware_lock, flags);
+       }
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+}
+
 static void
 qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
 {
@@ -1073,7 +1113,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev)
        else
                scsi_deactivate_tcq(sdev, ha->max_q_depth);
 
-       rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+       rport->dev_loss_tmo = ha->port_down_retry_count;
 
        return 0;
 }
@@ -1629,9 +1669,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
        host->can_queue = ha->request_q_length + 128;
 
-       /* load the F/W, read paramaters, and init the H/W */
-       ha->instance = num_hosts;
-
        mutex_init(&ha->vport_lock);
        init_completion(&ha->mbx_cmd_comp);
        complete(&ha->mbx_cmd_comp);
@@ -1679,7 +1716,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        host->this_id = 255;
        host->cmd_per_lun = 3;
-       host->unique_id = ha->instance;
+       host->unique_id = host->host_no;
        host->max_cmd_len = MAX_CMDSZ;
        host->max_channel = MAX_BUSES - 1;
        host->max_lun = MAX_LUNS;
@@ -1700,8 +1737,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        ha->flags.init_done = 1;
        ha->flags.online = 1;
 
-       num_hosts++;
-
        ret = scsi_add_host(host, &pdev->dev);
        if (ret)
                goto probe_failed;
@@ -1813,27 +1848,21 @@ static inline void
 qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
     int defer)
 {
-       unsigned long flags;
        struct fc_rport *rport;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
 
        if (!fcport->rport)
                return;
 
        rport = fcport->rport;
        if (defer) {
-               spin_lock_irqsave(&fcport->rport_lock, flags);
+               spin_lock_irq(ha->host->host_lock);
                fcport->drport = rport;
-               fcport->rport = NULL;
-               *(fc_port_t **)rport->dd_data = NULL;
-               spin_unlock_irqrestore(&fcport->rport_lock, flags);
-               set_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
-       } else {
-               spin_lock_irqsave(&fcport->rport_lock, flags);
-               fcport->rport = NULL;
-               *(fc_port_t **)rport->dd_data = NULL;
-               spin_unlock_irqrestore(&fcport->rport_lock, flags);
+               spin_unlock_irq(ha->host->host_lock);
+               set_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags);
+               qla2xxx_wake_dpc(pha);
+       } else
                fc_remote_port_delete(rport);
-       }
 }
 
 /*
@@ -1903,7 +1932,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
        list_for_each_entry(fcport, &pha->fcports, list) {
-               if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx)
+               if (ha->vp_idx != fcport->vp_idx)
                        continue;
                /*
                 * No point in marking the device as lost, if the device is
@@ -1911,17 +1940,10 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
                 */
                if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
                        continue;
-               if (atomic_read(&fcport->state) == FCS_ONLINE) {
-                       if (defer)
-                               qla2x00_schedule_rport_del(ha, fcport, defer);
-                       else if (ha->vp_idx == fcport->vp_idx)
-                               qla2x00_schedule_rport_del(ha, fcport, defer);
-               }
+               if (atomic_read(&fcport->state) == FCS_ONLINE)
+                       qla2x00_schedule_rport_del(ha, fcport, defer);
                atomic_set(&fcport->state, FCS_DEVICE_LOST);
        }
-
-       if (defer)
-               qla2xxx_wake_dpc(ha);
 }
 
 /*
@@ -2156,7 +2178,7 @@ qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
 static int
 qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked)
 {
-       unsigned long flags;
+       unsigned long uninitialized_var(flags);
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
        if (!locked)
@@ -2313,8 +2335,10 @@ qla2x00_do_dpc(void *data)
                            ha->host_no));
                }
 
-               if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags))
+               if (test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) {
                        qla2x00_update_fcports(ha);
+                       clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
+               }
 
                if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
                    (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) {
index 1728ab3ccb201f19677885d488d8b12726cf094d..1bca744749353d24e7d1651efde761a4f473c93f 100644 (file)
@@ -869,11 +869,9 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
        uint32_t i;
        uint32_t *dwptr;
        struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
-       unsigned long flags;
 
        ret = QLA_SUCCESS;
 
-       spin_lock_irqsave(&ha->hardware_lock, flags);
        /* Enable flash write. */
        WRT_REG_DWORD(&reg->ctrl_status,
            RD_REG_DWORD(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
@@ -907,7 +905,6 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
        WRT_REG_DWORD(&reg->ctrl_status,
            RD_REG_DWORD(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
        RD_REG_DWORD(&reg->ctrl_status);        /* PCI Posting. */
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
        return ret;
 }
@@ -2305,6 +2302,51 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
        return ret;
 }
 
+static int
+qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end)
+{
+       if (pos >= end || *pos != 0x82)
+               return 0;
+
+       pos += 3 + pos[1];
+       if (pos >= end || *pos != 0x90)
+               return 0;
+
+       pos += 3 + pos[1];
+       if (pos >= end || *pos != 0x78)
+               return 0;
+
+       return 1;
+}
+
+int
+qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size)
+{
+       uint8_t *pos = ha->vpd;
+       uint8_t *end = pos + ha->vpd_size;
+       int len = 0;
+
+       if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end))
+               return 0;
+
+       while (pos < end && *pos != 0x78) {
+               len = (*pos == 0x82) ? pos[1] : pos[2];
+
+               if (!strncmp(pos, key, strlen(key)))
+                       break;
+
+               if (*pos != 0x90 && *pos != 0x91)
+                       pos += len;
+
+               pos += 3;
+       }
+
+       if (pos < end - len && *pos != 0x78)
+               return snprintf(str, size, "%.*s", len, pos + 3);
+
+       return 0;
+}
+
 static int
 qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
 {
index d058c8862b358465b793adedf8ff403269d4962a..676c390db354c18c58d816154be7d41f3c8811c8 100644 (file)
@@ -7,7 +7,7 @@
 /*
  * Driver version
  */
-#define QLA2XXX_VERSION      "8.02.01-k4"
+#define QLA2XXX_VERSION      "8.02.01-k6"
 
 #define QLA_DRIVER_MAJOR_VER   8
 #define QLA_DRIVER_MINOR_VER   2
index 5822dd595826ddd8651078e8232a4461b5dda618..88bebb13bc520ab0d3959426adcaf3f30986a320 100644 (file)
@@ -46,6 +46,8 @@ MODULE_PARM_DESC(ql4xextended_error_logging,
 
 int ql4_mod_unload = 0;
 
+#define QL4_DEF_QDEPTH 32
+
 /*
  * SCSI host template entry points
  */
@@ -1387,7 +1389,7 @@ static int qla4xxx_slave_alloc(struct scsi_device *sdev)
 
        sdev->hostdata = ddb;
        sdev->tagged_supported = 1;
-       scsi_activate_tcq(sdev, sdev->host->can_queue);
+       scsi_activate_tcq(sdev, QL4_DEF_QDEPTH);
        return 0;
 }
 
index 36c92f961e154fb46b94690b36dd09d624e59666..ee6be596503d1515dc855e6e5715267ac4edb4e1 100644 (file)
@@ -197,10 +197,42 @@ static void
 scsi_pool_free_command(struct scsi_host_cmd_pool *pool,
                         struct scsi_cmnd *cmd)
 {
+       if (cmd->prot_sdb)
+               kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb);
+
        kmem_cache_free(pool->sense_slab, cmd->sense_buffer);
        kmem_cache_free(pool->cmd_slab, cmd);
 }
 
+/**
+ * scsi_host_alloc_command - internal function to allocate command
+ * @shost:     SCSI host whose pool to allocate from
+ * @gfp_mask:  mask for the allocation
+ *
+ * Returns a fully allocated command with sense buffer and protection
+ * data buffer (where applicable) or NULL on failure
+ */
+static struct scsi_cmnd *
+scsi_host_alloc_command(struct Scsi_Host *shost, gfp_t gfp_mask)
+{
+       struct scsi_cmnd *cmd;
+
+       cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask);
+       if (!cmd)
+               return NULL;
+
+       if (scsi_host_get_prot(shost) >= SHOST_DIX_TYPE0_PROTECTION) {
+               cmd->prot_sdb = kmem_cache_zalloc(scsi_sdb_cache, gfp_mask);
+
+               if (!cmd->prot_sdb) {
+                       scsi_pool_free_command(shost->cmd_pool, cmd);
+                       return NULL;
+               }
+       }
+
+       return cmd;
+}
+
 /**
  * __scsi_get_command - Allocate a struct scsi_cmnd
  * @shost: host to transmit command
@@ -214,7 +246,7 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask)
        struct scsi_cmnd *cmd;
        unsigned char *buf;
 
-       cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask);
+       cmd = scsi_host_alloc_command(shost, gfp_mask);
 
        if (unlikely(!cmd)) {
                unsigned long flags;
@@ -457,7 +489,7 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost)
        /*
         * Get one backup command for this host.
         */
-       cmd = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask);
+       cmd = scsi_host_alloc_command(shost, gfp_mask);
        if (!cmd) {
                scsi_put_host_cmd_pool(gfp_mask);
                shost->cmd_pool = NULL;
@@ -902,11 +934,20 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags)
 
        spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
 
-       /* Check to see if the queue is managed by the block layer.
-        * If it is, and we fail to adjust the depth, exit. */
-       if (blk_queue_tagged(sdev->request_queue) &&
-           blk_queue_resize_tags(sdev->request_queue, tags) != 0)
-               goto out;
+       /*
+        * Check to see if the queue is managed by the block layer.
+        * If it is, and we fail to adjust the depth, exit.
+        *
+        * Do not resize the tag map if it is a host wide share bqt,
+        * because the size should be the hosts's can_queue. If there
+        * is more IO than the LLD's can_queue (so there are not enuogh
+        * tags) request_fn's host queue ready check will handle it.
+        */
+       if (!sdev->host->bqt) {
+               if (blk_queue_tagged(sdev->request_queue) &&
+                   blk_queue_resize_tags(sdev->request_queue, tags) != 0)
+                       goto out;
+       }
 
        sdev->queue_depth = tags;
        switch (tagged) {
index 01d11a01ffbf7f5925235cdcf8cf68f8ce6cb76a..27c633f557945f8cfb99ce433bcf53ca774f2df8 100644 (file)
@@ -1753,7 +1753,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
                open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC);
                if (!open_devip) {
                        printk(KERN_ERR "%s: out of memory at line %d\n",
-                               __FUNCTION__, __LINE__);
+                               __func__, __LINE__);
                        return NULL;
                }
        }
@@ -2656,7 +2656,7 @@ static int sdebug_add_adapter(void)
         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
         if (NULL == sdbg_host) {
                 printk(KERN_ERR "%s: out of memory at line %d\n",
-                       __FUNCTION__, __LINE__);
+                       __func__, __LINE__);
                 return -ENOMEM;
         }
 
@@ -2667,7 +2667,7 @@ static int sdebug_add_adapter(void)
                sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL);
                if (!sdbg_devinfo) {
                         printk(KERN_ERR "%s: out of memory at line %d\n",
-                               __FUNCTION__, __LINE__);
+                               __func__, __LINE__);
                         error = -ENOMEM;
                        goto clean;
                 }
@@ -2987,7 +2987,7 @@ static int sdebug_driver_probe(struct device * dev)
 
         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
         if (NULL == hpnt) {
-                printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
+                printk(KERN_ERR "%s: scsi_register failed\n", __func__);
                 error = -ENODEV;
                return error;
         }
@@ -3002,7 +3002,7 @@ static int sdebug_driver_probe(struct device * dev)
 
         error = scsi_add_host(hpnt, &sdbg_host->dev);
         if (error) {
-                printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
+                printk(KERN_ERR "%s: scsi_add_host failed\n", __func__);
                 error = -ENODEV;
                scsi_host_put(hpnt);
         } else
@@ -3021,7 +3021,7 @@ static int sdebug_driver_remove(struct device * dev)
 
        if (!sdbg_host) {
                printk(KERN_ERR "%s: Unable to locate host info\n",
-                      __FUNCTION__);
+                      __func__);
                return -ENODEV;
        }
 
index a235802f298150e648eb5bdf3f054dae4b1721c8..4969e4ec75ea9f7b985c6c979763d8eae40bcb93 100644 (file)
@@ -272,7 +272,7 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length,
        }
        if (from_length > to_length)
                 printk(KERN_WARNING "%s: %s string '%s' is too long\n",
-                       __FUNCTION__, name, from);
+                       __func__, name, from);
 }
 
 /**
@@ -298,7 +298,7 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model,
 
        devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL);
        if (!devinfo) {
-               printk(KERN_ERR "%s: no memory\n", __FUNCTION__);
+               printk(KERN_ERR "%s: no memory\n", __func__);
                return -ENOMEM;
        }
 
@@ -363,7 +363,7 @@ static int scsi_dev_info_list_add_str(char *dev_list)
                        strflags = strsep(&next, next_check);
                if (!model || !strflags) {
                        printk(KERN_ERR "%s: bad dev info string '%s' '%s'"
-                              " '%s'\n", __FUNCTION__, vendor, model,
+                              " '%s'\n", __func__, vendor, model,
                               strflags);
                        res = -EINVAL;
                } else
index 006a95916f72ae4aff8cae52db9122a9d5e4bf9c..880051c89bde082320f7379b39adeb4e7823c12d 100644 (file)
@@ -139,7 +139,7 @@ void scsi_add_timer(struct scsi_cmnd *scmd, int timeout,
        scmd->eh_timeout.function = (void (*)(unsigned long)) complete;
 
        SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:"
-                                         " %d, (%p)\n", __FUNCTION__,
+                                         " %d, (%p)\n", __func__,
                                          scmd, timeout, complete));
 
        add_timer(&scmd->eh_timeout);
@@ -163,7 +163,7 @@ int scsi_delete_timer(struct scsi_cmnd *scmd)
        rtn = del_timer(&scmd->eh_timeout);
 
        SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p,"
-                                        " rtn: %d\n", __FUNCTION__,
+                                        " rtn: %d\n", __func__,
                                         scmd, rtn));
 
        scmd->eh_timeout.data = (unsigned long)NULL;
@@ -233,7 +233,7 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev)
 
        online = scsi_device_online(sdev);
 
-       SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__,
+       SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __func__,
                                          online));
 
        return online;
@@ -271,7 +271,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
                        SCSI_LOG_ERROR_RECOVERY(3,
                                sdev_printk(KERN_INFO, sdev,
                                            "%s: cmds failed: %d, cancel: %d\n",
-                                           __FUNCTION__, cmd_failed,
+                                           __func__, cmd_failed,
                                            cmd_cancel));
                        cmd_cancel = 0;
                        cmd_failed = 0;
@@ -344,6 +344,9 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
                return /* soft_error */ SUCCESS;
 
        case ABORTED_COMMAND:
+               if (sshdr.asc == 0x10) /* DIF */
+                       return SUCCESS;
+
                return NEEDS_RETRY;
        case NOT_READY:
        case UNIT_ATTENTION:
@@ -470,7 +473,7 @@ static void scsi_eh_done(struct scsi_cmnd *scmd)
 
        SCSI_LOG_ERROR_RECOVERY(3,
                printk("%s scmd: %p result: %x\n",
-                       __FUNCTION__, scmd, scmd->result));
+                       __func__, scmd, scmd->result));
 
        eh_action = scmd->device->host->eh_action;
        if (eh_action)
@@ -487,7 +490,7 @@ static int scsi_try_host_reset(struct scsi_cmnd *scmd)
        int rtn;
 
        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
-                                         __FUNCTION__));
+                                         __func__));
 
        if (!scmd->device->host->hostt->eh_host_reset_handler)
                return FAILED;
@@ -516,7 +519,7 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
        int rtn;
 
        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
-                                         __FUNCTION__));
+                                         __func__));
 
        if (!scmd->device->host->hostt->eh_bus_reset_handler)
                return FAILED;
@@ -664,7 +667,10 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
        ses->sdb = scmd->sdb;
        ses->next_rq = scmd->request->next_rq;
        ses->result = scmd->result;
+       ses->underflow = scmd->underflow;
+       ses->prot_op = scmd->prot_op;
 
+       scmd->prot_op = SCSI_PROT_NORMAL;
        scmd->cmnd = ses->eh_cmnd;
        memset(scmd->cmnd, 0, BLK_MAX_CDB);
        memset(&scmd->sdb, 0, sizeof(scmd->sdb));
@@ -722,6 +728,8 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
        scmd->sdb = ses->sdb;
        scmd->request->next_rq = ses->next_rq;
        scmd->result = ses->result;
+       scmd->underflow = ses->underflow;
+       scmd->prot_op = ses->prot_op;
 }
 EXPORT_SYMBOL(scsi_eh_restore_cmnd);
 
@@ -766,7 +774,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
 
        SCSI_LOG_ERROR_RECOVERY(3,
                printk("%s: scmd: %p, timeleft: %ld\n",
-                       __FUNCTION__, scmd, timeleft));
+                       __func__, scmd, timeleft));
 
        /*
         * If there is time left scsi_eh_done got called, and we will
@@ -778,7 +786,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
                rtn = scsi_eh_completed_normally(scmd);
                SCSI_LOG_ERROR_RECOVERY(3,
                        printk("%s: scsi_eh_completed_normally %x\n",
-                              __FUNCTION__, rtn));
+                              __func__, rtn));
 
                switch (rtn) {
                case SUCCESS:
@@ -913,7 +921,7 @@ retry_tur:
        rtn = scsi_send_eh_cmnd(scmd, tur_command, 6, SENSE_TIMEOUT, 0);
 
        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n",
-               __FUNCTION__, scmd, rtn));
+               __func__, scmd, rtn));
 
        switch (rtn) {
        case NEEDS_RETRY:
@@ -1296,7 +1304,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
        if (!scsi_device_online(scmd->device)) {
                SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report"
                                                  " as SUCCESS\n",
-                                                 __FUNCTION__));
+                                                 __func__));
                return SUCCESS;
        }
 
@@ -1511,7 +1519,7 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
         * ioctls to queued block devices.
         */
        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n",
-                                         __FUNCTION__));
+                                         __func__));
 
        spin_lock_irqsave(shost->host_lock, flags);
        if (scsi_host_set_state(shost, SHOST_RUNNING))
@@ -1835,7 +1843,7 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
         */
        SCSI_LOG_ERROR_RECOVERY(3,
                printk("%s: waking up host to restart after TMF\n",
-               __FUNCTION__));
+               __func__));
 
        wake_up(&shost->host_wait);
 
index 88d1b5f44e59f2847bfca483a035b686e57de051..ff5d56b3ee4d5c66a6ee4ab34609fc1800cd347b 100644 (file)
@@ -65,7 +65,7 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = {
 };
 #undef SP
 
-static struct kmem_cache *scsi_sdb_cache;
+struct kmem_cache *scsi_sdb_cache;
 
 static void scsi_run_queue(struct request_queue *q);
 
@@ -787,6 +787,9 @@ void scsi_release_buffers(struct scsi_cmnd *cmd)
                kmem_cache_free(scsi_sdb_cache, bidi_sdb);
                cmd->request->next_rq->special = NULL;
        }
+
+       if (scsi_prot_sg_count(cmd))
+               scsi_free_sgtable(cmd->prot_sdb);
 }
 EXPORT_SYMBOL(scsi_release_buffers);
 
@@ -947,9 +950,14 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
                                 * 6-byte command.
                                 */
                                scsi_requeue_command(q, cmd);
-                               return;
-                       } else {
+                       } else if (sshdr.asc == 0x10) /* DIX */
+                               scsi_end_request(cmd, -EIO, this_count, 0);
+                       else
                                scsi_end_request(cmd, -EIO, this_count, 1);
+                       return;
+               case ABORTED_COMMAND:
+                       if (sshdr.asc == 0x10) { /* DIF */
+                               scsi_end_request(cmd, -EIO, this_count, 0);
                                return;
                        }
                        break;
@@ -1072,6 +1080,26 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
                        goto err_exit;
        }
 
+       if (blk_integrity_rq(cmd->request)) {
+               struct scsi_data_buffer *prot_sdb = cmd->prot_sdb;
+               int ivecs, count;
+
+               BUG_ON(prot_sdb == NULL);
+               ivecs = blk_rq_count_integrity_sg(cmd->request);
+
+               if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) {
+                       error = BLKPREP_DEFER;
+                       goto err_exit;
+               }
+
+               count = blk_rq_map_integrity_sg(cmd->request,
+                                               prot_sdb->table.sgl);
+               BUG_ON(unlikely(count > ivecs));
+
+               cmd->prot_sdb = prot_sdb;
+               cmd->prot_sdb->table.nents = count;
+       }
+
        return BLKPREP_OK ;
 
 err_exit:
@@ -1367,7 +1395,7 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
 
        if (unlikely(cmd == NULL)) {
                printk(KERN_CRIT "impossible request in %s.\n",
-                                __FUNCTION__);
+                                __func__);
                BUG();
        }
 
@@ -1491,12 +1519,27 @@ static void scsi_request_fn(struct request_queue *q)
                        printk(KERN_CRIT "impossible request in %s.\n"
                                         "please mail a stack trace to "
                                         "linux-scsi@vger.kernel.org\n",
-                                        __FUNCTION__);
+                                        __func__);
                        blk_dump_rq_flags(req, "foo");
                        BUG();
                }
                spin_lock(shost->host_lock);
 
+               /*
+                * We hit this when the driver is using a host wide
+                * tag map. For device level tag maps the queue_depth check
+                * in the device ready fn would prevent us from trying
+                * to allocate a tag. Since the map is a shared host resource
+                * we add the dev to the starved list so it eventually gets
+                * a run when a tag is freed.
+                */
+               if (blk_queue_tagged(q) && !blk_rq_tagged(req)) {
+                       if (list_empty(&sdev->starved_entry))
+                               list_add_tail(&sdev->starved_entry,
+                                             &shost->starved_list);
+                       goto not_ready;
+               }
+
                if (!scsi_host_queue_ready(q, shost, sdev))
                        goto not_ready;
                if (scsi_target(sdev)->single_lun) {
@@ -2486,7 +2529,7 @@ void *scsi_kmap_atomic_sg(struct scatterlist *sgl, int sg_count,
        if (unlikely(i == sg_count)) {
                printk(KERN_ERR "%s: Bytes in sg: %zu, requested offset %zu, "
                        "elements %d\n",
-                      __FUNCTION__, sg_len, *offset, sg_count);
+                      __func__, sg_len, *offset, sg_count);
                WARN_ON(1);
                return NULL;
        }
index 370c78cc1cb562f20af68017489a78b6650374a0..ae7ed9a226623db41e1354a12c6f97d5d83bc324 100644 (file)
@@ -55,7 +55,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
                if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
                    (skb->len < nlh->nlmsg_len)) {
                        printk(KERN_WARNING "%s: discarding partial skb\n",
-                                __FUNCTION__);
+                                __func__);
                        return;
                }
 
@@ -82,7 +82,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
 
                if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
                        printk(KERN_WARNING "%s: discarding partial message\n",
-                                __FUNCTION__);
+                                __func__);
                        return;
                }
 
@@ -139,7 +139,7 @@ scsi_netlink_init(void)
        error = netlink_register_notifier(&scsi_netlink_notifier);
        if (error) {
                printk(KERN_ERR "%s: register of event handler failed - %d\n",
-                               __FUNCTION__, error);
+                               __func__, error);
                return;
        }
 
@@ -148,7 +148,7 @@ scsi_netlink_init(void)
                                THIS_MODULE);
        if (!scsi_nl_sock) {
                printk(KERN_ERR "%s: register of recieve handler failed\n",
-                               __FUNCTION__);
+                               __func__);
                netlink_unregister_notifier(&scsi_netlink_notifier);
        }
 
index b33e72516ef83ee685a5186e0894b0585faba4fd..79f0f7511204594186a36bae6e62acb4c3cd1a9c 100644 (file)
@@ -77,6 +77,7 @@ extern void scsi_exit_queue(void);
 struct request_queue;
 struct request;
 extern int scsi_prep_fn(struct request_queue *, struct request *);
+extern struct kmem_cache *scsi_sdb_cache;
 
 /* scsi_proc.c */
 #ifdef CONFIG_SCSI_PROC_FS
index e4a0d2f9b35752a47c2e84b7e99dafa67fce04f7..c6a904a45bf9871677e50ea762ceaf3c468ae049 100644 (file)
@@ -114,7 +114,7 @@ void scsi_proc_hostdir_add(struct scsi_host_template *sht)
                sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
                if (!sht->proc_dir)
                        printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
-                              __FUNCTION__, sht->proc_name);
+                              __func__, sht->proc_name);
                else
                        sht->proc_dir->owner = sht->module;
        }
@@ -157,7 +157,7 @@ void scsi_proc_host_add(struct Scsi_Host *shost)
                        sht->proc_dir, proc_scsi_read, shost);
        if (!p) {
                printk(KERN_ERR "%s: Failed to register host %d in"
-                      "%s\n", __FUNCTION__, shost->host_no,
+                      "%s\n", __func__, shost->host_no,
                       sht->proc_name);
                return;
        } 
index 196fe3af0d5efaab057619c723d54d2b3d76429c..84b4879cff114f8a6e709deed6e18c67966d9140 100644 (file)
@@ -318,7 +318,7 @@ out_device_destroy:
        put_device(&sdev->sdev_gendev);
 out:
        if (display_failure_msg)
-               printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+               printk(ALLOC_FAILURE_MSG, __func__);
        return NULL;
 }
 
@@ -404,7 +404,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
 
        starget = kzalloc(size, GFP_KERNEL);
        if (!starget) {
-               printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
+               printk(KERN_ERR "%s: allocation failure\n", __func__);
                return NULL;
        }
        dev = &starget->dev;
@@ -1337,7 +1337,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
        lun_data = kmalloc(length, GFP_ATOMIC |
                           (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0));
        if (!lun_data) {
-               printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+               printk(ALLOC_FAILURE_MSG, __func__);
                goto out;
        }
 
@@ -1649,7 +1649,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
 {
        SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost,
                "%s: <%u:%u:%u>\n",
-               __FUNCTION__, channel, id, lun));
+               __func__, channel, id, lun));
 
        if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
            ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
@@ -1703,7 +1703,7 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost)
                return NULL;
 
        if (shost->async_scan) {
-               printk("%s called twice for host %d", __FUNCTION__,
+               printk("%s called twice for host %d", __func__,
                                shost->host_no);
                dump_stack();
                return NULL;
@@ -1757,9 +1757,10 @@ static void scsi_finish_async_scan(struct async_scan_data *data)
        mutex_lock(&shost->scan_mutex);
 
        if (!shost->async_scan) {
-               printk("%s called twice for host %d", __FUNCTION__,
+               printk("%s called twice for host %d", __func__,
                                shost->host_no);
                dump_stack();
+               mutex_unlock(&shost->scan_mutex);
                return;
        }
 
index b6e5610597794c553a011231f9a097785e529bf9..ab3c71869be55d9deeb8ca101c59faf80e140561 100644 (file)
@@ -249,6 +249,8 @@ shost_rd_attr(cmd_per_lun, "%hd\n");
 shost_rd_attr(can_queue, "%hd\n");
 shost_rd_attr(sg_tablesize, "%hu\n");
 shost_rd_attr(unchecked_isa_dma, "%d\n");
+shost_rd_attr(prot_capabilities, "%u\n");
+shost_rd_attr(prot_guard_type, "%hd\n");
 shost_rd_attr2(proc_name, hostt->proc_name, "%s\n");
 
 static struct attribute *scsi_sysfs_shost_attrs[] = {
@@ -263,6 +265,8 @@ static struct attribute *scsi_sysfs_shost_attrs[] = {
        &dev_attr_hstate.attr,
        &dev_attr_supported_mode.attr,
        &dev_attr_active_mode.attr,
+       &dev_attr_prot_capabilities.attr,
+       &dev_attr_prot_guard_type.attr,
        NULL
 };
 
index cb92888948f9a04756a19bf994e389c02c03a350..fe4c62177f78f8394b3e1c230a5b4766c53db869 100644 (file)
@@ -6,7 +6,7 @@ struct task_struct;
 /* tmp - will replace with SCSI logging stuff */
 #define eprintk(fmt, args...)                                  \
 do {                                                           \
-       printk("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);  \
+       printk("%s(%d) " fmt, __func__, __LINE__, ##args);      \
 } while (0)
 
 #define dprintk(fmt, args...)
index a272b9a2c86945314a8ae4693cc18b2a37dce2cb..56823fd1fb8418811da8fc4418ec271b846b65da 100644 (file)
@@ -571,7 +571,7 @@ send_fail:
        name = get_fc_host_event_code_name(event_code);
        printk(KERN_WARNING
                "%s: Dropped Event : host %d %s data 0x%08x - err %d\n",
-               __FUNCTION__, shost->host_no,
+               __func__, shost->host_no,
                (name) ? name : "<unknown>", event_data, err);
        return;
 }
@@ -644,7 +644,7 @@ send_vendor_fail_skb:
 send_vendor_fail:
        printk(KERN_WARNING
                "%s: Dropped Event : host %d vendor_unique - err %d\n",
-               __FUNCTION__, shost->host_no, err);
+               __func__, shost->host_no, err);
        return;
 }
 EXPORT_SYMBOL(fc_host_post_vendor_event);
@@ -2464,7 +2464,7 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
        size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size);
        rport = kzalloc(size, GFP_KERNEL);
        if (unlikely(!rport)) {
-               printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
+               printk(KERN_ERR "%s: allocation failure\n", __func__);
                return NULL;
        }
 
@@ -3137,7 +3137,7 @@ fc_vport_create(struct Scsi_Host *shost, int channel, struct device *pdev,
        size = (sizeof(struct fc_vport) + fci->f->dd_fcvport_size);
        vport = kzalloc(size, GFP_KERNEL);
        if (unlikely(!vport)) {
-               printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
+               printk(KERN_ERR "%s: allocation failure\n", __func__);
                return -ENOMEM;
        }
 
@@ -3201,7 +3201,7 @@ fc_vport_create(struct Scsi_Host *shost, int channel, struct device *pdev,
                        printk(KERN_ERR
                                "%s: Cannot create vport symlinks for "
                                "%s, err=%d\n",
-                               __FUNCTION__, dev->bus_id, error);
+                               __func__, dev->bus_id, error);
        }
        spin_lock_irqsave(shost->host_lock, flags);
        vport->flags &= ~FC_VPORT_CREATING;
@@ -3314,7 +3314,7 @@ fc_vport_sched_delete(struct work_struct *work)
        if (stat)
                dev_printk(KERN_ERR, vport->dev.parent,
                        "%s: %s could not be deleted created via "
-                       "shost%d channel %d - error %d\n", __FUNCTION__,
+                       "shost%d channel %d - error %d\n", __func__,
                        vport->dev.bus_id, vport->shost->host_no,
                        vport->channel, stat);
 }
index f4461d35ffb9157d6cc3e634f692ef0937b5a9c5..366609386be108341875da60d0f58ac02583d6d0 100644 (file)
@@ -779,7 +779,7 @@ static void sas_port_create_link(struct sas_port *port,
        return;
 err:
        printk(KERN_ERR "%s: Cannot create port links, err=%d\n",
-              __FUNCTION__, res);
+              __func__, res);
 }
 
 static void sas_port_delete_link(struct sas_port *port,
@@ -1029,7 +1029,7 @@ void sas_port_mark_backlink(struct sas_port *port)
        return;
 err:
        printk(KERN_ERR "%s: Cannot create port backlink, err=%d\n",
-              __FUNCTION__, res);
+              __func__, res);
 
 }
 EXPORT_SYMBOL(sas_port_mark_backlink);
index 0c63947d8a9d8fce2b4fa6240d9847036289f3b2..e5e7d78564545543925fc6136c6e7c7fc994c028 100644 (file)
@@ -99,8 +99,7 @@ static void scsi_disk_release(struct device *cdev);
 static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
 static void sd_print_result(struct scsi_disk *, int);
 
-static DEFINE_IDR(sd_index_idr);
-static DEFINE_SPINLOCK(sd_index_lock);
+static DEFINE_IDA(sd_index_ida);
 
 /* This semaphore is used to mediate the 0->1 reference get in the
  * face of object destruction (i.e. we can't allow a get on an
@@ -234,6 +233,24 @@ sd_show_allow_restart(struct device *dev, struct device_attribute *attr,
        return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart);
 }
 
+static ssize_t
+sd_show_protection_type(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+       return snprintf(buf, 20, "%u\n", sdkp->protection_type);
+}
+
+static ssize_t
+sd_show_app_tag_own(struct device *dev, struct device_attribute *attr,
+                   char *buf)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+       return snprintf(buf, 20, "%u\n", sdkp->ATO);
+}
+
 static struct device_attribute sd_disk_attrs[] = {
        __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type,
               sd_store_cache_type),
@@ -242,6 +259,8 @@ static struct device_attribute sd_disk_attrs[] = {
               sd_store_allow_restart),
        __ATTR(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop,
               sd_store_manage_start_stop),
+       __ATTR(protection_type, S_IRUGO, sd_show_protection_type, NULL),
+       __ATTR(app_tag_own, S_IRUGO, sd_show_app_tag_own, NULL),
        __ATTR_NULL,
 };
 
@@ -354,7 +373,9 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        struct scsi_cmnd *SCpnt;
        struct scsi_device *sdp = q->queuedata;
        struct gendisk *disk = rq->rq_disk;
+       struct scsi_disk *sdkp;
        sector_t block = rq->sector;
+       sector_t threshold;
        unsigned int this_count = rq->nr_sectors;
        unsigned int timeout = sdp->timeout;
        int ret;
@@ -370,6 +391,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        if (ret != BLKPREP_OK)
                goto out;
        SCpnt = rq->special;
+       sdkp = scsi_disk(disk);
 
        /* from here on until we're complete, any goto out
         * is used for a killable error condition */
@@ -401,13 +423,21 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        }
 
        /*
-        * Some devices (some sdcards for one) don't like it if the
-        * last sector gets read in a larger then 1 sector read.
+        * Some SD card readers can't handle multi-sector accesses which touch
+        * the last one or two hardware sectors.  Split accesses as needed.
         */
-       if (unlikely(sdp->last_sector_bug &&
-           rq->nr_sectors > sdp->sector_size / 512 &&
-           block + this_count == get_capacity(disk)))
-               this_count -= sdp->sector_size / 512;
+       threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS *
+               (sdp->sector_size / 512);
+
+       if (unlikely(sdp->last_sector_bug && block + this_count > threshold)) {
+               if (block < threshold) {
+                       /* Access up to the threshold but not beyond */
+                       this_count = threshold - block;
+               } else {
+                       /* Access only a single hardware sector */
+                       this_count = sdp->sector_size / 512;
+               }
+       }
 
        SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
                                        (unsigned long long)block));
@@ -459,6 +489,11 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                }
                SCpnt->cmnd[0] = WRITE_6;
                SCpnt->sc_data_direction = DMA_TO_DEVICE;
+
+               if (blk_integrity_rq(rq) &&
+                   sd_dif_prepare(rq, block, sdp->sector_size) == -EIO)
+                       goto out;
+
        } else if (rq_data_dir(rq) == READ) {
                SCpnt->cmnd[0] = READ_6;
                SCpnt->sc_data_direction = DMA_FROM_DEVICE;
@@ -473,8 +508,12 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                                        "writing" : "reading", this_count,
                                        rq->nr_sectors));
 
-       SCpnt->cmnd[1] = 0;
-       
+       /* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */
+       if (scsi_host_dif_capable(sdp->host, sdkp->protection_type))
+               SCpnt->cmnd[1] = 1 << 5;
+       else
+               SCpnt->cmnd[1] = 0;
+
        if (block > 0xffffffff) {
                SCpnt->cmnd[0] += READ_16 - READ_6;
                SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0;
@@ -492,6 +531,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
                SCpnt->cmnd[13] = (unsigned char) this_count & 0xff;
                SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0;
        } else if ((this_count > 0xff) || (block > 0x1fffff) ||
+                  scsi_device_protection(SCpnt->device) ||
                   SCpnt->device->use_10_for_rw) {
                if (this_count > 0xffff)
                        this_count = 0xffff;
@@ -526,6 +566,10 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        }
        SCpnt->sdb.length = this_count * sdp->sector_size;
 
+       /* If DIF or DIX is enabled, tell HBA how to handle request */
+       if (sdkp->protection_type || scsi_prot_sg_count(SCpnt))
+               sd_dif_op(SCpnt, sdkp->protection_type, scsi_prot_sg_count(SCpnt));
+
        /*
         * We shouldn't disconnect in the middle of a sector, so with a dumb
         * host adapter, it's safe to assume that we can at least transfer
@@ -920,6 +964,48 @@ static struct block_device_operations sd_fops = {
        .revalidate_disk        = sd_revalidate_disk,
 };
 
+static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
+{
+       u64 start_lba = scmd->request->sector;
+       u64 end_lba = scmd->request->sector + (scsi_bufflen(scmd) / 512);
+       u64 bad_lba;
+       int info_valid;
+
+       if (!blk_fs_request(scmd->request))
+               return 0;
+
+       info_valid = scsi_get_sense_info_fld(scmd->sense_buffer,
+                                            SCSI_SENSE_BUFFERSIZE,
+                                            &bad_lba);
+       if (!info_valid)
+               return 0;
+
+       if (scsi_bufflen(scmd) <= scmd->device->sector_size)
+               return 0;
+
+       if (scmd->device->sector_size < 512) {
+               /* only legitimate sector_size here is 256 */
+               start_lba <<= 1;
+               end_lba <<= 1;
+       } else {
+               /* be careful ... don't want any overflows */
+               u64 factor = scmd->device->sector_size / 512;
+               do_div(start_lba, factor);
+               do_div(end_lba, factor);
+       }
+
+       /* The bad lba was reported incorrectly, we have no idea where
+        * the error is.
+        */
+       if (bad_lba < start_lba  || bad_lba >= end_lba)
+               return 0;
+
+       /* This computation should always be done in terms of
+        * the resolution of the device's medium.
+        */
+       return (bad_lba - start_lba) * scmd->device->sector_size;
+}
+
 /**
  *     sd_done - bottom half handler: called when the lower level
  *     driver has completed (successfully or otherwise) a scsi command.
@@ -930,15 +1016,10 @@ static struct block_device_operations sd_fops = {
 static int sd_done(struct scsi_cmnd *SCpnt)
 {
        int result = SCpnt->result;
-       unsigned int xfer_size = scsi_bufflen(SCpnt);
-       unsigned int good_bytes = result ? 0 : xfer_size;
-       u64 start_lba = SCpnt->request->sector;
-       u64 end_lba = SCpnt->request->sector + (xfer_size / 512);
-       u64 bad_lba;
+       unsigned int good_bytes = result ? 0 : scsi_bufflen(SCpnt);
        struct scsi_sense_hdr sshdr;
        int sense_valid = 0;
        int sense_deferred = 0;
-       int info_valid;
 
        if (result) {
                sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr);
@@ -963,36 +1044,7 @@ static int sd_done(struct scsi_cmnd *SCpnt)
        switch (sshdr.sense_key) {
        case HARDWARE_ERROR:
        case MEDIUM_ERROR:
-               if (!blk_fs_request(SCpnt->request))
-                       goto out;
-               info_valid = scsi_get_sense_info_fld(SCpnt->sense_buffer,
-                                                    SCSI_SENSE_BUFFERSIZE,
-                                                    &bad_lba);
-               if (!info_valid)
-                       goto out;
-               if (xfer_size <= SCpnt->device->sector_size)
-                       goto out;
-               if (SCpnt->device->sector_size < 512) {
-                       /* only legitimate sector_size here is 256 */
-                       start_lba <<= 1;
-                       end_lba <<= 1;
-               } else {
-                       /* be careful ... don't want any overflows */
-                       u64 factor = SCpnt->device->sector_size / 512;
-                       do_div(start_lba, factor);
-                       do_div(end_lba, factor);
-               }
-
-               if (bad_lba < start_lba  || bad_lba >= end_lba)
-                       /* the bad lba was reported incorrectly, we have
-                        * no idea where the error is
-                        */
-                       goto out;
-
-               /* This computation should always be done in terms of
-                * the resolution of the device's medium.
-                */
-               good_bytes = (bad_lba - start_lba)*SCpnt->device->sector_size;
+               good_bytes = sd_completed_bytes(SCpnt);
                break;
        case RECOVERED_ERROR:
        case NO_SENSE:
@@ -1002,10 +1054,23 @@ static int sd_done(struct scsi_cmnd *SCpnt)
                scsi_print_sense("sd", SCpnt);
                SCpnt->result = 0;
                memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-               good_bytes = xfer_size;
+               good_bytes = scsi_bufflen(SCpnt);
+               break;
+       case ABORTED_COMMAND:
+               if (sshdr.asc == 0x10) { /* DIF: Disk detected corruption */
+                       scsi_print_result(SCpnt);
+                       scsi_print_sense("sd", SCpnt);
+                       good_bytes = sd_completed_bytes(SCpnt);
+               }
                break;
        case ILLEGAL_REQUEST:
-               if (SCpnt->device->use_10_for_rw &&
+               if (sshdr.asc == 0x10) { /* DIX: HBA detected corruption */
+                       scsi_print_result(SCpnt);
+                       scsi_print_sense("sd", SCpnt);
+                       good_bytes = sd_completed_bytes(SCpnt);
+               }
+               if (!scsi_device_protection(SCpnt->device) &&
+                   SCpnt->device->use_10_for_rw &&
                    (SCpnt->cmnd[0] == READ_10 ||
                     SCpnt->cmnd[0] == WRITE_10))
                        SCpnt->device->use_10_for_rw = 0;
@@ -1018,6 +1083,9 @@ static int sd_done(struct scsi_cmnd *SCpnt)
                break;
        }
  out:
+       if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt))
+               sd_dif_complete(SCpnt, good_bytes);
+
        return good_bytes;
 }
 
@@ -1165,6 +1233,49 @@ sd_spinup_disk(struct scsi_disk *sdkp)
        }
 }
 
+
+/*
+ * Determine whether disk supports Data Integrity Field.
+ */
+void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+       struct scsi_device *sdp = sdkp->device;
+       u8 type;
+
+       if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0)
+               type = 0;
+       else
+               type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */
+
+       switch (type) {
+       case SD_DIF_TYPE0_PROTECTION:
+               sdkp->protection_type = 0;
+               break;
+
+       case SD_DIF_TYPE1_PROTECTION:
+       case SD_DIF_TYPE3_PROTECTION:
+               sdkp->protection_type = type;
+               break;
+
+       case SD_DIF_TYPE2_PROTECTION:
+               sd_printk(KERN_ERR, sdkp, "formatted with DIF Type 2 "  \
+                         "protection which is currently unsupported. " \
+                         "Disabling disk!\n");
+               goto disable;
+
+       default:
+               sd_printk(KERN_ERR, sdkp, "formatted with unknown "     \
+                         "protection type %d. Disabling disk!\n", type);
+               goto disable;
+       }
+
+       return;
+
+disable:
+       sdkp->protection_type = 0;
+       sdkp->capacity = 0;
+}
+
 /*
  * read disk capacity
  */
@@ -1174,7 +1285,8 @@ sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
        unsigned char cmd[16];
        int the_result, retries;
        int sector_size = 0;
-       int longrc = 0;
+       /* Force READ CAPACITY(16) when PROTECT=1 */
+       int longrc = scsi_device_protection(sdkp->device) ? 1 : 0;
        struct scsi_sense_hdr sshdr;
        int sense_valid = 0;
        struct scsi_device *sdp = sdkp->device;
@@ -1186,8 +1298,8 @@ repeat:
                        memset((void *) cmd, 0, 16);
                        cmd[0] = SERVICE_ACTION_IN;
                        cmd[1] = SAI_READ_CAPACITY_16;
-                       cmd[13] = 12;
-                       memset((void *) buffer, 0, 12);
+                       cmd[13] = 13;
+                       memset((void *) buffer, 0, 13);
                } else {
                        cmd[0] = READ_CAPACITY;
                        memset((void *) &cmd[1], 0, 9);
@@ -1195,7 +1307,7 @@ repeat:
                }
                
                the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
-                                             buffer, longrc ? 12 : 8, &sshdr,
+                                             buffer, longrc ? 13 : 8, &sshdr,
                                              SD_TIMEOUT, SD_MAX_RETRIES);
 
                if (media_not_present(sdkp, &sshdr))
@@ -1270,6 +1382,8 @@ repeat:
                        
                sector_size = (buffer[8] << 24) |
                        (buffer[9] << 16) | (buffer[10] << 8) | buffer[11];
+
+               sd_read_protection_type(sdkp, buffer);
        }       
 
        /* Some devices return the total number of sectors, not the
@@ -1531,6 +1645,52 @@ defaults:
        sdkp->DPOFUA = 0;
 }
 
+/*
+ * The ATO bit indicates whether the DIF application tag is available
+ * for use by the operating system.
+ */
+void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
+{
+       int res, offset;
+       struct scsi_device *sdp = sdkp->device;
+       struct scsi_mode_data data;
+       struct scsi_sense_hdr sshdr;
+
+       if (sdp->type != TYPE_DISK)
+               return;
+
+       if (sdkp->protection_type == 0)
+               return;
+
+       res = scsi_mode_sense(sdp, 1, 0x0a, buffer, 36, SD_TIMEOUT,
+                             SD_MAX_RETRIES, &data, &sshdr);
+
+       if (!scsi_status_is_good(res) || !data.header_length ||
+           data.length < 6) {
+               sd_printk(KERN_WARNING, sdkp,
+                         "getting Control mode page failed, assume no ATO\n");
+
+               if (scsi_sense_valid(&sshdr))
+                       sd_print_sense_hdr(sdkp, &sshdr);
+
+               return;
+       }
+
+       offset = data.header_length + data.block_descriptor_length;
+
+       if ((buffer[offset] & 0x3f) != 0x0a) {
+               sd_printk(KERN_ERR, sdkp, "ATO Got wrong page\n");
+               return;
+       }
+
+       if ((buffer[offset + 5] & 0x80) == 0)
+               return;
+
+       sdkp->ATO = 1;
+
+       return;
+}
+
 /**
  *     sd_revalidate_disk - called the first time a new disk is seen,
  *     performs disk spin up, read_capacity, etc.
@@ -1567,6 +1727,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
        sdkp->write_prot = 0;
        sdkp->WCE = 0;
        sdkp->RCD = 0;
+       sdkp->ATO = 0;
 
        sd_spinup_disk(sdkp);
 
@@ -1578,6 +1739,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
                sd_read_capacity(sdkp, buffer);
                sd_read_write_protect_flag(sdkp, buffer);
                sd_read_cache_type(sdkp, buffer);
+               sd_read_app_tag_own(sdkp, buffer);
        }
 
        /*
@@ -1643,18 +1805,20 @@ static int sd_probe(struct device *dev)
        if (!gd)
                goto out_free;
 
-       if (!idr_pre_get(&sd_index_idr, GFP_KERNEL))
-               goto out_put;
+       do {
+               if (!ida_pre_get(&sd_index_ida, GFP_KERNEL))
+                       goto out_put;
 
-       spin_lock(&sd_index_lock);
-       error = idr_get_new(&sd_index_idr, NULL, &index);
-       spin_unlock(&sd_index_lock);
+               error = ida_get_new(&sd_index_ida, &index);
+       } while (error == -EAGAIN);
 
-       if (index >= SD_MAX_DISKS)
-               error = -EBUSY;
        if (error)
                goto out_put;
 
+       error = -EBUSY;
+       if (index >= SD_MAX_DISKS)
+               goto out_free_index;
+
        sdkp->device = sdp;
        sdkp->driver = &sd_template;
        sdkp->disk = gd;
@@ -1675,7 +1839,7 @@ static int sd_probe(struct device *dev)
        strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE);
 
        if (device_add(&sdkp->dev))
-               goto out_put;
+               goto out_free_index;
 
        get_device(&sdp->sdev_gendev);
 
@@ -1711,12 +1875,15 @@ static int sd_probe(struct device *dev)
 
        dev_set_drvdata(dev, sdkp);
        add_disk(gd);
+       sd_dif_config_host(sdkp);
 
        sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
                  sdp->removable ? "removable " : "");
 
        return 0;
 
+ out_free_index:
+       ida_remove(&sd_index_ida, index);
  out_put:
        put_disk(gd);
  out_free:
@@ -1766,9 +1933,7 @@ static void scsi_disk_release(struct device *dev)
        struct scsi_disk *sdkp = to_scsi_disk(dev);
        struct gendisk *disk = sdkp->disk;
        
-       spin_lock(&sd_index_lock);
-       idr_remove(&sd_index_idr, sdkp->index);
-       spin_unlock(&sd_index_lock);
+       ida_remove(&sd_index_ida, sdkp->index);
 
        disk->private_data = NULL;
        put_disk(disk);
index 03a3d45cfa4237c26a545c209e1e2c8e30a1903c..95b9f06534d579bf72797c20ecd5546970f4b190 100644 (file)
  */
 #define SD_BUF_SIZE            512
 
+/*
+ * Number of sectors at the end of the device to avoid multi-sector
+ * accesses to in the case of last_sector_bug
+ */
+#define SD_LAST_BUGGY_SECTORS  8
+
 struct scsi_disk {
        struct scsi_driver *driver;     /* always &sd_template */
        struct scsi_device *device;
@@ -41,7 +47,9 @@ struct scsi_disk {
        u32             index;
        u8              media_present;
        u8              write_prot;
+       u8              protection_type;/* Data Integrity Field */
        unsigned        previous_state : 1;
+       unsigned        ATO : 1;        /* state of disk ATO bit */
        unsigned        WCE : 1;        /* state of disk WCE bit */
        unsigned        RCD : 1;        /* state of disk RCD bit, unused */
        unsigned        DPOFUA : 1;     /* state of disk DPOFUA bit */
@@ -59,4 +67,50 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
                    (sdsk)->disk->disk_name, ##a) :                     \
        sdev_printk(prefix, (sdsk)->device, fmt, ##a)
 
+/*
+ * A DIF-capable target device can be formatted with different
+ * protection schemes.  Currently 0 through 3 are defined:
+ *
+ * Type 0 is regular (unprotected) I/O
+ *
+ * Type 1 defines the contents of the guard and reference tags
+ *
+ * Type 2 defines the contents of the guard and reference tags and
+ * uses 32-byte commands to seed the latter
+ *
+ * Type 3 defines the contents of the guard tag only
+ */
+
+enum sd_dif_target_protection_types {
+       SD_DIF_TYPE0_PROTECTION = 0x0,
+       SD_DIF_TYPE1_PROTECTION = 0x1,
+       SD_DIF_TYPE2_PROTECTION = 0x2,
+       SD_DIF_TYPE3_PROTECTION = 0x3,
+};
+
+/*
+ * Data Integrity Field tuple.
+ */
+struct sd_dif_tuple {
+       __be16 guard_tag;       /* Checksum */
+       __be16 app_tag;         /* Opaque storage */
+       __be32 ref_tag;         /* Target LBA or indirect LBA */
+};
+
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+
+extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int);
+extern void sd_dif_config_host(struct scsi_disk *);
+extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int);
+extern void sd_dif_complete(struct scsi_cmnd *, unsigned int);
+
+#else /* CONFIG_BLK_DEV_INTEGRITY */
+
+#define sd_dif_op(a, b, c)                     do { } while (0)
+#define sd_dif_config_host(a)                  do { } while (0)
+#define sd_dif_prepare(a, b, c)                        (0)
+#define sd_dif_complete(a, b)                  (0)
+
+#endif /* CONFIG_BLK_DEV_INTEGRITY */
+
 #endif /* _SCSI_DISK_H */
diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c
new file mode 100644 (file)
index 0000000..4d17f3d
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * sd_dif.c - SCSI Data Integrity Field
+ *
+ * Copyright (C) 2007, 2008 Oracle Corporation
+ * Written by: Martin K. Petersen <martin.petersen@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/crc-t10dif.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsicam.h>
+
+#include <net/checksum.h>
+
+#include "sd.h"
+
+typedef __u16 (csum_fn) (void *, unsigned int);
+
+static __u16 sd_dif_crc_fn(void *data, unsigned int len)
+{
+       return cpu_to_be16(crc_t10dif(data, len));
+}
+
+static __u16 sd_dif_ip_fn(void *data, unsigned int len)
+{
+       return ip_compute_csum(data, len);
+}
+
+/*
+ * Type 1 and Type 2 protection use the same format: 16 bit guard tag,
+ * 16 bit app tag, 32 bit reference tag.
+ */
+static void sd_dif_type1_generate(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+       void *buf = bix->data_buf;
+       struct sd_dif_tuple *sdt = bix->prot_buf;
+       sector_t sector = bix->sector;
+       unsigned int i;
+
+       for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+               sdt->guard_tag = fn(buf, bix->sector_size);
+               sdt->ref_tag = cpu_to_be32(sector & 0xffffffff);
+               sdt->app_tag = 0;
+
+               buf += bix->sector_size;
+               sector++;
+       }
+}
+
+static void sd_dif_type1_generate_crc(struct blk_integrity_exchg *bix)
+{
+       sd_dif_type1_generate(bix, sd_dif_crc_fn);
+}
+
+static void sd_dif_type1_generate_ip(struct blk_integrity_exchg *bix)
+{
+       sd_dif_type1_generate(bix, sd_dif_ip_fn);
+}
+
+static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+       void *buf = bix->data_buf;
+       struct sd_dif_tuple *sdt = bix->prot_buf;
+       sector_t sector = bix->sector;
+       unsigned int i;
+       __u16 csum;
+
+       for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+               /* Unwritten sectors */
+               if (sdt->app_tag == 0xffff)
+                       return 0;
+
+               /* Bad ref tag received from disk */
+               if (sdt->ref_tag == 0xffffffff) {
+                       printk(KERN_ERR
+                              "%s: bad phys ref tag on sector %lu\n",
+                              bix->disk_name, (unsigned long)sector);
+                       return -EIO;
+               }
+
+               if (be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
+                       printk(KERN_ERR
+                              "%s: ref tag error on sector %lu (rcvd %u)\n",
+                              bix->disk_name, (unsigned long)sector,
+                              be32_to_cpu(sdt->ref_tag));
+                       return -EIO;
+               }
+
+               csum = fn(buf, bix->sector_size);
+
+               if (sdt->guard_tag != csum) {
+                       printk(KERN_ERR "%s: guard tag error on sector %lu " \
+                              "(rcvd %04x, data %04x)\n", bix->disk_name,
+                              (unsigned long)sector,
+                              be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
+                       return -EIO;
+               }
+
+               buf += bix->sector_size;
+               sector++;
+       }
+
+       return 0;
+}
+
+static int sd_dif_type1_verify_crc(struct blk_integrity_exchg *bix)
+{
+       return sd_dif_type1_verify(bix, sd_dif_crc_fn);
+}
+
+static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix)
+{
+       return sd_dif_type1_verify(bix, sd_dif_ip_fn);
+}
+
+/*
+ * Functions for interleaving and deinterleaving application tags
+ */
+static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+       struct sd_dif_tuple *sdt = prot;
+       char *tag = tag_buf;
+       unsigned int i, j;
+
+       for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) {
+               sdt->app_tag = tag[j] << 8 | tag[j+1];
+               BUG_ON(sdt->app_tag == 0xffff);
+       }
+}
+
+static void sd_dif_type1_get_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+       struct sd_dif_tuple *sdt = prot;
+       char *tag = tag_buf;
+       unsigned int i, j;
+
+       for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) {
+               tag[j] = (sdt->app_tag & 0xff00) >> 8;
+               tag[j+1] = sdt->app_tag & 0xff;
+       }
+}
+
+static struct blk_integrity dif_type1_integrity_crc = {
+       .name                   = "T10-DIF-TYPE1-CRC",
+       .generate_fn            = sd_dif_type1_generate_crc,
+       .verify_fn              = sd_dif_type1_verify_crc,
+       .get_tag_fn             = sd_dif_type1_get_tag,
+       .set_tag_fn             = sd_dif_type1_set_tag,
+       .tuple_size             = sizeof(struct sd_dif_tuple),
+       .tag_size               = 0,
+};
+
+static struct blk_integrity dif_type1_integrity_ip = {
+       .name                   = "T10-DIF-TYPE1-IP",
+       .generate_fn            = sd_dif_type1_generate_ip,
+       .verify_fn              = sd_dif_type1_verify_ip,
+       .get_tag_fn             = sd_dif_type1_get_tag,
+       .set_tag_fn             = sd_dif_type1_set_tag,
+       .tuple_size             = sizeof(struct sd_dif_tuple),
+       .tag_size               = 0,
+};
+
+
+/*
+ * Type 3 protection has a 16-bit guard tag and 16 + 32 bits of opaque
+ * tag space.
+ */
+static void sd_dif_type3_generate(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+       void *buf = bix->data_buf;
+       struct sd_dif_tuple *sdt = bix->prot_buf;
+       unsigned int i;
+
+       for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+               sdt->guard_tag = fn(buf, bix->sector_size);
+               sdt->ref_tag = 0;
+               sdt->app_tag = 0;
+
+               buf += bix->sector_size;
+       }
+}
+
+static void sd_dif_type3_generate_crc(struct blk_integrity_exchg *bix)
+{
+       sd_dif_type3_generate(bix, sd_dif_crc_fn);
+}
+
+static void sd_dif_type3_generate_ip(struct blk_integrity_exchg *bix)
+{
+       sd_dif_type3_generate(bix, sd_dif_ip_fn);
+}
+
+static int sd_dif_type3_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
+{
+       void *buf = bix->data_buf;
+       struct sd_dif_tuple *sdt = bix->prot_buf;
+       sector_t sector = bix->sector;
+       unsigned int i;
+       __u16 csum;
+
+       for (i = 0 ; i < bix->data_size ; i += bix->sector_size, sdt++) {
+               /* Unwritten sectors */
+               if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff)
+                       return 0;
+
+               csum = fn(buf, bix->sector_size);
+
+               if (sdt->guard_tag != csum) {
+                       printk(KERN_ERR "%s: guard tag error on sector %lu " \
+                              "(rcvd %04x, data %04x)\n", bix->disk_name,
+                              (unsigned long)sector,
+                              be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
+                       return -EIO;
+               }
+
+               buf += bix->sector_size;
+               sector++;
+       }
+
+       return 0;
+}
+
+static int sd_dif_type3_verify_crc(struct blk_integrity_exchg *bix)
+{
+       return sd_dif_type3_verify(bix, sd_dif_crc_fn);
+}
+
+static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix)
+{
+       return sd_dif_type3_verify(bix, sd_dif_ip_fn);
+}
+
+static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+       struct sd_dif_tuple *sdt = prot;
+       char *tag = tag_buf;
+       unsigned int i, j;
+
+       for (i = 0, j = 0 ; i < sectors ; i++, j += 6, sdt++) {
+               sdt->app_tag = tag[j] << 8 | tag[j+1];
+               sdt->ref_tag = tag[j+2] << 24 | tag[j+3] << 16 |
+                       tag[j+4] << 8 | tag[j+5];
+       }
+}
+
+static void sd_dif_type3_get_tag(void *prot, void *tag_buf, unsigned int sectors)
+{
+       struct sd_dif_tuple *sdt = prot;
+       char *tag = tag_buf;
+       unsigned int i, j;
+
+       for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) {
+               tag[j] = (sdt->app_tag & 0xff00) >> 8;
+               tag[j+1] = sdt->app_tag & 0xff;
+               tag[j+2] = (sdt->ref_tag & 0xff000000) >> 24;
+               tag[j+3] = (sdt->ref_tag & 0xff0000) >> 16;
+               tag[j+4] = (sdt->ref_tag & 0xff00) >> 8;
+               tag[j+5] = sdt->ref_tag & 0xff;
+               BUG_ON(sdt->app_tag == 0xffff || sdt->ref_tag == 0xffffffff);
+       }
+}
+
+static struct blk_integrity dif_type3_integrity_crc = {
+       .name                   = "T10-DIF-TYPE3-CRC",
+       .generate_fn            = sd_dif_type3_generate_crc,
+       .verify_fn              = sd_dif_type3_verify_crc,
+       .get_tag_fn             = sd_dif_type3_get_tag,
+       .set_tag_fn             = sd_dif_type3_set_tag,
+       .tuple_size             = sizeof(struct sd_dif_tuple),
+       .tag_size               = 0,
+};
+
+static struct blk_integrity dif_type3_integrity_ip = {
+       .name                   = "T10-DIF-TYPE3-IP",
+       .generate_fn            = sd_dif_type3_generate_ip,
+       .verify_fn              = sd_dif_type3_verify_ip,
+       .get_tag_fn             = sd_dif_type3_get_tag,
+       .set_tag_fn             = sd_dif_type3_set_tag,
+       .tuple_size             = sizeof(struct sd_dif_tuple),
+       .tag_size               = 0,
+};
+
+/*
+ * Configure exchange of protection information between OS and HBA.
+ */
+void sd_dif_config_host(struct scsi_disk *sdkp)
+{
+       struct scsi_device *sdp = sdkp->device;
+       struct gendisk *disk = sdkp->disk;
+       u8 type = sdkp->protection_type;
+
+       /* If this HBA doesn't support DIX, resort to normal I/O or DIF */
+       if (scsi_host_dix_capable(sdp->host, type) == 0) {
+
+               if (type == SD_DIF_TYPE0_PROTECTION)
+                       return;
+
+               if (scsi_host_dif_capable(sdp->host, type) == 0) {
+                       sd_printk(KERN_INFO, sdkp, "Type %d protection " \
+                                 "unsupported by HBA. Disabling DIF.\n", type);
+                       sdkp->protection_type = 0;
+                       return;
+               }
+
+               sd_printk(KERN_INFO, sdkp, "Enabling DIF Type %d protection\n",
+                         type);
+
+               return;
+       }
+
+       /* Enable DMA of protection information */
+       if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP)
+               if (type == SD_DIF_TYPE3_PROTECTION)
+                       blk_integrity_register(disk, &dif_type3_integrity_ip);
+               else
+                       blk_integrity_register(disk, &dif_type1_integrity_ip);
+       else
+               if (type == SD_DIF_TYPE3_PROTECTION)
+                       blk_integrity_register(disk, &dif_type3_integrity_crc);
+               else
+                       blk_integrity_register(disk, &dif_type1_integrity_crc);
+
+       sd_printk(KERN_INFO, sdkp,
+                 "Enabling %s integrity protection\n", disk->integrity->name);
+
+       /* Signal to block layer that we support sector tagging */
+       if (type && sdkp->ATO) {
+               if (type == SD_DIF_TYPE3_PROTECTION)
+                       disk->integrity->tag_size = sizeof(u16) + sizeof(u32);
+               else
+                       disk->integrity->tag_size = sizeof(u16);
+
+               sd_printk(KERN_INFO, sdkp, "DIF application tag size %u\n",
+                         disk->integrity->tag_size);
+       }
+}
+
+/*
+ * DIF DMA operation magic decoder ring.
+ */
+void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix)
+{
+       int csum_convert, prot_op;
+
+       prot_op = 0;
+
+       /* Convert checksum? */
+       if (scsi_host_get_guard(scmd->device->host) != SHOST_DIX_GUARD_CRC)
+               csum_convert = 1;
+       else
+               csum_convert = 0;
+
+       switch (scmd->cmnd[0]) {
+       case READ_10:
+       case READ_12:
+       case READ_16:
+               if (dif && dix)
+                       if (csum_convert)
+                               prot_op = SCSI_PROT_READ_CONVERT;
+                       else
+                               prot_op = SCSI_PROT_READ_PASS;
+               else if (dif && !dix)
+                       prot_op = SCSI_PROT_READ_STRIP;
+               else if (!dif && dix)
+                       prot_op = SCSI_PROT_READ_INSERT;
+
+               break;
+
+       case WRITE_10:
+       case WRITE_12:
+       case WRITE_16:
+               if (dif && dix)
+                       if (csum_convert)
+                               prot_op = SCSI_PROT_WRITE_CONVERT;
+                       else
+                               prot_op = SCSI_PROT_WRITE_PASS;
+               else if (dif && !dix)
+                       prot_op = SCSI_PROT_WRITE_INSERT;
+               else if (!dif && dix)
+                       prot_op = SCSI_PROT_WRITE_STRIP;
+
+               break;
+       }
+
+       scsi_set_prot_op(scmd, prot_op);
+       scsi_set_prot_type(scmd, dif);
+}
+
+/*
+ * The virtual start sector is the one that was originally submitted
+ * by the block layer. Due to partitioning, MD/DM cloning, etc. the
+ * actual physical start sector is likely to be different.  Remap
+ * protection information to match the physical LBA.
+ *
+ * From a protocol perspective there's a slight difference between
+ * Type 1 and 2.  The latter uses 32-byte CDBs exclusively, and the
+ * reference tag is seeded in the CDB.  This gives us the potential to
+ * avoid virt->phys remapping during write.  However, at read time we
+ * don't know whether the virt sector is the same as when we wrote it
+ * (we could be reading from real disk as opposed to MD/DM device.  So
+ * we always remap Type 2 making it identical to Type 1.
+ *
+ * Type 3 does not have a reference tag so no remapping is required.
+ */
+int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_sz)
+{
+       const int tuple_sz = sizeof(struct sd_dif_tuple);
+       struct bio *bio;
+       struct scsi_disk *sdkp;
+       struct sd_dif_tuple *sdt;
+       unsigned int i, j;
+       u32 phys, virt;
+
+       /* Already remapped? */
+       if (rq->cmd_flags & REQ_INTEGRITY)
+               return 0;
+
+       sdkp = rq->bio->bi_bdev->bd_disk->private_data;
+
+       if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION)
+               return 0;
+
+       rq->cmd_flags |= REQ_INTEGRITY;
+       phys = hw_sector & 0xffffffff;
+
+       __rq_for_each_bio(bio, rq) {
+               struct bio_vec *iv;
+
+               virt = bio->bi_integrity->bip_sector & 0xffffffff;
+
+               bip_for_each_vec(iv, bio->bi_integrity, i) {
+                       sdt = kmap_atomic(iv->bv_page, KM_USER0)
+                               + iv->bv_offset;
+
+                       for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) {
+
+                               if (be32_to_cpu(sdt->ref_tag) != virt)
+                                       goto error;
+
+                               sdt->ref_tag = cpu_to_be32(phys);
+                               virt++;
+                               phys++;
+                       }
+
+                       kunmap_atomic(sdt, KM_USER0);
+               }
+       }
+
+       return 0;
+
+error:
+       kunmap_atomic(sdt, KM_USER0);
+       sd_printk(KERN_ERR, sdkp, "%s: virt %u, phys %u, ref %u\n",
+                 __func__, virt, phys, be32_to_cpu(sdt->ref_tag));
+
+       return -EIO;
+}
+
+/*
+ * Remap physical sector values in the reference tag to the virtual
+ * values expected by the block layer.
+ */
+void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes)
+{
+       const int tuple_sz = sizeof(struct sd_dif_tuple);
+       struct scsi_disk *sdkp;
+       struct bio *bio;
+       struct sd_dif_tuple *sdt;
+       unsigned int i, j, sectors, sector_sz;
+       u32 phys, virt;
+
+       sdkp = scsi_disk(scmd->request->rq_disk);
+
+       if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0)
+               return;
+
+       sector_sz = scmd->device->sector_size;
+       sectors = good_bytes / sector_sz;
+
+       phys = scmd->request->sector & 0xffffffff;
+       if (sector_sz == 4096)
+               phys >>= 3;
+
+       __rq_for_each_bio(bio, scmd->request) {
+               struct bio_vec *iv;
+
+               virt = bio->bi_integrity->bip_sector & 0xffffffff;
+
+               bip_for_each_vec(iv, bio->bi_integrity, i) {
+                       sdt = kmap_atomic(iv->bv_page, KM_USER0)
+                               + iv->bv_offset;
+
+                       for (j = 0 ; j < iv->bv_len ; j += tuple_sz, sdt++) {
+
+                               if (sectors == 0) {
+                                       kunmap_atomic(sdt, KM_USER0);
+                                       return;
+                               }
+
+                               if (be32_to_cpu(sdt->ref_tag) != phys &&
+                                   sdt->app_tag != 0xffff)
+                                       sdt->ref_tag = 0xffffffff; /* Bad ref */
+                               else
+                                       sdt->ref_tag = cpu_to_be32(virt);
+
+                               virt++;
+                               phys++;
+                               sectors--;
+                       }
+
+                       kunmap_atomic(sdt, KM_USER0);
+               }
+       }
+}
+
index 4684cc716aa4033575b7738ded7ca3196cadd850..c2bb53e3d941faee5d29f5902ac757093102d772 100644 (file)
@@ -17,7 +17,7 @@
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
  */
 
-static const char *verstr = "20080224";
+static const char *verstr = "20080504";
 
 #include <linux/module.h>
 
@@ -631,7 +631,7 @@ static int cross_eof(struct scsi_tape * STp, int forward)
 /* Flush the write buffer (never need to write if variable blocksize). */
 static int st_flush_write_buffer(struct scsi_tape * STp)
 {
-       int offset, transfer, blks;
+       int transfer, blks;
        int result;
        unsigned char cmd[MAX_COMMAND_SIZE];
        struct st_request *SRpnt;
@@ -644,14 +644,10 @@ static int st_flush_write_buffer(struct scsi_tape * STp)
        result = 0;
        if (STp->dirty == 1) {
 
-               offset = (STp->buffer)->buffer_bytes;
-               transfer = ((offset + STp->block_size - 1) /
-                           STp->block_size) * STp->block_size;
+               transfer = STp->buffer->buffer_bytes;
                 DEBC(printk(ST_DEB_MSG "%s: Flushing %d bytes.\n",
                                tape_name(STp), transfer));
 
-               memset((STp->buffer)->b_data + offset, 0, transfer - offset);
-
                memset(cmd, 0, MAX_COMMAND_SIZE);
                cmd[0] = WRITE_6;
                cmd[1] = 1;
@@ -1670,6 +1666,7 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
                                if (undone <= do_count) {
                                        /* Only data from this write is not written */
                                        count += undone;
+                                       b_point -= undone;
                                        do_count -= undone;
                                        if (STp->block_size)
                                                blks = (transfer - undone) / STp->block_size;
index f308a030882916aed13c1805ef9213758e1313eb..3790906a77d10c06705b3616a22c5650d50f1948 100644 (file)
@@ -467,7 +467,7 @@ stex_slave_alloc(struct scsi_device *sdev)
        /* Cheat: usually extracted from Inquiry data */
        sdev->tagged_supported = 1;
 
-       scsi_activate_tcq(sdev, sdev->host->can_queue);
+       scsi_activate_tcq(sdev, ST_CMD_PER_LUN);
 
        return 0;
 }
index 22a6aae78699ab06762858f7c1c7f6c3bcbb4958..98df1651404faff46e9241e7a776f4e69f45885f 100644 (file)
@@ -5741,6 +5741,8 @@ void sym_hcb_free(struct sym_hcb *np)
 
        for (target = 0; target < SYM_CONF_MAX_TARGET ; target++) {
                tp = &np->target[target];
+               if (tp->luntbl)
+                       sym_mfree_dma(tp->luntbl, 256, "LUNTBL");
 #if SYM_CONF_MAX_LUN > 1
                kfree(tp->lunmp);
 #endif 
index 5b04ddfed26c6162c0234963963598998d79a4b3..1723d71cbf3fa383ed99e706721027e918fa80da 100644 (file)
@@ -452,7 +452,7 @@ static int dc390_pci_map (struct dc390_srb* pSRB)
                /* TODO: error handling */
                if (pSRB->SGcount != 1)
                        error = 1;
-               DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __FUNCTION__, pcmd->sense_buffer, cmdp->saved_dma_handle));
+               DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __func__, pcmd->sense_buffer, cmdp->saved_dma_handle));
        /* Map SG list */
        } else if (scsi_sg_count(pcmd)) {
                int nseg;
@@ -466,7 +466,7 @@ static int dc390_pci_map (struct dc390_srb* pSRB)
                if (nseg < 0)
                        error = 1;
                DEBUG1(printk("%s(): Mapped SG %p with %d (%d) elements\n",\
-                             __FUNCTION__, scsi_sglist(pcmd), nseg, scsi_sg_count(pcmd)));
+                             __func__, scsi_sglist(pcmd), nseg, scsi_sg_count(pcmd)));
        /* Map single segment */
        } else
                pSRB->SGcount = 0;
@@ -483,11 +483,11 @@ static void dc390_pci_unmap (struct dc390_srb* pSRB)
 
        if (pSRB->SRBFlag) {
                pci_unmap_sg(pdev, &pSRB->Segmentx, 1, DMA_FROM_DEVICE);
-               DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __FUNCTION__, cmdp->saved_dma_handle));
+               DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __func__, cmdp->saved_dma_handle));
        } else {
                scsi_dma_unmap(pcmd);
                DEBUG1(printk("%s(): Unmapped SG at %p with %d elements\n",
-                             __FUNCTION__, scsi_sglist(pcmd), scsi_sg_count(pcmd)));
+                             __func__, scsi_sglist(pcmd), scsi_sg_count(pcmd)));
        }
 }
 
index c975c01b3a02431000f071116f3b40caffd4f3f8..d4c13561f4a6f39d9e719ba6df45f5692c83e26b 100644 (file)
  *
  * 2002/10/04 - Alan Cox <alan@redhat.com>
  *
- * Use dev_id for interrupts, kill __FUNCTION__ pasting
+ * Use dev_id for interrupts, kill __func__ pasting
  * Add a lock for the scb pool, clean up all other cli/sti usage stuff
  * Use the adapter lock for the other places we had the cli's
  *
@@ -640,12 +640,12 @@ static int __init wd7000_setup(char *str)
        (void) get_options(str, ARRAY_SIZE(ints), ints);
 
        if (wd7000_card_num >= NUM_CONFIGS) {
-               printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __FUNCTION__);
+               printk(KERN_ERR "%s: Too many \"wd7000=\" configurations in " "command line!\n", __func__);
                return 0;
        }
 
        if ((ints[0] < 3) || (ints[0] > 5)) {
-               printk(KERN_ERR "%s: Error in command line!  " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __FUNCTION__);
+               printk(KERN_ERR "%s: Error in command line!  " "Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>" "[,<BUS_OFF>]]\n", __func__);
        } else {
                for (i = 0; i < NUM_IRQS; i++)
                        if (ints[1] == wd7000_irq[i])
@@ -1642,7 +1642,7 @@ static int wd7000_biosparam(struct scsi_device *sdev,
                        ip[2] = info[2];
 
                        if (info[0] == 255)
-                               printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __FUNCTION__);
+                               printk(KERN_INFO "%s: current partition table is " "using extended translation.\n", __func__);
                }
        }
 
index 4b5f908d35c308c26f76afd8a7cbb0100dc2cce4..3c4a300494a40843b49c6e86c730408a8716c832 100644 (file)
@@ -68,11 +68,11 @@ lasi_scsi_clock(void * hpa, int defaultclock)
        if (status == PDC_RET_OK) {
                clock = (int) pdc_result[16];
        } else {
-               printk(KERN_WARNING "%s: pdc_iodc_read returned %d\n", __FUNCTION__, status);
+               printk(KERN_WARNING "%s: pdc_iodc_read returned %d\n", __func__, status);
                clock = defaultclock; 
        }
 
-       printk(KERN_DEBUG "%s: SCSI clock %d\n", __FUNCTION__, clock);
+       printk(KERN_DEBUG "%s: SCSI clock %d\n", __func__, clock);
        return clock;
 }
 #endif
@@ -108,13 +108,13 @@ zalon_probe(struct parisc_device *dev)
        */
        dev->irq = gsc_alloc_irq(&gsc_irq);
 
-       printk(KERN_INFO "%s: Zalon version %d, IRQ %d\n", __FUNCTION__,
+       printk(KERN_INFO "%s: Zalon version %d, IRQ %d\n", __func__,
                zalon_vers, dev->irq);
 
        __raw_writel(gsc_irq.txn_addr | gsc_irq.txn_data, zalon + IO_MODULE_EIM);
 
        if (zalon_vers == 0)
-               printk(KERN_WARNING "%s: Zalon 1.1 or earlier\n", __FUNCTION__);
+               printk(KERN_WARNING "%s: Zalon 1.1 or earlier\n", __func__);
 
        memset(&device, 0, sizeof(struct ncr_device));
 
index 832a5a4f3cb3ca9ab3394b9795b7685c8f80d7b1..cd9a2e138c8b35f3a3cacb4cd60525ed39d179b6 100644 (file)
@@ -651,15 +651,17 @@ static int ipaq_open(struct tty_struct *tty,
         */
 
        kfree(port->bulk_in_buffer);
+       kfree(port->bulk_out_buffer);
+       /* make sure the generic serial code knows */
+       port->bulk_out_buffer = NULL;
+
        port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
-       if (port->bulk_in_buffer == NULL) {
-               port->bulk_out_buffer = NULL; /* prevent double free */
+       if (port->bulk_in_buffer == NULL)
                goto enomem;
-       }
 
-       kfree(port->bulk_out_buffer);
        port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
        if (port->bulk_out_buffer == NULL) {
+               /* the buffer is useless, free it */
                kfree(port->bulk_in_buffer);
                port->bulk_in_buffer = NULL;
                goto enomem;
index 6eac7bdeec94dc320924d7be0892fb89a23cde72..1377b1240b6eadba12cf89c5b961d5d90d33c594 100644 (file)
@@ -46,8 +46,6 @@ const struct inode_operations affs_file_inode_operations = {
 static int
 affs_file_open(struct inode *inode, struct file *filp)
 {
-       if (atomic_read(&filp->f_count) != 1)
-               return 0;
        pr_debug("AFFS: open(%lu,%d)\n",
                 inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
        atomic_inc(&AFFS_I(inode)->i_opencnt);
@@ -57,8 +55,6 @@ affs_file_open(struct inode *inode, struct file *filp)
 static int
 affs_file_release(struct inode *inode, struct file *filp)
 {
-       if (atomic_read(&filp->f_count) != 0)
-               return 0;
        pr_debug("AFFS: release(%lu, %d)\n",
                 inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
 
index 7102824ba8479c6e2d006816fb3555311d56df61..3cb6920ff30bc6ee7f22a98a390b04119d1ae81c 100644 (file)
@@ -469,8 +469,6 @@ extern bool afs_cm_incoming_call(struct afs_call *);
 extern const struct inode_operations afs_dir_inode_operations;
 extern const struct file_operations afs_dir_file_operations;
 
-extern int afs_permission(struct inode *, int, struct nameidata *);
-
 /*
  * file.c
  */
@@ -605,7 +603,7 @@ extern void afs_clear_permits(struct afs_vnode *);
 extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
 extern void afs_zap_permits(struct rcu_head *);
 extern struct key *afs_request_key(struct afs_cell *);
-extern int afs_permission(struct inode *, int, struct nameidata *);
+extern int afs_permission(struct inode *, int);
 
 /*
  * server.c
index 3bcbeceba1bb6a778b08328ea459023927a2ccc8..3ef504370034c2311fdd50f631856ac95ab76903 100644 (file)
@@ -284,7 +284,7 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
  * - AFS ACLs are attached to directories only, and a file is controlled by its
  *   parent directory's ACL
  */
-int afs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int afs_permission(struct inode *inode, int mask)
 {
        struct afs_vnode *vnode = AFS_FS_I(inode);
        afs_access_t uninitialized_var(access);
index 0051fd94b44e7e75bde7882b2d9c074a1d1f8188..f658441d5666c9d108f8ec337b2e0909686723bb 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -512,8 +512,8 @@ static void aio_fput_routine(struct work_struct *data)
  */
 static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
 {
-       dprintk(KERN_DEBUG "aio_put(%p): f_count=%d\n",
-               req, atomic_read(&req->ki_filp->f_count));
+       dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n",
+               req, atomic_long_read(&req->ki_filp->f_count));
 
        assert_spin_locked(&ctx->ctx_lock);
 
@@ -528,7 +528,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
        /* Must be done under the lock to serialise against cancellation.
         * Call this aio_fput as it duplicates fput via the fput_work.
         */
-       if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) {
+       if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) {
                get_ioctx(ctx);
                spin_lock(&fput_lock);
                list_add(&req->ki_list, &fput_head);
index 966b73e25f82e06f69ad0eca22a3f6fe268c4cd0..26c71ba1eed49d2f500c9e8044e68693492a47ee 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -51,7 +51,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
        }
 
        /* Check for setting the inode time. */
-       if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {
+       if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
                if (!is_owner_or_cap(inode))
                        goto error;
        }
@@ -108,6 +108,11 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
        struct timespec now;
        unsigned int ia_valid = attr->ia_valid;
 
+       if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
+               if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+                       return -EPERM;
+       }
+
        now = current_fs_time(inode->i_sb);
 
        attr->ia_ctime = now;
index f1c2ea8342f514c974539dd5a676b650efbf6e5f..5f1538c03b1bbeb068c089146cc28fc502bb0d83 100644 (file)
@@ -243,8 +243,7 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
        return -EIO;
 }
 
-static int bad_inode_permission(struct inode *inode, int mask,
-                       struct nameidata *nd)
+static int bad_inode_permission(struct inode *inode, int mask)
 {
        return -EIO;
 }
index fe5f6809cba6e75322511b10b4066c3e4d61551f..1ec7076f7b241b7a86d182043db5debf36fdd977 100644 (file)
@@ -267,7 +267,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
        return 0;
 }
 
-static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int cifs_permission(struct inode *inode, int mask)
 {
        struct cifs_sb_info *cifs_sb;
 
index 3d2580e00a3e272e0cfe04103c98466e2daa15e7..c5916228243c1516b2d62e6b06cea208523a09ac 100644 (file)
@@ -137,9 +137,11 @@ exit:
 }
 
 
-int coda_permission(struct inode *inode, int mask, struct nameidata *nd)
+int coda_permission(struct inode *inode, int mask)
 {
         int error = 0;
+
+       mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
        if (!mask)
                return 0; 
index c21a1f552a63bf0ca7ef5d09905a326036f0e844..c51365422aa8da4311133c3b0ff773421efbc723 100644 (file)
@@ -24,8 +24,7 @@
 #include <linux/coda_psdev.h>
 
 /* pioctl ops */
-static int coda_ioctl_permission(struct inode *inode, int mask,
-                                struct nameidata *nd);
+static int coda_ioctl_permission(struct inode *inode, int mask);
 static int coda_pioctl(struct inode * inode, struct file * filp, 
                        unsigned int cmd, unsigned long user_data);
 
@@ -42,8 +41,7 @@ const struct file_operations coda_ioctl_operations = {
 };
 
 /* the coda pioctl inode ops */
-static int coda_ioctl_permission(struct inode *inode, int mask,
-                                struct nameidata *nd)
+static int coda_ioctl_permission(struct inode *inode, int mask)
 {
         return 0;
 }
@@ -51,7 +49,7 @@ static int coda_ioctl_permission(struct inode *inode, int mask,
 static int coda_pioctl(struct inode * inode, struct file * filp, 
                        unsigned int cmd, unsigned long user_data)
 {
-       struct nameidata nd;
+       struct path path;
         int error;
        struct PioctlData data;
         struct inode *target_inode = NULL;
@@ -66,21 +64,21 @@ static int coda_pioctl(struct inode * inode, struct file * filp,
          * Look up the pathname. Note that the pathname is in 
          * user memory, and namei takes care of this
          */
-        if ( data.follow ) {
-                error = user_path_walk(data.path, &nd);
+        if (data.follow) {
+                error = user_path(data.path, &path);
        } else {
-               error = user_path_walk_link(data.path, &nd);
+               error = user_lpath(data.path, &path);
        }
                
        if ( error ) {
                return error;
         } else {
-               target_inode = nd.path.dentry->d_inode;
+               target_inode = path.dentry->d_inode;
        }
        
        /* return if it is not a Coda inode */
        if ( target_inode->i_sb != inode->i_sb ) {
-               path_put(&nd.path);
+               path_put(&path);
                return  -EINVAL;
        }
 
@@ -89,7 +87,7 @@ static int coda_pioctl(struct inode * inode, struct file * filp,
 
        error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
 
-       path_put(&nd.path);
+       path_put(&path);
         return error;
 }
 
index 106eba28ec5a8c7a4f05f906f9fea1f7fd421c5c..c9d1472e65c5a78288cd6c85c8c16f9bcd190a4c 100644 (file)
@@ -234,18 +234,18 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
  * The following statfs calls are copies of code from fs/open.c and
  * should be checked against those from time to time
  */
-asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf)
+asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct kstatfs tmp;
-               error = vfs_statfs(nd.path.dentry, &tmp);
+               error = vfs_statfs(path.dentry, &tmp);
                if (!error)
                        error = put_compat_statfs(buf, &tmp);
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
@@ -299,21 +299,21 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat
        return 0;
 }
 
-asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf)
+asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
        if (sz != sizeof(*buf))
                return -EINVAL;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct kstatfs tmp;
-               error = vfs_statfs(nd.path.dentry, &tmp);
+               error = vfs_statfs(path.dentry, &tmp);
                if (!error)
                        error = put_compat_statfs64(buf, &tmp);
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
index d755455e3bff03aaffd1fd95715265b9f28f931f..89209f00f9c74ecc752ed944f26ecff6e2f5c974 100644 (file)
@@ -465,7 +465,6 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
        int rc;
        struct dentry *lower_dentry;
        struct dentry *lower_dir_dentry;
-       umode_t mode;
        char *encoded_symname;
        int encoded_symlen;
        struct ecryptfs_crypt_stat *crypt_stat = NULL;
@@ -473,7 +472,6 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
        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);
@@ -482,7 +480,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
                goto out_lock;
        }
        rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
-                        encoded_symname, mode);
+                        encoded_symname);
        kfree(encoded_symname);
        if (rc || !lower_dentry->d_inode)
                goto out_lock;
@@ -830,22 +828,9 @@ out:
 }
 
 static int
-ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+ecryptfs_permission(struct inode *inode, int mask)
 {
-       int rc;
-
-        if (nd) {
-               struct vfsmount *vfsmnt_save = nd->path.mnt;
-               struct dentry *dentry_save = nd->path.dentry;
-
-               nd->path.mnt = ecryptfs_dentry_to_lower_mnt(nd->path.dentry);
-               nd->path.dentry = ecryptfs_dentry_to_lower(nd->path.dentry);
-               rc = permission(ecryptfs_inode_to_lower(inode), mask, nd);
-               nd->path.mnt = vfsmnt_save;
-               nd->path.dentry = dentry_save;
-        } else
-               rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL);
-        return rc;
+       return inode_permission(ecryptfs_inode_to_lower(inode), mask);
 }
 
 /**
index b8792a13153377f83bc465a320eb35d6d0bd2c33..9696bbf0f0b1c1b1bd4578fdbfac331fa62ffc48 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -106,11 +106,17 @@ static inline void put_binfmt(struct linux_binfmt * fmt)
  */
 asmlinkage long sys_uselib(const char __user * library)
 {
-       struct file * file;
+       struct file *file;
        struct nameidata nd;
-       int error;
-
-       error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
+       char *tmp = getname(library);
+       int error = PTR_ERR(tmp);
+
+       if (!IS_ERR(tmp)) {
+               error = path_lookup_open(AT_FDCWD, tmp,
+                                        LOOKUP_FOLLOW, &nd,
+                                        FMODE_READ|FMODE_EXEC);
+               putname(tmp);
+       }
        if (error)
                goto out;
 
@@ -118,7 +124,11 @@ asmlinkage long sys_uselib(const char __user * library)
        if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
                goto exit;
 
-       error = vfs_permission(&nd, MAY_READ | MAY_EXEC);
+       error = -EACCES;
+       if (nd.path.mnt->mnt_flags & MNT_NOEXEC)
+               goto exit;
+
+       error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN);
        if (error)
                goto exit;
 
@@ -656,38 +666,43 @@ EXPORT_SYMBOL(setup_arg_pages);
 struct file *open_exec(const char *name)
 {
        struct nameidata nd;
-       int err;
        struct file *file;
+       int err;
 
-       err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
-       file = ERR_PTR(err);
-
-       if (!err) {
-               struct inode *inode = nd.path.dentry->d_inode;
-               file = ERR_PTR(-EACCES);
-               if (S_ISREG(inode->i_mode)) {
-                       int err = vfs_permission(&nd, MAY_EXEC);
-                       file = ERR_PTR(err);
-                       if (!err) {
-                               file = nameidata_to_filp(&nd,
-                                                       O_RDONLY|O_LARGEFILE);
-                               if (!IS_ERR(file)) {
-                                       err = deny_write_access(file);
-                                       if (err) {
-                                               fput(file);
-                                               file = ERR_PTR(err);
-                                       }
-                               }
-out:
-                               return file;
-                       }
-               }
-               release_open_intent(&nd);
-               path_put(&nd.path);
+       err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd,
+                               FMODE_READ|FMODE_EXEC);
+       if (err)
+               goto out;
+
+       err = -EACCES;
+       if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
+               goto out_path_put;
+
+       if (nd.path.mnt->mnt_flags & MNT_NOEXEC)
+               goto out_path_put;
+
+       err = vfs_permission(&nd, MAY_EXEC | MAY_OPEN);
+       if (err)
+               goto out_path_put;
+
+       file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
+       if (IS_ERR(file))
+               return file;
+
+       err = deny_write_access(file);
+       if (err) {
+               fput(file);
+               goto out;
        }
-       goto out;
-}
 
+       return file;
+
+ out_path_put:
+       release_open_intent(&nd);
+       path_put(&nd.path);
+ out:
+       return ERR_PTR(err);
+}
 EXPORT_SYMBOL(open_exec);
 
 int kernel_read(struct file *file, unsigned long offset,
index e58669e1b87c6bab0e0a0ac8677713b5b2f3fbb8..ae8c4f850b27d0e83e28e684a07637a992abbe69 100644 (file)
@@ -294,7 +294,7 @@ ext2_check_acl(struct inode *inode, int mask)
 }
 
 int
-ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext2_permission(struct inode *inode, int mask)
 {
        return generic_permission(inode, mask, ext2_check_acl);
 }
index 0bde85bafe386eb47187919a8409e0231b4f5298..b42cf578554b5fd10ec8c290ea3fca3af05f9020 100644 (file)
@@ -58,7 +58,7 @@ static inline int ext2_acl_count(size_t size)
 #define EXT2_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext2_permission (struct inode *, int, struct nameidata *);
+extern int ext2_permission (struct inode *, int);
 extern int ext2_acl_chmod (struct inode *);
 extern int ext2_init_acl (struct inode *, struct inode *);
 
index a754d1848173af1b0d5e899d29c0a914211a134a..b60bb241880c86f50420e13411dc19b1148e1a51 100644 (file)
@@ -299,7 +299,7 @@ ext3_check_acl(struct inode *inode, int mask)
 }
 
 int
-ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext3_permission(struct inode *inode, int mask)
 {
        return generic_permission(inode, mask, ext3_check_acl);
 }
index 0d1e6279cbfd5799a11f27f774fbcdaf8bd397fb..42da16b8cac0a82d1bdd06a18c178308068e8d08 100644 (file)
@@ -58,7 +58,7 @@ static inline int ext3_acl_count(size_t size)
 #define EXT3_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext3_permission (struct inode *, int, struct nameidata *);
+extern int ext3_permission (struct inode *, int);
 extern int ext3_acl_chmod (struct inode *);
 extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
 
index 3c8dab880d91f1bb38247d44d605f2bc3943a90e..c7d04e165446dfc5391930b24ce2a1d3280e71d1 100644 (file)
@@ -299,7 +299,7 @@ ext4_check_acl(struct inode *inode, int mask)
 }
 
 int
-ext4_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext4_permission(struct inode *inode, int mask)
 {
        return generic_permission(inode, mask, ext4_check_acl);
 }
index 26a5c1abf147bf9690f091baba133ec5eab7cf00..cd2b855a07d669988f817f6e7415088b8dcefed6 100644 (file)
@@ -58,7 +58,7 @@ static inline int ext4_acl_count(size_t size)
 #define EXT4_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext4_permission (struct inode *, int, struct nameidata *);
+extern int ext4_permission (struct inode *, int);
 extern int ext4_acl_chmod (struct inode *);
 extern int ext4_init_acl (handle_t *, struct inode *, struct inode *);
 
index c672df4036e94cc59aed3c1c59b4fdeb8882fe7a..8707a8cfa02cdd8454056ed320ee49a4604c84f8 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/writeback.h>
 #include <linux/backing-dev.h>
 #include <linux/blkdev.h>
+#include <linux/fsnotify.h>
+#include <linux/security.h>
 
 int fat_generic_ioctl(struct inode *inode, struct file *filp,
                      unsigned int cmd, unsigned long arg)
@@ -64,6 +66,7 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp,
 
                /* Equivalent to a chmod() */
                ia.ia_valid = ATTR_MODE | ATTR_CTIME;
+               ia.ia_ctime = current_fs_time(inode->i_sb);
                if (is_dir) {
                        ia.ia_mode = MSDOS_MKMODE(attr,
                                S_IRWXUGO & ~sbi->options.fs_dmask)
@@ -90,11 +93,21 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp,
                        }
                }
 
+               /*
+                * The security check is questionable...  We single
+                * out the RO attribute for checking by the security
+                * module, just because it maps to a file mode.
+                */
+               err = security_inode_setattr(filp->f_path.dentry, &ia);
+               if (err)
+                       goto up;
+
                /* This MUST be done before doing anything irreversible... */
-               err = notify_change(filp->f_path.dentry, &ia);
+               err = fat_setattr(filp->f_path.dentry, &ia);
                if (err)
                        goto up;
 
+               fsnotify_change(filp->f_path.dentry, ia.ia_valid);
                if (sbi->options.sys_immutable) {
                        if (attr & ATTR_SYS)
                                inode->i_flags |= S_IMMUTABLE;
index 9679fcbdeaa0b7440992db5baec1698e90704588..61d625136813648c3ced7aa4c6daf344d6700e43 100644 (file)
@@ -64,11 +64,6 @@ static int locate_fd(unsigned int orig_start, int cloexec)
        struct fdtable *fdt;
 
        spin_lock(&files->file_lock);
-
-       error = -EINVAL;
-       if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-               goto out;
-
 repeat:
        fdt = files_fdtable(files);
        /*
@@ -83,10 +78,6 @@ repeat:
        if (start < fdt->max_fds)
                newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
                                           fdt->max_fds, start);
-       
-       error = -EMFILE;
-       if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-               goto out;
 
        error = expand_files(files, newfd);
        if (error < 0)
@@ -135,20 +126,20 @@ asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags)
        if ((flags & ~O_CLOEXEC) != 0)
                return -EINVAL;
 
+       if (unlikely(oldfd == newfd))
+               return -EINVAL;
+
        spin_lock(&files->file_lock);
        if (!(file = fcheck(oldfd)))
                goto out_unlock;
-       err = newfd;
-       if (newfd == oldfd)
-               goto out_unlock;
-       err = -EBADF;
-       if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-               goto out_unlock;
        get_file(file);                 /* We are now finished with oldfd */
 
        err = expand_files(files, newfd);
-       if (err < 0)
+       if (unlikely(err < 0)) {
+               if (err == -EMFILE)
+                       err = -EBADF;
                goto out_fput;
+       }
 
        /* To avoid races with open() and dup(), we will mark the fd as
         * in-use in the open-file bitmap throughout the entire dup2()
@@ -189,6 +180,14 @@ out_fput:
 
 asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
 {
+       if (unlikely(newfd == oldfd)) { /* corner case */
+               struct files_struct *files = current->files;
+               rcu_read_lock();
+               if (!fcheck_files(files, oldfd))
+                       oldfd = -EBADF;
+               rcu_read_unlock();
+               return oldfd;
+       }
        return sys_dup3(oldfd, newfd, 0);
 }
 
@@ -321,6 +320,8 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        switch (cmd) {
        case F_DUPFD:
        case F_DUPFD_CLOEXEC:
+               if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+                       break;
                get_file(filp);
                err = dupfd(filp, arg, cmd == F_DUPFD_CLOEXEC);
                break;
index 9785e36f81e730e24dd9f1312e2c38f5e35929a4..987bf94114957beca2250a221778f3f93c20d6d2 100644 (file)
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -57,7 +57,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
         *  POSIX.1 says that O_NONBLOCK means return with the FIFO
         *  opened, even when there is no process writing the FIFO.
         */
-               filp->f_op = &read_fifo_fops;
+               filp->f_op = &read_pipefifo_fops;
                pipe->r_counter++;
                if (pipe->readers++ == 0)
                        wake_up_partner(inode);
@@ -86,7 +86,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
                if ((filp->f_flags & O_NONBLOCK) && !pipe->readers)
                        goto err;
 
-               filp->f_op = &write_fifo_fops;
+               filp->f_op = &write_pipefifo_fops;
                pipe->w_counter++;
                if (!pipe->writers++)
                        wake_up_partner(inode);
@@ -105,7 +105,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
         *  This implementation will NEVER block on a O_RDWR open, since
         *  the process can at least talk to itself.
         */
-               filp->f_op = &rdwr_fifo_fops;
+               filp->f_op = &rdwr_pipefifo_fops;
 
                pipe->readers++;
                pipe->writers++;
@@ -151,5 +151,5 @@ err_nocleanup:
  * depending on the access mode of the file...
  */
 const struct file_operations def_fifo_fops = {
-       .open           = fifo_open,    /* will set read or write pipe_fops */
+       .open           = fifo_open,    /* will set read_ or write_pipefifo_fops */
 };
index 7b3887e054d0bcb5e0e187c8e1e42691c4fadcd0..d8773b19fe47fd23d6bbec2149679b123a79c139 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -250,9 +250,18 @@ int expand_files(struct files_struct *files, int nr)
        struct fdtable *fdt;
 
        fdt = files_fdtable(files);
+
+       /*
+        * N.B. For clone tasks sharing a files structure, this test
+        * will limit the total number of files that can be opened.
+        */
+       if (nr >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+               return -EMFILE;
+
        /* Do we need to expand? */
        if (nr < fdt->max_fds)
                return 0;
+
        /* Can we expand? */
        if (nr >= sysctl_nr_open)
                return -EMFILE;
index 83084225b4c3b3195520d5a7a441360046d6f230..f45a4493f9e71b5afe52640f453a33bce0603c70 100644 (file)
@@ -120,7 +120,7 @@ struct file *get_empty_filp(void)
 
        tsk = current;
        INIT_LIST_HEAD(&f->f_u.fu_list);
-       atomic_set(&f->f_count, 1);
+       atomic_long_set(&f->f_count, 1);
        rwlock_init(&f->f_owner.lock);
        f->f_uid = tsk->fsuid;
        f->f_gid = tsk->fsgid;
@@ -219,7 +219,7 @@ EXPORT_SYMBOL(init_file);
 
 void fput(struct file *file)
 {
-       if (atomic_dec_and_test(&file->f_count))
+       if (atomic_long_dec_and_test(&file->f_count))
                __fput(file);
 }
 
@@ -294,7 +294,7 @@ struct file *fget(unsigned int fd)
        rcu_read_lock();
        file = fcheck_files(files, fd);
        if (file) {
-               if (!atomic_inc_not_zero(&file->f_count)) {
+               if (!atomic_long_inc_not_zero(&file->f_count)) {
                        /* File object ref couldn't be taken */
                        rcu_read_unlock();
                        return NULL;
@@ -326,7 +326,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed)
                rcu_read_lock();
                file = fcheck_files(files, fd);
                if (file) {
-                       if (atomic_inc_not_zero(&file->f_count))
+                       if (atomic_long_inc_not_zero(&file->f_count))
                                *fput_needed = 1;
                        else
                                /* Didn't get the reference, someone's freed */
@@ -341,7 +341,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed)
 
 void put_filp(struct file *file)
 {
-       if (atomic_dec_and_test(&file->f_count)) {
+       if (atomic_long_dec_and_test(&file->f_count)) {
                security_file_free(file);
                file_kill(file);
                file_free(file);
index 51d0035ff07e3a59037324ef256eb6b849163deb..fd03330cadeb135979c5229eb861c89155df8de9 100644 (file)
@@ -898,7 +898,7 @@ static int fuse_access(struct inode *inode, int mask)
                return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
-       inarg.mask = mask;
+       inarg.mask = mask & (MAY_READ | MAY_WRITE | MAY_EXEC);
        req->in.h.opcode = FUSE_ACCESS;
        req->in.h.nodeid = get_node_id(inode);
        req->in.numargs = 1;
@@ -927,7 +927,7 @@ static int fuse_access(struct inode *inode, int mask)
  * access request is sent.  Execute permission is still checked
  * locally based on file mode.
  */
-static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int fuse_permission(struct inode *inode, int mask)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
        bool refreshed = false;
@@ -962,7 +962,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
                   exist.  So if permissions are revoked this won't be
                   noticed immediately, only after the attribute
                   timeout has expired */
-       } else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) {
+       } else if (mask & MAY_ACCESS) {
                err = fuse_access(inode, mask);
        } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
                if (!(inode->i_mode & S_IXUGO)) {
index 67ff2c6a8f6309bc09746a65d29b8d55f0a90bb5..2bada6bbc317dbeab3887fbcd26ed9fe630b6148 100644 (file)
@@ -893,7 +893,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (count == 0)
                goto out;
 
-       err = remove_suid(file->f_path.dentry);
+       err = file_remove_suid(file);
        if (err)
                goto out;
 
index 6da0ab355b8a6e6672ae256ae881289b72c9edb0..8b0806a3294863392c914aa262b2a4cb08f4c98e 100644 (file)
@@ -448,7 +448,7 @@ struct inode *gfs2_lookup_simple(struct inode *dip, const char *name)
        struct qstr qstr;
        struct inode *inode;
        gfs2_str2qstr(&qstr, name);
-       inode = gfs2_lookupi(dip, &qstr, 1, NULL);
+       inode = gfs2_lookupi(dip, &qstr, 1);
        /* gfs2_lookupi has inconsistent callers: vfs
         * related routines expect NULL for no entry found,
         * gfs2_lookup_simple callers expect ENOENT
@@ -477,7 +477,7 @@ struct inode *gfs2_lookup_simple(struct inode *dip, const char *name)
  */
 
 struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
-                          int is_root, struct nameidata *nd)
+                          int is_root)
 {
        struct super_block *sb = dir->i_sb;
        struct gfs2_inode *dip = GFS2_I(dir);
@@ -1173,7 +1173,7 @@ int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
                        break;
                }
 
-               tmp = gfs2_lookupi(dir, &dotdot, 1, NULL);
+               tmp = gfs2_lookupi(dir, &dotdot, 1);
                if (IS_ERR(tmp)) {
                        error = PTR_ERR(tmp);
                        break;
index 6074c2506f75b3fced5934ff64e159b06b45b409..58f9607d6a8675694769b978bd93f17e56da47dc 100644 (file)
@@ -83,7 +83,7 @@ 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);
+                          int is_root);
 struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
                           unsigned int mode, dev_t dev);
 int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name,
index 990d9f4bc463588df8f24e94f9439f4750e8f951..9cda8536530cb04d40a3a1e85ccc128c1f3baba1 100644 (file)
@@ -134,7 +134,7 @@ static struct dentry *gfs2_get_parent(struct dentry *child)
        struct dentry *dentry;
 
        gfs2_str2qstr(&dotdot, "..");
-       inode = gfs2_lookupi(child->d_inode, &dotdot, 1, NULL);
+       inode = gfs2_lookupi(child->d_inode, &dotdot, 1);
 
        if (!inode)
                return ERR_PTR(-ENOENT);
index 1e252dfc52940c745dea61137afc386dbe183cd7..e2c62f73a7783db388f8da70fee51874cf9e4879 100644 (file)
@@ -74,7 +74,7 @@ static int gfs2_create(struct inode *dir, struct dentry *dentry,
                        return PTR_ERR(inode);
                }
 
-               inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd);
+               inode = gfs2_lookupi(dir, &dentry->d_name, 0);
                if (inode) {
                        if (!IS_ERR(inode)) {
                                gfs2_holder_uninit(ghs);
@@ -109,7 +109,7 @@ static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
 
        dentry->d_op = &gfs2_dops;
 
-       inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd);
+       inode = gfs2_lookupi(dir, &dentry->d_name, 0);
        if (inode && IS_ERR(inode))
                return ERR_CAST(inode);
 
@@ -915,12 +915,6 @@ int gfs2_permission(struct inode *inode, int mask)
        return error;
 }
 
-static int gfs2_iop_permission(struct inode *inode, int mask,
-                              struct nameidata *nd)
-{
-       return gfs2_permission(inode, mask);
-}
-
 static int setattr_size(struct inode *inode, struct iattr *attr)
 {
        struct gfs2_inode *ip = GFS2_I(inode);
@@ -1150,7 +1144,7 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name)
 }
 
 const struct inode_operations gfs2_file_iops = {
-       .permission = gfs2_iop_permission,
+       .permission = gfs2_permission,
        .setattr = gfs2_setattr,
        .getattr = gfs2_getattr,
        .setxattr = gfs2_setxattr,
@@ -1169,7 +1163,7 @@ const struct inode_operations gfs2_dir_iops = {
        .rmdir = gfs2_rmdir,
        .mknod = gfs2_mknod,
        .rename = gfs2_rename,
-       .permission = gfs2_iop_permission,
+       .permission = gfs2_permission,
        .setattr = gfs2_setattr,
        .getattr = gfs2_getattr,
        .setxattr = gfs2_setxattr,
@@ -1181,7 +1175,7 @@ const struct inode_operations gfs2_dir_iops = {
 const struct inode_operations gfs2_symlink_iops = {
        .readlink = gfs2_readlink,
        .follow_link = gfs2_follow_link,
-       .permission = gfs2_iop_permission,
+       .permission = gfs2_permission,
        .setattr = gfs2_setattr,
        .getattr = gfs2_getattr,
        .setxattr = gfs2_setxattr,
index 63a8a902d9db16029898c8d1caf74e5a198aac48..ca831991cbc2d8a9cd0e82ee1ec6dc8f839d2d13 100644 (file)
@@ -389,7 +389,7 @@ int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
                        break;
 
                INIT_LIST_HEAD(&jd->extent_list);
-               jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1, NULL);
+               jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1);
                if (!jd->jd_inode || IS_ERR(jd->jd_inode)) {
                        if (!jd->jd_inode)
                                error = -ENOENT;
index dc4ec640e875974f0588ee60f3e478a4f0e4f7c6..7e19835efa2ea91675980a18e963281a02d46bb8 100644 (file)
@@ -511,8 +511,7 @@ void hfs_clear_inode(struct inode *inode)
        }
 }
 
-static int hfs_permission(struct inode *inode, int mask,
-                         struct nameidata *nd)
+static int hfs_permission(struct inode *inode, int mask)
 {
        if (S_ISREG(inode->i_mode) && mask & MAY_EXEC)
                return 0;
@@ -523,8 +522,6 @@ static int hfs_file_open(struct inode *inode, struct file *file)
 {
        if (HFS_IS_RSRC(inode))
                inode = HFS_I(inode)->rsrc_inode;
-       if (atomic_read(&file->f_count) != 1)
-               return 0;
        atomic_inc(&HFS_I(inode)->opencnt);
        return 0;
 }
@@ -535,8 +532,6 @@ static int hfs_file_release(struct inode *inode, struct file *file)
 
        if (HFS_IS_RSRC(inode))
                inode = HFS_I(inode)->rsrc_inode;
-       if (atomic_read(&file->f_count) != 0)
-               return 0;
        if (atomic_dec_and_test(&HFS_I(inode)->opencnt)) {
                mutex_lock(&inode->i_mutex);
                hfs_file_truncate(inode);
index cc3b5e24339b2089bf684cdafc7ebcee38ab928d..b085d64a2b672580e328be86af33475e87544f6e 100644 (file)
@@ -238,7 +238,7 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
        perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev);
 }
 
-static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int hfsplus_permission(struct inode *inode, int mask)
 {
        /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup,
         * open_exec has the same test, so it's still not executable, if a x bit
@@ -254,8 +254,6 @@ static int hfsplus_file_open(struct inode *inode, struct file *file)
 {
        if (HFSPLUS_IS_RSRC(inode))
                inode = HFSPLUS_I(inode).rsrc_inode;
-       if (atomic_read(&file->f_count) != 1)
-               return 0;
        atomic_inc(&HFSPLUS_I(inode).opencnt);
        return 0;
 }
@@ -266,8 +264,6 @@ static int hfsplus_file_release(struct inode *inode, struct file *file)
 
        if (HFSPLUS_IS_RSRC(inode))
                inode = HFSPLUS_I(inode).rsrc_inode;
-       if (atomic_read(&file->f_count) != 0)
-               return 0;
        if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) {
                mutex_lock(&inode->i_mutex);
                hfsplus_file_truncate(inode);
index 5222345ddccf6525a13cd74fe801da1bde50a304..d6ecabf4d231f2a9aa9f1cf5b5e1c061ea28cf6c 100644 (file)
@@ -822,7 +822,7 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
        return err;
 }
 
-int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
+int hostfs_permission(struct inode *ino, int desired)
 {
        char *name;
        int r = 0, w = 0, x = 0, err;
index d256559b4104c5ff90370a6d6690e40935c6f1b8..d9c59a775449ee9abfc27e24ed898ce17d05251a 100644 (file)
@@ -415,7 +415,7 @@ again:
                d_drop(dentry);
                spin_lock(&dentry->d_lock);
                if (atomic_read(&dentry->d_count) > 1 ||
-                   permission(inode, MAY_WRITE, NULL) ||
+                   generic_permission(inode, MAY_WRITE, NULL) ||
                    !S_ISREG(inode->i_mode) ||
                    get_write_access(inode)) {
                        spin_unlock(&dentry->d_lock);
index 65077aa90f0a74ea405d25ff5796fe3ef3442be1..2b3d1828db99a2a68a3e5fef961f0f6740cf04c8 100644 (file)
@@ -655,20 +655,13 @@ static void *hppfs_follow_link(struct dentry *dentry, struct nameidata *nd)
        return proc_dentry->d_inode->i_op->follow_link(proc_dentry, nd);
 }
 
-int hppfs_permission(struct inode *inode, int mask, struct nameidata *nd)
-{
-       return generic_permission(inode, mask, NULL);
-}
-
 static const struct inode_operations hppfs_dir_iops = {
        .lookup         = hppfs_lookup,
-       .permission     = hppfs_permission,
 };
 
 static const struct inode_operations hppfs_link_iops = {
        .readlink       = hppfs_readlink,
        .follow_link    = hppfs_follow_link,
-       .permission     = hppfs_permission,
 };
 
 static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
index fe79c25d95dc9c630649aec8485b784bbb34d49c..60249429a2530d11e6d5037e148d9a3c4fd00799 100644 (file)
@@ -354,20 +354,20 @@ static void inotify_dev_event_dequeue(struct inotify_device *dev)
 }
 
 /*
- * find_inode - resolve a user-given path to a specific inode and return a nd
+ * find_inode - resolve a user-given path to a specific inode
  */
-static int find_inode(const char __user *dirname, struct nameidata *nd,
+static int find_inode(const char __user *dirname, struct path *path,
                      unsigned flags)
 {
        int error;
 
-       error = __user_walk(dirname, flags, nd);
+       error = user_path_at(AT_FDCWD, dirname, flags, path);
        if (error)
                return error;
        /* you can only watch an inode if you have read permissions on it */
-       error = vfs_permission(nd, MAY_READ);
+       error = inode_permission(path->dentry->d_inode, MAY_READ);
        if (error)
-               path_put(&nd->path);
+               path_put(path);
        return error;
 }
 
@@ -650,11 +650,11 @@ asmlinkage long sys_inotify_init(void)
        return sys_inotify_init1(0);
 }
 
-asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
+asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask)
 {
        struct inode *inode;
        struct inotify_device *dev;
-       struct nameidata nd;
+       struct path path;
        struct file *filp;
        int ret, fput_needed;
        unsigned flags = 0;
@@ -674,12 +674,12 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
        if (mask & IN_ONLYDIR)
                flags |= LOOKUP_DIRECTORY;
 
-       ret = find_inode(path, &nd, flags);
+       ret = find_inode(pathname, &path, flags);
        if (unlikely(ret))
                goto fput_and_out;
 
-       /* inode held in place by reference to nd; dev by fget on fd */
-       inode = nd.path.dentry->d_inode;
+       /* inode held in place by reference to path; dev by fget on fd */
+       inode = path.dentry->d_inode;
        dev = filp->private_data;
 
        mutex_lock(&dev->up_mutex);
@@ -688,7 +688,7 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
                ret = create_watch(dev, inode, mask);
        mutex_unlock(&dev->up_mutex);
 
-       path_put(&nd.path);
+       path_put(&path);
 fput_and_out:
        fput_light(filp, fput_needed);
        return ret;
index 4c80404a9abae4b61e075135bb62441450596b11..d98713777a1b62f2aa12f659dd872616d2dbeb4a 100644 (file)
@@ -314,7 +314,7 @@ static int jffs2_check_acl(struct inode *inode, int mask)
        return -EAGAIN;
 }
 
-int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int jffs2_permission(struct inode *inode, int mask)
 {
        return generic_permission(inode, mask, jffs2_check_acl);
 }
index 0bb7f003fd80bb8b64909fedf420c3af327198c9..8ca058aed3840fa5f8c13c5b733127c473899739 100644 (file)
@@ -28,7 +28,7 @@ struct jffs2_acl_header {
 
 #define JFFS2_ACL_NOT_CACHED ((void *)-1)
 
-extern int jffs2_permission(struct inode *, int, struct nameidata *);
+extern int jffs2_permission(struct inode *, int);
 extern int jffs2_acl_chmod(struct inode *);
 extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *);
 extern int jffs2_init_acl_post(struct inode *);
index c0c141f6fde11c5e9367698494471e2462f181a3..cd219ef552543cdc98d0a392f95cd6dc1c3ec4fd 100644 (file)
@@ -38,7 +38,7 @@ const struct file_operations jffs2_dir_operations =
 {
        .read =         generic_read_dir,
        .readdir =      jffs2_readdir,
-       .ioctl =        jffs2_ioctl,
+       .unlocked_ioctl=jffs2_ioctl,
        .fsync =        jffs2_fsync
 };
 
index 5e920343b2c598595f2c068455e17d7d360c5a4f..5a98aa87c8530b169ec0453f977860550df73f38 100644 (file)
@@ -46,7 +46,7 @@ const struct file_operations jffs2_file_operations =
        .aio_read =     generic_file_aio_read,
        .write =        do_sync_write,
        .aio_write =    generic_file_aio_write,
-       .ioctl =        jffs2_ioctl,
+       .unlocked_ioctl=jffs2_ioctl,
        .mmap =         generic_file_readonly_mmap,
        .fsync =        jffs2_fsync,
        .splice_read =  generic_file_splice_read,
index e2177210f62153bec393482eb6f2e2b58b48f0eb..9d41f43e47bb29766bc6b309b7179e4d5cee759f 100644 (file)
@@ -12,8 +12,7 @@
 #include <linux/fs.h>
 #include "nodelist.h"
 
-int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
-               unsigned long arg)
+long jffs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
           will include compression support etc. */
index 2cc866cf134f18ce26bbecdd9b70c450cc075906..5e194a5c8e29c7166116bb8430d962dc4e029cc0 100644 (file)
@@ -167,7 +167,7 @@ int jffs2_fsync(struct file *, struct dentry *, int);
 int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
 
 /* ioctl.c */
-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+long jffs2_ioctl(struct file *, unsigned int, unsigned long);
 
 /* symlink.c */
 extern const struct inode_operations jffs2_symlink_inode_operations;
index 4d84bdc882999150a1e9bd7ae829f1aff24b33f2..d3e5c33665deceb13a0af0797a67b282129c3037 100644 (file)
@@ -140,7 +140,7 @@ static int jfs_check_acl(struct inode *inode, int mask)
        return -EAGAIN;
 }
 
-int jfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int jfs_permission(struct inode *inode, int mask)
 {
        return generic_permission(inode, mask, jfs_check_acl);
 }
index 455fa4292045559ea52202da59ae920dfe8d64ed..88475f10a3899dca4c6fa15eceffbbccb97cdab0 100644 (file)
@@ -20,7 +20,7 @@
 
 #ifdef CONFIG_JFS_POSIX_ACL
 
-int jfs_permission(struct inode *, int, struct nameidata *);
+int jfs_permission(struct inode *, int);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
 int jfs_setattr(struct dentry *, struct iattr *);
 
index 01e67dddcc3d2033a2038c37f9f791ba0921a781..a7b0a0b8012873dd1c8db6fe79c45b459096af41 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/device_cgroup.h>
-#include <asm/namei.h>
 #include <asm/uaccess.h>
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
@@ -185,6 +184,8 @@ int generic_permission(struct inode *inode, int mask,
 {
        umode_t                 mode = inode->i_mode;
 
+       mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
+
        if (current->fsuid == inode->i_uid)
                mode >>= 6;
        else {
@@ -203,7 +204,7 @@ int generic_permission(struct inode *inode, int mask,
        /*
         * If the DACs are ok we don't need any capability check.
         */
-       if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
+       if ((mask & ~mode) == 0)
                return 0;
 
  check_capabilities:
@@ -226,13 +227,9 @@ int generic_permission(struct inode *inode, int mask,
        return -EACCES;
 }
 
-int permission(struct inode *inode, int mask, struct nameidata *nd)
+int inode_permission(struct inode *inode, int mask)
 {
-       int retval, submask;
-       struct vfsmount *mnt = NULL;
-
-       if (nd)
-               mnt = nd->path.mnt;
+       int retval;
 
        if (mask & MAY_WRITE) {
                umode_t mode = inode->i_mode;
@@ -251,19 +248,9 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
                        return -EACCES;
        }
 
-       if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
-               /*
-                * MAY_EXEC on regular files is denied if the fs is mounted
-                * with the "noexec" flag.
-                */
-               if (mnt && (mnt->mnt_flags & MNT_NOEXEC))
-                       return -EACCES;
-       }
-
        /* Ordinary permission routines do not understand MAY_APPEND. */
-       submask = mask & ~MAY_APPEND;
        if (inode->i_op && inode->i_op->permission) {
-               retval = inode->i_op->permission(inode, submask, nd);
+               retval = inode->i_op->permission(inode, mask);
                if (!retval) {
                        /*
                         * Exec permission on a regular file is denied if none
@@ -277,7 +264,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
                                return -EACCES;
                }
        } else {
-               retval = generic_permission(inode, submask, NULL);
+               retval = generic_permission(inode, mask, NULL);
        }
        if (retval)
                return retval;
@@ -286,7 +273,8 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
        if (retval)
                return retval;
 
-       return security_inode_permission(inode, mask, nd);
+       return security_inode_permission(inode,
+                       mask & (MAY_READ|MAY_WRITE|MAY_EXEC));
 }
 
 /**
@@ -301,7 +289,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
  */
 int vfs_permission(struct nameidata *nd, int mask)
 {
-       return permission(nd->path.dentry->d_inode, mask, nd);
+       return inode_permission(nd->path.dentry->d_inode, mask);
 }
 
 /**
@@ -318,7 +306,7 @@ int vfs_permission(struct nameidata *nd, int mask)
  */
 int file_permission(struct file *file, int mask)
 {
-       return permission(file->f_path.dentry->d_inode, mask, NULL);
+       return inode_permission(file->f_path.dentry->d_inode, mask);
 }
 
 /*
@@ -459,8 +447,7 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name,
  * short-cut DAC fails, then call permission() to do more
  * complete permission check.
  */
-static int exec_permission_lite(struct inode *inode,
-                                      struct nameidata *nd)
+static int exec_permission_lite(struct inode *inode)
 {
        umode_t mode = inode->i_mode;
 
@@ -486,7 +473,7 @@ static int exec_permission_lite(struct inode *inode,
 
        return -EACCES;
 ok:
-       return security_inode_permission(inode, MAY_EXEC, nd);
+       return security_inode_permission(inode, MAY_EXEC);
 }
 
 /*
@@ -519,7 +506,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
         */
        result = d_lookup(parent, name);
        if (!result) {
-               struct dentry * dentry = d_alloc(parent, name);
+               struct dentry *dentry;
+
+               /* Don't create child dentry for a dead directory. */
+               result = ERR_PTR(-ENOENT);
+               if (IS_DEADDIR(dir))
+                       goto out_unlock;
+
+               dentry = d_alloc(parent, name);
                result = ERR_PTR(-ENOMEM);
                if (dentry) {
                        result = dir->i_op->lookup(dir, dentry, nd);
@@ -528,6 +522,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
                        else
                                result = dentry;
                }
+out_unlock:
                mutex_unlock(&dir->i_mutex);
                return result;
        }
@@ -545,27 +540,16 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, s
        return result;
 }
 
-static int __emul_lookup_dentry(const char *, struct nameidata *);
-
 /* SMP-safe */
-static __always_inline int
+static __always_inline void
 walk_init_root(const char *name, struct nameidata *nd)
 {
        struct fs_struct *fs = current->fs;
 
        read_lock(&fs->lock);
-       if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
-               nd->path = fs->altroot;
-               path_get(&fs->altroot);
-               read_unlock(&fs->lock);
-               if (__emul_lookup_dentry(name,nd))
-                       return 0;
-               read_lock(&fs->lock);
-       }
        nd->path = fs->root;
        path_get(&fs->root);
        read_unlock(&fs->lock);
-       return 1;
 }
 
 /*
@@ -606,12 +590,9 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
 
        if (*link == '/') {
                path_put(&nd->path);
-               if (!walk_init_root(link, nd))
-                       /* weird __emul_prefix() stuff did it */
-                       goto out;
+               walk_init_root(link, nd);
        }
        res = link_path_walk(link, nd);
-out:
        if (nd->depth || res || nd->last_type!=LAST_NORM)
                return res;
        /*
@@ -889,7 +870,7 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                unsigned int c;
 
                nd->flags |= LOOKUP_CONTINUE;
-               err = exec_permission_lite(inode, nd);
+               err = exec_permission_lite(inode);
                if (err == -EAGAIN)
                        err = vfs_permission(nd, MAY_EXEC);
                if (err)
@@ -1060,67 +1041,6 @@ static int path_walk(const char *name, struct nameidata *nd)
        return link_path_walk(name, nd);
 }
 
-/* 
- * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if
- * everything is done. Returns 0 and drops input nd, if lookup failed;
- */
-static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
-{
-       if (path_walk(name, nd))
-               return 0;               /* something went wrong... */
-
-       if (!nd->path.dentry->d_inode ||
-           S_ISDIR(nd->path.dentry->d_inode->i_mode)) {
-               struct path old_path = nd->path;
-               struct qstr last = nd->last;
-               int last_type = nd->last_type;
-               struct fs_struct *fs = current->fs;
-
-               /*
-                * NAME was not found in alternate root or it's a directory.
-                * Try to find it in the normal root:
-                */
-               nd->last_type = LAST_ROOT;
-               read_lock(&fs->lock);
-               nd->path = fs->root;
-               path_get(&fs->root);
-               read_unlock(&fs->lock);
-               if (path_walk(name, nd) == 0) {
-                       if (nd->path.dentry->d_inode) {
-                               path_put(&old_path);
-                               return 1;
-                       }
-                       path_put(&nd->path);
-               }
-               nd->path = old_path;
-               nd->last = last;
-               nd->last_type = last_type;
-       }
-       return 1;
-}
-
-void set_fs_altroot(void)
-{
-       char *emul = __emul_prefix();
-       struct nameidata nd;
-       struct path path = {}, old_path;
-       int err;
-       struct fs_struct *fs = current->fs;
-
-       if (!emul)
-               goto set_it;
-       err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
-       if (!err)
-               path = nd.path;
-set_it:
-       write_lock(&fs->lock);
-       old_path = fs->altroot;
-       fs->altroot = path;
-       write_unlock(&fs->lock);
-       if (old_path.dentry)
-               path_put(&old_path);
-}
-
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
 static int do_path_lookup(int dfd, const char *name,
                                unsigned int flags, struct nameidata *nd)
@@ -1136,14 +1056,6 @@ static int do_path_lookup(int dfd, const char *name,
 
        if (*name=='/') {
                read_lock(&fs->lock);
-               if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
-                       nd->path = fs->altroot;
-                       path_get(&fs->altroot);
-                       read_unlock(&fs->lock);
-                       if (__emul_lookup_dentry(name,nd))
-                               goto out; /* found in altroot */
-                       read_lock(&fs->lock);
-               }
                nd->path = fs->root;
                path_get(&fs->root);
                read_unlock(&fs->lock);
@@ -1177,7 +1089,6 @@ static int do_path_lookup(int dfd, const char *name,
        }
 
        retval = path_walk(name, nd);
-out:
        if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
                                nd->path.dentry->d_inode))
                audit_inode(name, nd->path.dentry);
@@ -1282,19 +1193,6 @@ static int path_lookup_create(int dfd, const char *name,
                        nd, open_flags, create_mode);
 }
 
-int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
-               struct nameidata *nd, int open_flags)
-{
-       char *tmp = getname(name);
-       int err = PTR_ERR(tmp);
-
-       if (!IS_ERR(tmp)) {
-               err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0);
-               putname(tmp);
-       }
-       return err;
-}
-
 static struct dentry *__lookup_hash(struct qstr *name,
                struct dentry *base, struct nameidata *nd)
 {
@@ -1317,7 +1215,14 @@ static struct dentry *__lookup_hash(struct qstr *name,
 
        dentry = cached_lookup(base, name, nd);
        if (!dentry) {
-               struct dentry *new = d_alloc(base, name);
+               struct dentry *new;
+
+               /* Don't create child dentry for a dead directory. */
+               dentry = ERR_PTR(-ENOENT);
+               if (IS_DEADDIR(inode))
+                       goto out;
+
+               new = d_alloc(base, name);
                dentry = ERR_PTR(-ENOMEM);
                if (!new)
                        goto out;
@@ -1340,7 +1245,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
 {
        int err;
 
-       err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd);
+       err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
        return __lookup_hash(&nd->last, nd->path.dentry, nd);
@@ -1388,7 +1293,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
        if (err)
                return ERR_PTR(err);
 
-       err = permission(base->d_inode, MAY_EXEC, NULL);
+       err = inode_permission(base->d_inode, MAY_EXEC);
        if (err)
                return ERR_PTR(err);
        return __lookup_hash(&this, base, NULL);
@@ -1416,22 +1321,40 @@ struct dentry *lookup_one_noperm(const char *name, struct dentry *base)
        return __lookup_hash(&this, base, NULL);
 }
 
-int __user_walk_fd(int dfd, const char __user *name, unsigned flags,
-                           struct nameidata *nd)
+int user_path_at(int dfd, const char __user *name, unsigned flags,
+                struct path *path)
 {
+       struct nameidata nd;
        char *tmp = getname(name);
        int err = PTR_ERR(tmp);
-
        if (!IS_ERR(tmp)) {
-               err = do_path_lookup(dfd, tmp, flags, nd);
+
+               BUG_ON(flags & LOOKUP_PARENT);
+
+               err = do_path_lookup(dfd, tmp, flags, &nd);
                putname(tmp);
+               if (!err)
+                       *path = nd.path;
        }
        return err;
 }
 
-int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
+static int user_path_parent(int dfd, const char __user *path,
+                       struct nameidata *nd, char **name)
 {
-       return __user_walk_fd(AT_FDCWD, name, flags, nd);
+       char *s = getname(path);
+       int error;
+
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+
+       error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd);
+       if (error)
+               putname(s);
+       else
+               *name = s;
+
+       return error;
 }
 
 /*
@@ -1478,7 +1401,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
        BUG_ON(victim->d_parent->d_inode != dir);
        audit_inode_child(victim->d_name.name, victim, dir);
 
-       error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
        if (error)
                return error;
        if (IS_APPEND(dir))
@@ -1515,7 +1438,7 @@ static inline int may_create(struct inode *dir, struct dentry *child,
                return -EEXIST;
        if (IS_DEADDIR(dir))
                return -ENOENT;
-       return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+       return inode_permission(dir, MAY_WRITE | MAY_EXEC);
 }
 
 /* 
@@ -1755,7 +1678,7 @@ struct file *do_filp_open(int dfd, const char *pathname,
        int will_write;
        int flag = open_to_namei_flags(open_flag);
 
-       acc_mode = ACC_MODE(flag);
+       acc_mode = MAY_OPEN | ACC_MODE(flag);
 
        /* O_TRUNC implies we need access checks for write permissions */
        if (flag & O_TRUNC)
@@ -2071,20 +1994,18 @@ static int may_mknod(mode_t mode)
 asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                                unsigned dev)
 {
-       int error = 0;
-       char * tmp;
-       struct dentry * dentry;
+       int error;
+       char *tmp;
+       struct dentry *dentry;
        struct nameidata nd;
 
        if (S_ISDIR(mode))
                return -EPERM;
-       tmp = getname(filename);
-       if (IS_ERR(tmp))
-               return PTR_ERR(tmp);
 
-       error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
+       error = user_path_parent(dfd, filename, &nd, &tmp);
        if (error)
-               goto out;
+               return error;
+
        dentry = lookup_create(&nd, 0);
        if (IS_ERR(dentry)) {
                error = PTR_ERR(dentry);
@@ -2116,7 +2037,6 @@ out_dput:
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
-out:
        putname(tmp);
 
        return error;
@@ -2156,14 +2076,10 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
        struct dentry *dentry;
        struct nameidata nd;
 
-       tmp = getname(pathname);
-       error = PTR_ERR(tmp);
-       if (IS_ERR(tmp))
+       error = user_path_parent(dfd, pathname, &nd, &tmp);
+       if (error)
                goto out_err;
 
-       error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
-       if (error)
-               goto out;
        dentry = lookup_create(&nd, 1);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
@@ -2181,7 +2097,6 @@ out_dput:
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
-out:
        putname(tmp);
 out_err:
        return error;
@@ -2259,13 +2174,9 @@ static long do_rmdir(int dfd, const char __user *pathname)
        struct dentry *dentry;
        struct nameidata nd;
 
-       name = getname(pathname);
-       if(IS_ERR(name))
-               return PTR_ERR(name);
-
-       error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
+       error = user_path_parent(dfd, pathname, &nd, &name);
        if (error)
-               goto exit;
+               return error;
 
        switch(nd.last_type) {
                case LAST_DOTDOT:
@@ -2294,7 +2205,6 @@ exit2:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 exit1:
        path_put(&nd.path);
-exit:
        putname(name);
        return error;
 }
@@ -2343,19 +2253,16 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
  */
 static long do_unlinkat(int dfd, const char __user *pathname)
 {
-       int error = 0;
-       char * name;
+       int error;
+       char *name;
        struct dentry *dentry;
        struct nameidata nd;
        struct inode *inode = NULL;
 
-       name = getname(pathname);
-       if(IS_ERR(name))
-               return PTR_ERR(name);
-
-       error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
+       error = user_path_parent(dfd, pathname, &nd, &name);
        if (error)
-               goto exit;
+               return error;
+
        error = -EISDIR;
        if (nd.last_type != LAST_NORM)
                goto exit1;
@@ -2382,7 +2289,6 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                iput(inode);    /* truncate the inode here */
 exit1:
        path_put(&nd.path);
-exit:
        putname(name);
        return error;
 
@@ -2408,7 +2314,7 @@ asmlinkage long sys_unlink(const char __user *pathname)
        return do_unlinkat(AT_FDCWD, pathname);
 }
 
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 {
        int error = may_create(dir, dentry, NULL);
 
@@ -2432,23 +2338,20 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
 asmlinkage long sys_symlinkat(const char __user *oldname,
                              int newdfd, const char __user *newname)
 {
-       int error = 0;
-       char * from;
-       char * to;
+       int error;
+       char *from;
+       char *to;
        struct dentry *dentry;
        struct nameidata nd;
 
        from = getname(oldname);
-       if(IS_ERR(from))
+       if (IS_ERR(from))
                return PTR_ERR(from);
-       to = getname(newname);
-       error = PTR_ERR(to);
-       if (IS_ERR(to))
-               goto out_putname;
 
-       error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+       error = user_path_parent(newdfd, newname, &nd, &to);
        if (error)
-               goto out;
+               goto out_putname;
+
        dentry = lookup_create(&nd, 0);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
@@ -2457,14 +2360,13 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
-       error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
+       error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
-out:
        putname(to);
 out_putname:
        putname(from);
@@ -2498,19 +2400,19 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
                return -EPERM;
        if (!dir->i_op || !dir->i_op->link)
                return -EPERM;
-       if (S_ISDIR(old_dentry->d_inode->i_mode))
+       if (S_ISDIR(inode->i_mode))
                return -EPERM;
 
        error = security_inode_link(old_dentry, dir, new_dentry);
        if (error)
                return error;
 
-       mutex_lock(&old_dentry->d_inode->i_mutex);
+       mutex_lock(&inode->i_mutex);
        DQUOT_INIT(dir);
        error = dir->i_op->link(old_dentry, dir, new_dentry);
-       mutex_unlock(&old_dentry->d_inode->i_mutex);
+       mutex_unlock(&inode->i_mutex);
        if (!error)
-               fsnotify_link(dir, old_dentry->d_inode, new_dentry);
+               fsnotify_link(dir, inode, new_dentry);
        return error;
 }
 
@@ -2528,27 +2430,25 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
                           int flags)
 {
        struct dentry *new_dentry;
-       struct nameidata nd, old_nd;
+       struct nameidata nd;
+       struct path old_path;
        int error;
-       char * to;
+       char *to;
 
        if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
                return -EINVAL;
 
-       to = getname(newname);
-       if (IS_ERR(to))
-               return PTR_ERR(to);
-
-       error = __user_walk_fd(olddfd, oldname,
-                              flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
-                              &old_nd);
+       error = user_path_at(olddfd, oldname,
+                            flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
+                            &old_path);
        if (error)
-               goto exit;
-       error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+               return error;
+
+       error = user_path_parent(newdfd, newname, &nd, &to);
        if (error)
                goto out;
        error = -EXDEV;
-       if (old_nd.path.mnt != nd.path.mnt)
+       if (old_path.mnt != nd.path.mnt)
                goto out_release;
        new_dentry = lookup_create(&nd, 0);
        error = PTR_ERR(new_dentry);
@@ -2557,7 +2457,7 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
        error = mnt_want_write(nd.path.mnt);
        if (error)
                goto out_dput;
-       error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
+       error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
        mnt_drop_write(nd.path.mnt);
 out_dput:
        dput(new_dentry);
@@ -2565,10 +2465,9 @@ out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 out_release:
        path_put(&nd.path);
-out:
-       path_put(&old_nd.path);
-exit:
        putname(to);
+out:
+       path_put(&old_path);
 
        return error;
 }
@@ -2621,7 +2520,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
         * we'll need to flip '..'.
         */
        if (new_dir != old_dir) {
-               error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
+               error = inode_permission(old_dentry->d_inode, MAY_WRITE);
                if (error)
                        return error;
        }
@@ -2724,20 +2623,22 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        return error;
 }
 
-static int do_rename(int olddfd, const char *oldname,
-                       int newdfd, const char *newname)
+asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
+                            int newdfd, const char __user *newname)
 {
-       int error = 0;
-       struct dentry * old_dir, * new_dir;
-       struct dentry * old_dentry, *new_dentry;
-       struct dentry * trap;
+       struct dentry *old_dir, *new_dir;
+       struct dentry *old_dentry, *new_dentry;
+       struct dentry *trap;
        struct nameidata oldnd, newnd;
+       char *from;
+       char *to;
+       int error;
 
-       error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
+       error = user_path_parent(olddfd, oldname, &oldnd, &from);
        if (error)
                goto exit;
 
-       error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd);
+       error = user_path_parent(newdfd, newname, &newnd, &to);
        if (error)
                goto exit1;
 
@@ -2799,29 +2700,11 @@ exit3:
        unlock_rename(new_dir, old_dir);
 exit2:
        path_put(&newnd.path);
+       putname(to);
 exit1:
        path_put(&oldnd.path);
-exit:
-       return error;
-}
-
-asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
-                            int newdfd, const char __user *newname)
-{
-       int error;
-       char * from;
-       char * to;
-
-       from = getname(oldname);
-       if(IS_ERR(from))
-               return PTR_ERR(from);
-       to = getname(newname);
-       error = PTR_ERR(to);
-       if (!IS_ERR(to)) {
-               error = do_rename(olddfd, from, newdfd, to);
-               putname(to);
-       }
        putname(from);
+exit:
        return error;
 }
 
@@ -2959,8 +2842,7 @@ const struct inode_operations page_symlink_inode_operations = {
        .put_link       = page_put_link,
 };
 
-EXPORT_SYMBOL(__user_walk);
-EXPORT_SYMBOL(__user_walk_fd);
+EXPORT_SYMBOL(user_path_at);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
@@ -2975,7 +2857,7 @@ EXPORT_SYMBOL(page_symlink);
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(path_lookup);
 EXPORT_SYMBOL(vfs_path_lookup);
-EXPORT_SYMBOL(permission);
+EXPORT_SYMBOL(inode_permission);
 EXPORT_SYMBOL(vfs_permission);
 EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);
index f30b11e2240e170e8d6dfe7fd6a395e02344f212..411728c0c8bb2b0ee1248d95106c32c1ad4ef36b 100644 (file)
@@ -112,9 +112,13 @@ struct vfsmount *alloc_vfsmnt(const char *name)
                int err;
 
                err = mnt_alloc_id(mnt);
-               if (err) {
-                       kmem_cache_free(mnt_cache, mnt);
-                       return NULL;
+               if (err)
+                       goto out_free_cache;
+
+               if (name) {
+                       mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
+                       if (!mnt->mnt_devname)
+                               goto out_free_id;
                }
 
                atomic_set(&mnt->mnt_count, 1);
@@ -127,16 +131,14 @@ struct vfsmount *alloc_vfsmnt(const char *name)
                INIT_LIST_HEAD(&mnt->mnt_slave_list);
                INIT_LIST_HEAD(&mnt->mnt_slave);
                atomic_set(&mnt->__mnt_writers, 0);
-               if (name) {
-                       int size = strlen(name) + 1;
-                       char *newname = kmalloc(size, GFP_KERNEL);
-                       if (newname) {
-                               memcpy(newname, name, size);
-                               mnt->mnt_devname = newname;
-                       }
-               }
        }
        return mnt;
+
+out_free_id:
+       mnt_free_id(mnt);
+out_free_cache:
+       kmem_cache_free(mnt_cache, mnt);
+       return NULL;
 }
 
 /*
@@ -1128,27 +1130,27 @@ static int do_umount(struct vfsmount *mnt, int flags)
 
 asmlinkage long sys_umount(char __user * name, int flags)
 {
-       struct nameidata nd;
+       struct path path;
        int retval;
 
-       retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
+       retval = user_path(name, &path);
        if (retval)
                goto out;
        retval = -EINVAL;
-       if (nd.path.dentry != nd.path.mnt->mnt_root)
+       if (path.dentry != path.mnt->mnt_root)
                goto dput_and_out;
-       if (!check_mnt(nd.path.mnt))
+       if (!check_mnt(path.mnt))
                goto dput_and_out;
 
        retval = -EPERM;
        if (!capable(CAP_SYS_ADMIN))
                goto dput_and_out;
 
-       retval = do_umount(nd.path.mnt, flags);
+       retval = do_umount(path.mnt, flags);
 dput_and_out:
        /* we mustn't call path_put() as that would clear mnt_expiry_mark */
-       dput(nd.path.dentry);
-       mntput_no_expire(nd.path.mnt);
+       dput(path.dentry);
+       mntput_no_expire(path.mnt);
 out:
        return retval;
 }
@@ -1972,7 +1974,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
                struct fs_struct *fs)
 {
        struct mnt_namespace *new_ns;
-       struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
+       struct vfsmount *rootmnt = NULL, *pwdmnt = NULL;
        struct vfsmount *p, *q;
 
        new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
@@ -2015,10 +2017,6 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
                                pwdmnt = p;
                                fs->pwd.mnt = mntget(q);
                        }
-                       if (p == fs->altroot.mnt) {
-                               altrootmnt = p;
-                               fs->altroot.mnt = mntget(q);
-                       }
                }
                p = next_mnt(p, mnt_ns->root);
                q = next_mnt(q, new_ns->root);
@@ -2029,8 +2027,6 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
                mntput(rootmnt);
        if (pwdmnt)
                mntput(pwdmnt);
-       if (altrootmnt)
-               mntput(altrootmnt);
 
        return new_ns;
 }
@@ -2183,28 +2179,26 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
                               const char __user * put_old)
 {
        struct vfsmount *tmp;
-       struct nameidata new_nd, old_nd;
-       struct path parent_path, root_parent, root;
+       struct path new, old, parent_path, root_parent, root;
        int error;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
-       error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
-                           &new_nd);
+       error = user_path_dir(new_root, &new);
        if (error)
                goto out0;
        error = -EINVAL;
-       if (!check_mnt(new_nd.path.mnt))
+       if (!check_mnt(new.mnt))
                goto out1;
 
-       error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd);
+       error = user_path_dir(put_old, &old);
        if (error)
                goto out1;
 
-       error = security_sb_pivotroot(&old_nd.path, &new_nd.path);
+       error = security_sb_pivotroot(&old, &new);
        if (error) {
-               path_put(&old_nd.path);
+               path_put(&old);
                goto out1;
        }
 
@@ -2213,69 +2207,69 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
        path_get(&current->fs->root);
        read_unlock(&current->fs->lock);
        down_write(&namespace_sem);
-       mutex_lock(&old_nd.path.dentry->d_inode->i_mutex);
+       mutex_lock(&old.dentry->d_inode->i_mutex);
        error = -EINVAL;
-       if (IS_MNT_SHARED(old_nd.path.mnt) ||
-               IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) ||
+       if (IS_MNT_SHARED(old.mnt) ||
+               IS_MNT_SHARED(new.mnt->mnt_parent) ||
                IS_MNT_SHARED(root.mnt->mnt_parent))
                goto out2;
        if (!check_mnt(root.mnt))
                goto out2;
        error = -ENOENT;
-       if (IS_DEADDIR(new_nd.path.dentry->d_inode))
+       if (IS_DEADDIR(new.dentry->d_inode))
                goto out2;
-       if (d_unhashed(new_nd.path.dentry) && !IS_ROOT(new_nd.path.dentry))
+       if (d_unhashed(new.dentry) && !IS_ROOT(new.dentry))
                goto out2;
-       if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry))
+       if (d_unhashed(old.dentry) && !IS_ROOT(old.dentry))
                goto out2;
        error = -EBUSY;
-       if (new_nd.path.mnt == root.mnt ||
-           old_nd.path.mnt == root.mnt)
+       if (new.mnt == root.mnt ||
+           old.mnt == root.mnt)
                goto out2; /* loop, on the same file system  */
        error = -EINVAL;
        if (root.mnt->mnt_root != root.dentry)
                goto out2; /* not a mountpoint */
        if (root.mnt->mnt_parent == root.mnt)
                goto out2; /* not attached */
-       if (new_nd.path.mnt->mnt_root != new_nd.path.dentry)
+       if (new.mnt->mnt_root != new.dentry)
                goto out2; /* not a mountpoint */
-       if (new_nd.path.mnt->mnt_parent == new_nd.path.mnt)
+       if (new.mnt->mnt_parent == new.mnt)
                goto out2; /* not attached */
        /* make sure we can reach put_old from new_root */
-       tmp = old_nd.path.mnt;
+       tmp = old.mnt;
        spin_lock(&vfsmount_lock);
-       if (tmp != new_nd.path.mnt) {
+       if (tmp != new.mnt) {
                for (;;) {
                        if (tmp->mnt_parent == tmp)
                                goto out3; /* already mounted on put_old */
-                       if (tmp->mnt_parent == new_nd.path.mnt)
+                       if (tmp->mnt_parent == new.mnt)
                                break;
                        tmp = tmp->mnt_parent;
                }
-               if (!is_subdir(tmp->mnt_mountpoint, new_nd.path.dentry))
+               if (!is_subdir(tmp->mnt_mountpoint, new.dentry))
                        goto out3;
-       } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry))
+       } else if (!is_subdir(old.dentry, new.dentry))
                goto out3;
-       detach_mnt(new_nd.path.mnt, &parent_path);
+       detach_mnt(new.mnt, &parent_path);
        detach_mnt(root.mnt, &root_parent);
        /* mount old root on put_old */
-       attach_mnt(root.mnt, &old_nd.path);
+       attach_mnt(root.mnt, &old);
        /* mount new_root on / */
-       attach_mnt(new_nd.path.mnt, &root_parent);
+       attach_mnt(new.mnt, &root_parent);
        touch_mnt_namespace(current->nsproxy->mnt_ns);
        spin_unlock(&vfsmount_lock);
-       chroot_fs_refs(&root, &new_nd.path);
-       security_sb_post_pivotroot(&root, &new_nd.path);
+       chroot_fs_refs(&root, &new);
+       security_sb_post_pivotroot(&root, &new);
        error = 0;
        path_put(&root_parent);
        path_put(&parent_path);
 out2:
-       mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex);
+       mutex_unlock(&old.dentry->d_inode->i_mutex);
        up_write(&namespace_sem);
        path_put(&root);
-       path_put(&old_nd.path);
+       path_put(&old);
 out1:
-       path_put(&new_nd.path);
+       path_put(&new);
 out0:
        return error;
 out3:
index 011ef0b6d2d48eb9fcc391fc9180ceb4d34d4f6a..07e9715b865887042b899299effe1c4f37a7e106 100644 (file)
@@ -266,7 +266,7 @@ leave_me:;
 
 
 static int
-__ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd)
+__ncp_lookup_validate(struct dentry *dentry)
 {
        struct ncp_server *server;
        struct dentry *parent;
@@ -340,7 +340,7 @@ ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd)
 {
        int res;
        lock_kernel();
-       res = __ncp_lookup_validate(dentry, nd);
+       res = __ncp_lookup_validate(dentry);
        unlock_kernel();
        return res;
 }
index 28a238dab23a2a16919ef58706a431447bcc8f5b..74f92b717f783ca1974094beabb879ea1adde0cc 100644 (file)
@@ -1884,7 +1884,7 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
                return status;
        nfs_access_add_cache(inode, &cache);
 out:
-       if ((cache.mask & mask) == mask)
+       if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
                return 0;
        return -EACCES;
 }
@@ -1907,17 +1907,17 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
        return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
 }
 
-int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int nfs_permission(struct inode *inode, int mask)
 {
        struct rpc_cred *cred;
        int res = 0;
 
        nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
-       if (mask == 0)
+       if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
                goto out;
        /* Is this sys_access() ? */
-       if (nd != NULL && (nd->flags & LOOKUP_ACCESS))
+       if (mask & MAY_ACCESS)
                goto force_lookup;
 
        switch (inode->i_mode & S_IFMT) {
@@ -1926,8 +1926,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
                case S_IFREG:
                        /* NFSv4 has atomic_open... */
                        if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
-                                       && nd != NULL
-                                       && (nd->flags & LOOKUP_OPEN))
+                                       && (mask & MAY_OPEN))
                                goto out;
                        break;
                case S_IFDIR:
index 1955a2702e60a473cad46549d2e58298c5c767c7..c53e65f8f3a2b460d6c01424094b29c8082843a4 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/time.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/fcntl.h>
 #include <linux/net.h>
 #include <linux/in.h>
index f45451eb1e383b6bb1a353c5c7a2ed2e2c9d0468..ea37c96f04454e7f1513ca1222aa4eba86f98fb0 100644 (file)
@@ -51,7 +51,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
                /* make sure parents give x permission to user */
                int err;
                parent = dget_parent(tdentry);
-               err = permission(parent->d_inode, MAY_EXEC, NULL);
+               err = inode_permission(parent->d_inode, MAY_EXEC);
                if (err < 0) {
                        dput(parent);
                        break;
index 0f4481e0502d132b624e952f3c2cbad50163c4ab..18060bed5267451c005345ef19f5a270ee5143aa 100644 (file)
@@ -1516,7 +1516,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
        struct dentry   *dentry, *dnew;
        __be32          err, cerr;
        int             host_err;
-       umode_t         mode;
 
        err = nfserr_noent;
        if (!flen || !plen)
@@ -1535,11 +1534,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (IS_ERR(dnew))
                goto out_nfserr;
 
-       mode = S_IALLUGO;
-       /* Only the MODE ATTRibute is even vaguely meaningful */
-       if (iap && (iap->ia_valid & ATTR_MODE))
-               mode = iap->ia_mode & S_IALLUGO;
-
        host_err = mnt_want_write(fhp->fh_export->ex_path.mnt);
        if (host_err)
                goto out_nfserr;
@@ -1551,11 +1545,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
                else {
                        strncpy(path_alloced, path, plen);
                        path_alloced[plen] = 0;
-                       host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode);
+                       host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
                        kfree(path_alloced);
                }
        } else
-               host_err = vfs_symlink(dentry->d_inode, dnew, path, mode);
+               host_err = vfs_symlink(dentry->d_inode, dnew, path);
 
        if (!host_err) {
                if (EX_ISSYNC(fhp->fh_export))
@@ -1959,12 +1953,12 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
                return 0;
 
        /* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
-       err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL);
+       err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
 
        /* Allow read access to binaries even when mode 111 */
        if (err == -EACCES && S_ISREG(inode->i_mode) &&
            acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE))
-               err = permission(inode, MAY_EXEC, NULL);
+               err = inode_permission(inode, MAY_EXEC);
 
        return err? nfserrno(err) : 0;
 }
index 3c5550cd11d67b53a5d3ef6ff49dd6d4312f9d4c..d020866d42320dddd0ed6a8b9a9d31e572a42cab 100644 (file)
@@ -2118,7 +2118,7 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
                goto out;
        if (!count)
                goto out;
-       err = remove_suid(file->f_path.dentry);
+       err = file_remove_suid(file);
        if (err)
                goto out;
        file_update_time(file);
index e8514e8b6ce8977eb3f75d3a64a7b8f33f96726a..be2dd95d3a1dc0c916229ad4deb643ed0ee841b0 100644 (file)
@@ -1176,7 +1176,7 @@ bail:
        return err;
 }
 
-int ocfs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int ocfs2_permission(struct inode *inode, int mask)
 {
        int ret;
 
index 048ddcaf5c80e9b2bb76b1e06d3f6ddb340856c8..1e27b4d017ea49401ec43445a29d62bcd6b219ce 100644 (file)
@@ -62,8 +62,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat);
-int ocfs2_permission(struct inode *inode, int mask,
-                    struct nameidata *nd);
+int ocfs2_permission(struct inode *inode, int mask);
 
 int ocfs2_should_update_atime(struct inode *inode,
                              struct vfsmount *vfsmnt);
index bb98d2fe809f09fa382a9d6224231206e1161962..52647be277a2c6bbe7e6ba5a9e0603d07ad33c71 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -122,37 +122,37 @@ static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf)
        return 0;
 }
 
-asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
+asmlinkage long sys_statfs(const char __user *pathname, struct statfs __user * buf)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct statfs tmp;
-               error = vfs_statfs_native(nd.path.dentry, &tmp);
+               error = vfs_statfs_native(path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
 
 
-asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf)
+asmlinkage long sys_statfs64(const char __user *pathname, size_t sz, struct statfs64 __user *buf)
 {
-       struct nameidata nd;
+       struct path path;
        long error;
 
        if (sz != sizeof(*buf))
                return -EINVAL;
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (!error) {
                struct statfs64 tmp;
-               error = vfs_statfs64(nd.path.dentry, &tmp);
+               error = vfs_statfs64(path.dentry, &tmp);
                if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
                        error = -EFAULT;
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
@@ -223,20 +223,20 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
        return err;
 }
 
-static long do_sys_truncate(const char __user * path, loff_t length)
+static long do_sys_truncate(const char __user *pathname, loff_t length)
 {
-       struct nameidata nd;
-       struct inode * inode;
+       struct path path;
+       struct inode *inode;
        int error;
 
        error = -EINVAL;
        if (length < 0) /* sorry, but loff_t says... */
                goto out;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (error)
                goto out;
-       inode = nd.path.dentry->d_inode;
+       inode = path.dentry->d_inode;
 
        /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
        error = -EISDIR;
@@ -247,16 +247,16 @@ static long do_sys_truncate(const char __user * path, loff_t length)
        if (!S_ISREG(inode->i_mode))
                goto dput_and_out;
 
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto dput_and_out;
 
-       error = vfs_permission(&nd, MAY_WRITE);
+       error = inode_permission(inode, MAY_WRITE);
        if (error)
                goto mnt_drop_write_and_out;
 
        error = -EPERM;
-       if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+       if (IS_APPEND(inode))
                goto mnt_drop_write_and_out;
 
        error = get_write_access(inode);
@@ -274,15 +274,15 @@ static long do_sys_truncate(const char __user * path, loff_t length)
        error = locks_verify_truncate(inode, NULL, length);
        if (!error) {
                DQUOT_INIT(inode);
-               error = do_truncate(nd.path.dentry, length, 0, NULL);
+               error = do_truncate(path.dentry, length, 0, NULL);
        }
 
 put_write_and_out:
        put_write_access(inode);
 mnt_drop_write_and_out:
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(path.mnt);
 dput_and_out:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
@@ -425,7 +425,8 @@ out:
  */
 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 {
-       struct nameidata nd;
+       struct path path;
+       struct inode *inode;
        int old_fsuid, old_fsgid;
        kernel_cap_t uninitialized_var(old_cap);  /* !SECURE_NO_SETUID_FIXUP */
        int res;
@@ -448,7 +449,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
                 * FIXME: There is a race here against sys_capset.  The
                 * capabilities can change yet we will restore the old
                 * value below.  We should hold task_capabilities_lock,
-                * but we cannot because user_path_walk can sleep.
+                * but we cannot because user_path_at can sleep.
                 */
 #endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */
                if (current->uid)
@@ -457,14 +458,25 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
                        old_cap = cap_set_effective(current->cap_permitted);
        }
 
-       res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
+       res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
        if (res)
                goto out;
 
-       res = vfs_permission(&nd, mode);
+       inode = path.dentry->d_inode;
+
+       if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
+               /*
+                * MAY_EXEC on regular files is denied if the fs is mounted
+                * with the "noexec" flag.
+                */
+               res = -EACCES;
+               if (path.mnt->mnt_flags & MNT_NOEXEC)
+                       goto out_path_release;
+       }
+
+       res = inode_permission(inode, mode | MAY_ACCESS);
        /* SuS v2 requires we report a read only fs too */
-       if(res || !(mode & S_IWOTH) ||
-          special_file(nd.path.dentry->d_inode->i_mode))
+       if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
                goto out_path_release;
        /*
         * This is a rare case where using __mnt_is_readonly()
@@ -476,11 +488,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
         * inherently racy and know that the fs may change
         * state before we even see this result.
         */
-       if (__mnt_is_readonly(nd.path.mnt))
+       if (__mnt_is_readonly(path.mnt))
                res = -EROFS;
 
 out_path_release:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        current->fsuid = old_fsuid;
        current->fsgid = old_fsgid;
@@ -498,22 +510,21 @@ asmlinkage long sys_access(const char __user *filename, int mode)
 
 asmlinkage long sys_chdir(const char __user * filename)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = __user_walk(filename,
-                           LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
+       error = user_path_dir(filename, &path);
        if (error)
                goto out;
 
-       error = vfs_permission(&nd, MAY_EXEC);
+       error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
        if (error)
                goto dput_and_out;
 
-       set_fs_pwd(current->fs, &nd.path);
+       set_fs_pwd(current->fs, &path);
 
 dput_and_out:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
@@ -535,7 +546,7 @@ asmlinkage long sys_fchdir(unsigned int fd)
        if (!S_ISDIR(inode->i_mode))
                goto out_putf;
 
-       error = file_permission(file, MAY_EXEC);
+       error = inode_permission(inode, MAY_EXEC | MAY_ACCESS);
        if (!error)
                set_fs_pwd(current->fs, &file->f_path);
 out_putf:
@@ -546,14 +557,14 @@ out:
 
 asmlinkage long sys_chroot(const char __user * filename)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
+       error = user_path_dir(filename, &path);
        if (error)
                goto out;
 
-       error = vfs_permission(&nd, MAY_EXEC);
+       error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
        if (error)
                goto dput_and_out;
 
@@ -561,11 +572,10 @@ asmlinkage long sys_chroot(const char __user * filename)
        if (!capable(CAP_SYS_CHROOT))
                goto dput_and_out;
 
-       set_fs_root(current->fs, &nd.path);
-       set_fs_altroot();
+       set_fs_root(current->fs, &path);
        error = 0;
 dput_and_out:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
@@ -590,9 +600,6 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        err = mnt_want_write(file->f_path.mnt);
        if (err)
                goto out_putf;
-       err = -EPERM;
-       if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-               goto out_drop_write;
        mutex_lock(&inode->i_mutex);
        if (mode == (mode_t) -1)
                mode = inode->i_mode;
@@ -600,8 +607,6 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
        err = notify_change(dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
-
-out_drop_write:
        mnt_drop_write(file->f_path.mnt);
 out_putf:
        fput(file);
@@ -612,36 +617,29 @@ out:
 asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
                             mode_t mode)
 {
-       struct nameidata nd;
-       struct inode * inode;
+       struct path path;
+       struct inode *inode;
        int error;
        struct iattr newattrs;
 
-       error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
+       error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
        if (error)
                goto out;
-       inode = nd.path.dentry->d_inode;
+       inode = path.dentry->d_inode;
 
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto dput_and_out;
-
-       error = -EPERM;
-       if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-               goto out_drop_write;
-
        mutex_lock(&inode->i_mutex);
        if (mode == (mode_t) -1)
                mode = inode->i_mode;
        newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
        newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-       error = notify_change(nd.path.dentry, &newattrs);
+       error = notify_change(path.dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
-
-out_drop_write:
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(path.mnt);
 dput_and_out:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
@@ -653,18 +651,10 @@ asmlinkage long sys_chmod(const char __user *filename, mode_t mode)
 
 static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
 {
-       struct inode * inode;
+       struct inode *inode = dentry->d_inode;
        int error;
        struct iattr newattrs;
 
-       error = -ENOENT;
-       if (!(inode = dentry->d_inode)) {
-               printk(KERN_ERR "chown_common: NULL inode\n");
-               goto out;
-       }
-       error = -EPERM;
-       if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-               goto out;
        newattrs.ia_valid =  ATTR_CTIME;
        if (user != (uid_t) -1) {
                newattrs.ia_valid |= ATTR_UID;
@@ -680,25 +670,25 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
        mutex_lock(&inode->i_mutex);
        error = notify_change(dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
-out:
+
        return error;
 }
 
 asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(filename, &nd);
+       error = user_path(filename, &path);
        if (error)
                goto out;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto out_release;
-       error = chown_common(nd.path.dentry, user, group);
-       mnt_drop_write(nd.path.mnt);
+       error = chown_common(path.dentry, user, group);
+       mnt_drop_write(path.mnt);
 out_release:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
@@ -706,7 +696,7 @@ out:
 asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
                             gid_t group, int flag)
 {
-       struct nameidata nd;
+       struct path path;
        int error = -EINVAL;
        int follow;
 
@@ -714,35 +704,35 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
                goto out;
 
        follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
-       error = __user_walk_fd(dfd, filename, follow, &nd);
+       error = user_path_at(dfd, filename, follow, &path);
        if (error)
                goto out;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto out_release;
-       error = chown_common(nd.path.dentry, user, group);
-       mnt_drop_write(nd.path.mnt);
+       error = chown_common(path.dentry, user, group);
+       mnt_drop_write(path.mnt);
 out_release:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
 
 asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk_link(filename, &nd);
+       error = user_lpath(filename, &path);
        if (error)
                goto out;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto out_release;
-       error = chown_common(nd.path.dentry, user, group);
-       mnt_drop_write(nd.path.mnt);
+       error = chown_common(path.dentry, user, group);
+       mnt_drop_write(path.mnt);
 out_release:
-       path_put(&nd.path);
+       path_put(&path);
 out:
        return error;
 }
@@ -982,7 +972,6 @@ int get_unused_fd_flags(int flags)
        int fd, error;
        struct fdtable *fdt;
 
-       error = -EMFILE;
        spin_lock(&files->file_lock);
 
 repeat:
@@ -990,13 +979,6 @@ repeat:
        fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
                                files->next_fd);
 
-       /*
-        * N.B. For clone tasks sharing a files structure, this test
-        * will limit the total number of files that can be opened.
-        */
-       if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-               goto out;
-
        /* Do we need to expand the fd array or fd set?  */
        error = expand_files(files, fd);
        if (error < 0)
@@ -1007,7 +989,6 @@ repeat:
                 * If we needed to expand the fs array we
                 * might have blocked - try again.
                 */
-               error = -EMFILE;
                goto repeat;
        }
 
index 10c4e9aa5c49cef011ee9b2da628d00350da63be..fcba6542b8d0132b72be8a93959a60b57c556e38 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -777,45 +777,10 @@ pipe_rdwr_open(struct inode *inode, struct file *filp)
 /*
  * The file_operations structs are not static because they
  * are also used in linux/fs/fifo.c to do operations on FIFOs.
+ *
+ * Pipes reuse fifos' file_operations structs.
  */
-const struct file_operations read_fifo_fops = {
-       .llseek         = no_llseek,
-       .read           = do_sync_read,
-       .aio_read       = pipe_read,
-       .write          = bad_pipe_w,
-       .poll           = pipe_poll,
-       .unlocked_ioctl = pipe_ioctl,
-       .open           = pipe_read_open,
-       .release        = pipe_read_release,
-       .fasync         = pipe_read_fasync,
-};
-
-const struct file_operations write_fifo_fops = {
-       .llseek         = no_llseek,
-       .read           = bad_pipe_r,
-       .write          = do_sync_write,
-       .aio_write      = pipe_write,
-       .poll           = pipe_poll,
-       .unlocked_ioctl = pipe_ioctl,
-       .open           = pipe_write_open,
-       .release        = pipe_write_release,
-       .fasync         = pipe_write_fasync,
-};
-
-const struct file_operations rdwr_fifo_fops = {
-       .llseek         = no_llseek,
-       .read           = do_sync_read,
-       .aio_read       = pipe_read,
-       .write          = do_sync_write,
-       .aio_write      = pipe_write,
-       .poll           = pipe_poll,
-       .unlocked_ioctl = pipe_ioctl,
-       .open           = pipe_rdwr_open,
-       .release        = pipe_rdwr_release,
-       .fasync         = pipe_rdwr_fasync,
-};
-
-static const struct file_operations read_pipe_fops = {
+const struct file_operations read_pipefifo_fops = {
        .llseek         = no_llseek,
        .read           = do_sync_read,
        .aio_read       = pipe_read,
@@ -827,7 +792,7 @@ static const struct file_operations read_pipe_fops = {
        .fasync         = pipe_read_fasync,
 };
 
-static const struct file_operations write_pipe_fops = {
+const struct file_operations write_pipefifo_fops = {
        .llseek         = no_llseek,
        .read           = bad_pipe_r,
        .write          = do_sync_write,
@@ -839,7 +804,7 @@ static const struct file_operations write_pipe_fops = {
        .fasync         = pipe_write_fasync,
 };
 
-static const struct file_operations rdwr_pipe_fops = {
+const struct file_operations rdwr_pipefifo_fops = {
        .llseek         = no_llseek,
        .read           = do_sync_read,
        .aio_read       = pipe_read,
@@ -927,7 +892,7 @@ static struct inode * get_pipe_inode(void)
        inode->i_pipe = pipe;
 
        pipe->readers = pipe->writers = 1;
-       inode->i_fop = &rdwr_pipe_fops;
+       inode->i_fop = &rdwr_pipefifo_fops;
 
        /*
         * Mark the inode dirty from the very beginning,
@@ -978,7 +943,7 @@ struct file *create_write_pipe(int flags)
        d_instantiate(dentry, inode);
 
        err = -ENFILE;
-       f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipe_fops);
+       f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipefifo_fops);
        if (!f)
                goto err_dentry;
        f->f_mapping = inode->i_mapping;
@@ -1020,7 +985,7 @@ struct file *create_read_pipe(struct file *wrf, int flags)
 
        f->f_pos = 0;
        f->f_flags = O_RDONLY | (flags & O_NONBLOCK);
-       f->f_op = &read_pipe_fops;
+       f->f_op = &read_pipefifo_fops;
        f->f_mode = FMODE_READ;
        f->f_version = 0;
 
index 81bce6791bfc3cccd000309c53cd328b6cfc7425..01ed610f9b87f6fc0ffe1bfa89f2177e77d07323 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/time.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/task_io_accounting_ops.h>
 #include <linux/init.h>
 #include <linux/capability.h>
 #include <linux/file.h>
@@ -1859,8 +1860,7 @@ static const struct file_operations proc_fd_operations = {
  * /proc/pid/fd needs a special permission handler so that a process can still
  * access /proc/self/fd after it has executed a setuid().
  */
-static int proc_fd_permission(struct inode *inode, int mask,
-                               struct nameidata *nd)
+static int proc_fd_permission(struct inode *inode, int mask)
 {
        int rv;
 
@@ -2403,53 +2403,18 @@ static int proc_base_fill_cache(struct file *filp, void *dirent,
 #ifdef CONFIG_TASK_IO_ACCOUNTING
 static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
 {
-       u64 rchar, wchar, syscr, syscw;
-       struct task_io_accounting ioac;
-
-       if (!whole) {
-               rchar = task->rchar;
-               wchar = task->wchar;
-               syscr = task->syscr;
-               syscw = task->syscw;
-               memcpy(&ioac, &task->ioac, sizeof(ioac));
-       } else {
-               unsigned long flags;
-               struct task_struct *t = task;
-               rchar = wchar = syscr = syscw = 0;
-               memset(&ioac, 0, sizeof(ioac));
-
-               rcu_read_lock();
-               do {
-                       rchar += t->rchar;
-                       wchar += t->wchar;
-                       syscr += t->syscr;
-                       syscw += t->syscw;
-
-                       ioac.read_bytes += t->ioac.read_bytes;
-                       ioac.write_bytes += t->ioac.write_bytes;
-                       ioac.cancelled_write_bytes +=
-                                       t->ioac.cancelled_write_bytes;
-                       t = next_thread(t);
-               } while (t != task);
-               rcu_read_unlock();
-
-               if (lock_task_sighand(task, &flags)) {
-                       struct signal_struct *sig = task->signal;
+       struct task_io_accounting acct = task->ioac;
+       unsigned long flags;
 
-                       rchar += sig->rchar;
-                       wchar += sig->wchar;
-                       syscr += sig->syscr;
-                       syscw += sig->syscw;
+       if (whole && lock_task_sighand(task, &flags)) {
+               struct task_struct *t = task;
 
-                       ioac.read_bytes += sig->ioac.read_bytes;
-                       ioac.write_bytes += sig->ioac.write_bytes;
-                       ioac.cancelled_write_bytes +=
-                                       sig->ioac.cancelled_write_bytes;
+               task_io_accounting_add(&acct, &task->signal->ioac);
+               while_each_thread(task, t)
+                       task_io_accounting_add(&acct, &t->ioac);
 
-                       unlock_task_sighand(task, &flags);
-               }
+               unlock_task_sighand(task, &flags);
        }
-
        return sprintf(buffer,
                        "rchar: %llu\n"
                        "wchar: %llu\n"
@@ -2458,13 +2423,10 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
                        "read_bytes: %llu\n"
                        "write_bytes: %llu\n"
                        "cancelled_write_bytes: %llu\n",
-                       (unsigned long long)rchar,
-                       (unsigned long long)wchar,
-                       (unsigned long long)syscr,
-                       (unsigned long long)syscw,
-                       (unsigned long long)ioac.read_bytes,
-                       (unsigned long long)ioac.write_bytes,
-                       (unsigned long long)ioac.cancelled_write_bytes);
+                       acct.rchar, acct.wchar,
+                       acct.syscr, acct.syscw,
+                       acct.read_bytes, acct.write_bytes,
+                       acct.cancelled_write_bytes);
 }
 
 static int proc_tid_io_accounting(struct task_struct *task, char *buffer)
index b37f25dc45a58030b43893169879ad67dcfeed4b..8bb03f056c282ec1c0b4b256c5f4ed4551182f1c 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
+#include <linux/sysctl.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -65,6 +66,8 @@ static void proc_delete_inode(struct inode *inode)
                        module_put(de->owner);
                de_put(de);
        }
+       if (PROC_I(inode)->sysctl)
+               sysctl_head_put(PROC_I(inode)->sysctl);
        clear_inode(inode);
 }
 
@@ -84,6 +87,8 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
        ei->fd = 0;
        ei->op.proc_get_link = NULL;
        ei->pde = NULL;
+       ei->sysctl = NULL;
+       ei->sysctl_entry = NULL;
        inode = &ei->vfs_inode;
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
        return inode;
index 5acc001d49f6c72db83216173136701c2169f950..f9a8b892718fc2f1a04ce1d169ba1667378832b5 100644 (file)
 static struct dentry_operations proc_sys_dentry_operations;
 static const struct file_operations proc_sys_file_operations;
 static const struct inode_operations proc_sys_inode_operations;
+static const struct file_operations proc_sys_dir_file_operations;
+static const struct inode_operations proc_sys_dir_operations;
 
-static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table)
-{
-       /* Refresh the cached information bits in the inode */
-       if (table) {
-               inode->i_uid = 0;
-               inode->i_gid = 0;
-               inode->i_mode = table->mode;
-               if (table->proc_handler) {
-                       inode->i_mode |= S_IFREG;
-                       inode->i_nlink = 1;
-               } else {
-                       inode->i_mode |= S_IFDIR;
-                       inode->i_nlink = 0;     /* It is too hard to figure out */
-               }
-       }
-}
-
-static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table)
+static struct inode *proc_sys_make_inode(struct super_block *sb,
+               struct ctl_table_header *head, struct ctl_table *table)
 {
        struct inode *inode;
-       struct proc_inode *dir_ei, *ei;
-       int depth;
+       struct proc_inode *ei;
 
-       inode = new_inode(dir->i_sb);
+       inode = new_inode(sb);
        if (!inode)
                goto out;
 
-       /* A directory is always one deeper than it's parent */
-       dir_ei = PROC_I(dir);
-       depth = dir_ei->fd + 1;
-
+       sysctl_head_get(head);
        ei = PROC_I(inode);
-       ei->fd = depth;
+       ei->sysctl = head;
+       ei->sysctl_entry = table;
+
        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-       inode->i_op = &proc_sys_inode_operations;
-       inode->i_fop = &proc_sys_file_operations;
        inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
-       proc_sys_refresh_inode(inode, table);
+       inode->i_mode = table->mode;
+       if (!table->child) {
+               inode->i_mode |= S_IFREG;
+               inode->i_op = &proc_sys_inode_operations;
+               inode->i_fop = &proc_sys_file_operations;
+       } else {
+               inode->i_mode |= S_IFDIR;
+               inode->i_nlink = 0;
+               inode->i_op = &proc_sys_dir_operations;
+               inode->i_fop = &proc_sys_dir_file_operations;
+       }
 out:
        return inode;
 }
 
-static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
-{
-       for (;;) {
-               struct proc_inode *ei;
-
-               ei = PROC_I(dentry->d_inode);
-               if (ei->fd == depth)
-                       break; /* found */
-
-               dentry = dentry->d_parent;
-       }
-       return dentry;
-}
-
-static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,
-                                                       struct qstr *name)
+static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name)
 {
        int len;
-       for ( ; table->ctl_name || table->procname; table++) {
+       for ( ; p->ctl_name || p->procname; p++) {
 
-               if (!table->procname)
+               if (!p->procname)
                        continue;
 
-               len = strlen(table->procname);
+               len = strlen(p->procname);
                if (len != name->len)
                        continue;
 
-               if (memcmp(table->procname, name->name, len) != 0)
+               if (memcmp(p->procname, name->name, len) != 0)
                        continue;
 
                /* I have a match */
-               return table;
+               return p;
        }
        return NULL;
 }
 
-static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
-                                               struct ctl_table *table)
+struct ctl_table_header *grab_header(struct inode *inode)
 {
-       struct dentry *ancestor;
-       struct proc_inode *ei;
-       int depth, i;
+       if (PROC_I(inode)->sysctl)
+               return sysctl_head_grab(PROC_I(inode)->sysctl);
+       else
+               return sysctl_head_next(NULL);
+}
 
-       ei = PROC_I(dentry->d_inode);
-       depth = ei->fd;
+static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
+                                       struct nameidata *nd)
+{
+       struct ctl_table_header *head = grab_header(dir);
+       struct ctl_table *table = PROC_I(dir)->sysctl_entry;
+       struct ctl_table_header *h = NULL;
+       struct qstr *name = &dentry->d_name;
+       struct ctl_table *p;
+       struct inode *inode;
+       struct dentry *err = ERR_PTR(-ENOENT);
 
-       if (depth == 0)
-               return table;
+       if (IS_ERR(head))
+               return ERR_CAST(head);
 
-       for (i = 1; table && (i <= depth); i++) {
-               ancestor = proc_sys_ancestor(dentry, i);
-               table = proc_sys_lookup_table_one(table, &ancestor->d_name);
-               if (table)
-                       table = table->child;
+       if (table && !table->child) {
+               WARN_ON(1);
+               goto out;
        }
-       return table;
-
-}
-static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
-                                               struct qstr *name,
-                                               struct ctl_table *table)
-{
-       table = proc_sys_lookup_table(dparent, table);
-       if (table)
-               table = proc_sys_lookup_table_one(table, name);
-       return table;
-}
 
-static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
-                                               struct qstr *name,
-                                               struct ctl_table_header **ptr)
-{
-       struct ctl_table_header *head;
-       struct ctl_table *table = NULL;
+       table = table ? table->child : head->ctl_table;
 
-       for (head = sysctl_head_next(NULL); head;
-                       head = sysctl_head_next(head)) {
-               table = proc_sys_lookup_entry(parent, name, head->ctl_table);
-               if (table)
-                       break;
+       p = find_in_table(table, name);
+       if (!p) {
+               for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
+                       if (h->attached_to != table)
+                               continue;
+                       p = find_in_table(h->attached_by, name);
+                       if (p)
+                               break;
+               }
        }
-       *ptr = head;
-       return table;
-}
-
-static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
-                                       struct nameidata *nd)
-{
-       struct ctl_table_header *head;
-       struct inode *inode;
-       struct dentry *err;
-       struct ctl_table *table;
 
-       err = ERR_PTR(-ENOENT);
-       table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-       if (!table)
+       if (!p)
                goto out;
 
        err = ERR_PTR(-ENOMEM);
-       inode = proc_sys_make_inode(dir, table);
+       inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
+       if (h)
+               sysctl_head_finish(h);
+
        if (!inode)
                goto out;
 
@@ -168,22 +129,14 @@ out:
 static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
                size_t count, loff_t *ppos, int write)
 {
-       struct dentry *dentry = filp->f_dentry;
-       struct ctl_table_header *head;
-       struct ctl_table *table;
+       struct inode *inode = filp->f_path.dentry->d_inode;
+       struct ctl_table_header *head = grab_header(inode);
+       struct ctl_table *table = PROC_I(inode)->sysctl_entry;
        ssize_t error;
        size_t res;
 
-       table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-       /* Has the sysctl entry disappeared on us? */
-       error = -ENOENT;
-       if (!table)
-               goto out;
-
-       /* Has the sysctl entry been replaced by a directory? */
-       error = -EISDIR;
-       if (!table->proc_handler)
-               goto out;
+       if (IS_ERR(head))
+               return PTR_ERR(head);
 
        /*
         * At this point we know that the sysctl was not unregistered
@@ -193,6 +146,11 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
        if (sysctl_perm(head->root, table, write ? MAY_WRITE : MAY_READ))
                goto out;
 
+       /* if that can happen at all, it should be -EINVAL, not -EISDIR */
+       error = -EINVAL;
+       if (!table->proc_handler)
+               goto out;
+
        /* careful: calling conventions are nasty here */
        res = count;
        error = table->proc_handler(table, write, filp, buf, &res, ppos);
@@ -218,82 +176,86 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
 
 
 static int proc_sys_fill_cache(struct file *filp, void *dirent,
-                               filldir_t filldir, struct ctl_table *table)
+                               filldir_t filldir,
+                               struct ctl_table_header *head,
+                               struct ctl_table *table)
 {
-       struct ctl_table_header *head;
-       struct ctl_table *child_table = NULL;
        struct dentry *child, *dir = filp->f_path.dentry;
        struct inode *inode;
        struct qstr qname;
        ino_t ino = 0;
        unsigned type = DT_UNKNOWN;
-       int ret;
 
        qname.name = table->procname;
        qname.len  = strlen(table->procname);
        qname.hash = full_name_hash(qname.name, qname.len);
 
-       /* Suppress duplicates.
-        * Only fill a directory entry if it is the value that
-        * an ordinary lookup of that name returns.  Hide all
-        * others.
-        *
-        * If we ever cache this translation in the dcache
-        * I should do a dcache lookup first.  But for now
-        * it is just simpler not to.
-        */
-       ret = 0;
-       child_table = do_proc_sys_lookup(dir, &qname, &head);
-       sysctl_head_finish(head);
-       if (child_table != table)
-               return 0;
-
        child = d_lookup(dir, &qname);
        if (!child) {
-               struct dentry *new;
-               new = d_alloc(dir, &qname);
-               if (new) {
-                       inode = proc_sys_make_inode(dir->d_inode, table);
-                       if (!inode)
-                               child = ERR_PTR(-ENOMEM);
-                       else {
-                               new->d_op = &proc_sys_dentry_operations;
-                               d_add(new, inode);
+               child = d_alloc(dir, &qname);
+               if (child) {
+                       inode = proc_sys_make_inode(dir->d_sb, head, table);
+                       if (!inode) {
+                               dput(child);
+                               return -ENOMEM;
+                       else {
+                               child->d_op = &proc_sys_dentry_operations;
+                               d_add(child, inode);
                        }
-                       if (child)
-                               dput(new);
-                       else
-                               child = new;
+               } else {
+                       return -ENOMEM;
                }
        }
-       if (!child || IS_ERR(child) || !child->d_inode)
-               goto end_instantiate;
        inode = child->d_inode;
-       if (inode) {
-               ino  = inode->i_ino;
-               type = inode->i_mode >> 12;
-       }
+       ino  = inode->i_ino;
+       type = inode->i_mode >> 12;
        dput(child);
-end_instantiate:
-       if (!ino)
-               ino= find_inode_number(dir, &qname);
-       if (!ino)
-               ino = 1;
-       return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
+       return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
+}
+
+static int scan(struct ctl_table_header *head, ctl_table *table,
+               unsigned long *pos, struct file *file,
+               void *dirent, filldir_t filldir)
+{
+
+       for (; table->ctl_name || table->procname; table++, (*pos)++) {
+               int res;
+
+               /* Can't do anything without a proc name */
+               if (!table->procname)
+                       continue;
+
+               if (*pos < file->f_pos)
+                       continue;
+
+               res = proc_sys_fill_cache(file, dirent, filldir, head, table);
+               if (res)
+                       return res;
+
+               file->f_pos = *pos + 1;
+       }
+       return 0;
 }
 
 static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-       struct dentry *dentry = filp->f_dentry;
+       struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
-       struct ctl_table_header *head = NULL;
-       struct ctl_table *table;
+       struct ctl_table_header *head = grab_header(inode);
+       struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+       struct ctl_table_header *h = NULL;
        unsigned long pos;
-       int ret;
+       int ret = -EINVAL;
+
+       if (IS_ERR(head))
+               return PTR_ERR(head);
 
-       ret = -ENOTDIR;
-       if (!S_ISDIR(inode->i_mode))
+       if (table && !table->child) {
+               WARN_ON(1);
                goto out;
+       }
+
+       table = table ? table->child : head->ctl_table;
 
        ret = 0;
        /* Avoid a switch here: arm builds fail with missing __cmpdi2 */
@@ -311,30 +273,17 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
        }
        pos = 2;
 
-       /* - Find each instance of the directory
-        * - Read all entries in each instance
-        * - Before returning an entry to user space lookup the entry
-        *   by name and if I find a different entry don't return
-        *   this one because it means it is a buried dup.
-        * For sysctl this should only happen for directory entries.
-        */
-       for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
-               table = proc_sys_lookup_table(dentry, head->ctl_table);
+       ret = scan(head, table, &pos, filp, dirent, filldir);
+       if (ret)
+               goto out;
 
-               if (!table)
+       for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
+               if (h->attached_to != table)
                        continue;
-
-               for (; table->ctl_name || table->procname; table++, pos++) {
-                       /* Can't do anything without a proc name */
-                       if (!table->procname)
-                               continue;
-
-                       if (pos < filp->f_pos)
-                               continue;
-
-                       if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
-                               goto out;
-                       filp->f_pos = pos + 1;
+               ret = scan(h, h->attached_by, &pos, filp, dirent, filldir);
+               if (ret) {
+                       sysctl_head_finish(h);
+                       break;
                }
        }
        ret = 1;
@@ -343,53 +292,24 @@ out:
        return ret;
 }
 
-static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int proc_sys_permission(struct inode *inode, int mask)
 {
        /*
         * sysctl entries that are not writeable,
         * are _NOT_ writeable, capabilities or not.
         */
-       struct ctl_table_header *head;
-       struct ctl_table *table;
-       struct dentry *dentry;
-       int mode;
-       int depth;
+       struct ctl_table_header *head = grab_header(inode);
+       struct ctl_table *table = PROC_I(inode)->sysctl_entry;
        int error;
 
-       head = NULL;
-       depth = PROC_I(inode)->fd;
-
-       /* First check the cached permissions, in case we don't have
-        * enough information to lookup the sysctl table entry.
-        */
-       error = -EACCES;
-       mode = inode->i_mode;
-
-       if (current->euid == 0)
-               mode >>= 6;
-       else if (in_group_p(0))
-               mode >>= 3;
-
-       if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
-               error = 0;
-
-       /* If we can't get a sysctl table entry the permission
-        * checks on the cached mode will have to be enough.
-        */
-       if (!nd || !depth)
-               goto out;
+       if (IS_ERR(head))
+               return PTR_ERR(head);
 
-       dentry = nd->path.dentry;
-       table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
+       if (!table) /* global root - r-xr-xr-x */
+               error = mask & MAY_WRITE ? -EACCES : 0;
+       else /* Use the permissions on the sysctl table entry */
+               error = sysctl_perm(head->root, table, mask);
 
-       /* If the entry does not exist deny permission */
-       error = -EACCES;
-       if (!table)
-               goto out;
-
-       /* Use the permissions on the sysctl table entry */
-       error = sysctl_perm(head->root, table, mask);
-out:
        sysctl_head_finish(head);
        return error;
 }
@@ -409,33 +329,70 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
        return error;
 }
 
-/* I'm lazy and don't distinguish between files and directories,
- * until access time.
- */
+static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+       struct inode *inode = dentry->d_inode;
+       struct ctl_table_header *head = grab_header(inode);
+       struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+
+       if (IS_ERR(head))
+               return PTR_ERR(head);
+
+       generic_fillattr(inode, stat);
+       if (table)
+               stat->mode = (stat->mode & S_IFMT) | table->mode;
+
+       sysctl_head_finish(head);
+       return 0;
+}
+
 static const struct file_operations proc_sys_file_operations = {
        .read           = proc_sys_read,
        .write          = proc_sys_write,
+};
+
+static const struct file_operations proc_sys_dir_file_operations = {
        .readdir        = proc_sys_readdir,
 };
 
 static const struct inode_operations proc_sys_inode_operations = {
+       .permission     = proc_sys_permission,
+       .setattr        = proc_sys_setattr,
+       .getattr        = proc_sys_getattr,
+};
+
+static const struct inode_operations proc_sys_dir_operations = {
        .lookup         = proc_sys_lookup,
        .permission     = proc_sys_permission,
        .setattr        = proc_sys_setattr,
+       .getattr        = proc_sys_getattr,
 };
 
 static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-       struct ctl_table_header *head;
-       struct ctl_table *table;
-       table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-       proc_sys_refresh_inode(dentry->d_inode, table);
-       sysctl_head_finish(head);
-       return !!table;
+       return !PROC_I(dentry->d_inode)->sysctl->unregistering;
+}
+
+static int proc_sys_delete(struct dentry *dentry)
+{
+       return !!PROC_I(dentry->d_inode)->sysctl->unregistering;
+}
+
+static int proc_sys_compare(struct dentry *dir, struct qstr *qstr,
+                           struct qstr *name)
+{
+       struct dentry *dentry = container_of(qstr, struct dentry, d_name);
+       if (qstr->len != name->len)
+               return 1;
+       if (memcmp(qstr->name, name->name, name->len))
+               return 1;
+       return !sysctl_is_seen(PROC_I(dentry->d_inode)->sysctl);
 }
 
 static struct dentry_operations proc_sys_dentry_operations = {
        .d_revalidate   = proc_sys_revalidate,
+       .d_delete       = proc_sys_delete,
+       .d_compare      = proc_sys_compare,
 };
 
 static struct proc_dir_entry *proc_sys_root;
@@ -443,8 +400,8 @@ static struct proc_dir_entry *proc_sys_root;
 int proc_sys_init(void)
 {
        proc_sys_root = proc_mkdir("sys", NULL);
-       proc_sys_root->proc_iops = &proc_sys_inode_operations;
-       proc_sys_root->proc_fops = &proc_sys_file_operations;
+       proc_sys_root->proc_iops = &proc_sys_dir_operations;
+       proc_sys_root->proc_fops = &proc_sys_dir_file_operations;
        proc_sys_root->nlink = 0;
        return 0;
 }
index d7c4935c1034b58d8a5399dcd22d63b2d645cc41..bb3cb5b7cdb2479d1b68b9eb2fa2521e54810490 100644 (file)
@@ -1250,7 +1250,7 @@ static int reiserfs_check_acl(struct inode *inode, int mask)
        return error;
 }
 
-int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int reiserfs_permission(struct inode *inode, int mask)
 {
        /*
         * We don't do permission checks on the internal objects.
index 2294783320cbd14a1b88cd2822122bed664e42cf..e4f8d51a5553651bd0235b2d4310ac9c44f32fb6 100644 (file)
@@ -408,7 +408,7 @@ smb_file_release(struct inode *inode, struct file * file)
  * privileges, so we need our own check for this.
  */
 static int
-smb_file_permission(struct inode *inode, int mask, struct nameidata *nd)
+smb_file_permission(struct inode *inode, int mask)
 {
        int mode = inode->i_mode;
        int error = 0;
@@ -417,7 +417,7 @@ smb_file_permission(struct inode *inode, int mask, struct nameidata *nd)
 
        /* Look at user permissions */
        mode >>= 6;
-       if ((mode & 7 & mask) != mask)
+       if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC))
                error = -EACCES;
        return error;
 }
index 47dc1a445d1f0953123668cae1633c797f2dcc7f..b30311ba8af6ff23ee45af51d07655e68c98fad2 100644 (file)
@@ -772,7 +772,7 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out,
        ssize_t ret;
        int err;
 
-       err = remove_suid(out->f_path.dentry);
+       err = file_remove_suid(out);
        if (unlikely(err))
                return err;
 
@@ -830,7 +830,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
        ssize_t ret;
 
        inode_double_lock(inode, pipe->inode);
-       ret = remove_suid(out->f_path.dentry);
+       ret = file_remove_suid(out);
        if (likely(!ret))
                ret = __splice_from_pipe(pipe, &sd, pipe_to_file);
        inode_double_unlock(inode, pipe->inode);
index 9cf41f719d50d18adf2fca7a6582b0ecfdc506aa..7c46fbeb8b762e0f125a594a6fd27747ff160c55 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -57,13 +57,13 @@ EXPORT_SYMBOL(vfs_getattr);
 
 int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd);
+       error = user_path_at(dfd, name, LOOKUP_FOLLOW, &path);
        if (!error) {
-               error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat);
-               path_put(&nd.path);
+               error = vfs_getattr(path.mnt, path.dentry, stat);
+               path_put(&path);
        }
        return error;
 }
@@ -77,13 +77,13 @@ EXPORT_SYMBOL(vfs_stat);
 
 int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = __user_walk_fd(dfd, name, 0, &nd);
+       error = user_path_at(dfd, name, 0, &path);
        if (!error) {
-               error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat);
-               path_put(&nd.path);
+               error = vfs_getattr(path.mnt, path.dentry, stat);
+               path_put(&path);
        }
        return error;
 }
@@ -291,29 +291,29 @@ asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf)
        return error;
 }
 
-asmlinkage long sys_readlinkat(int dfd, const char __user *path,
+asmlinkage long sys_readlinkat(int dfd, const char __user *pathname,
                                char __user *buf, int bufsiz)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
        if (bufsiz <= 0)
                return -EINVAL;
 
-       error = __user_walk_fd(dfd, path, 0, &nd);
+       error = user_path_at(dfd, pathname, 0, &path);
        if (!error) {
-               struct inode *inode = nd.path.dentry->d_inode;
+               struct inode *inode = path.dentry->d_inode;
 
                error = -EINVAL;
                if (inode->i_op && inode->i_op->readlink) {
-                       error = security_inode_readlink(nd.path.dentry);
+                       error = security_inode_readlink(path.dentry);
                        if (!error) {
-                               touch_atime(nd.path.mnt, nd.path.dentry);
-                               error = inode->i_op->readlink(nd.path.dentry,
+                               touch_atime(path.mnt, path.dentry);
+                               error = inode->i_op->readlink(path.dentry,
                                                              buf, bufsiz);
                        }
                }
-               path_put(&nd.path);
+               path_put(&path);
        }
        return error;
 }
index 005a3b854d96ad120098f750e0ef17d540a4abe1..8565e586e533bdafae604abfda4d06b369025bb9 100644 (file)
@@ -53,6 +53,7 @@
 
 #include "ubifs.h"
 #include <linux/mount.h>
+#include <linux/namei.h>
 
 static int read_block(struct inode *inode, void *addr, unsigned int block,
                      struct ubifs_data_node *dn)
index b6b664e7145ec7e0debea47c041b7c3faa9a0aee..6929e3e91d0579d28c6cd4453554a4c559236736 100644 (file)
@@ -48,66 +48,22 @@ static bool nsec_valid(long nsec)
        return nsec >= 0 && nsec <= 999999999;
 }
 
-/* If times==NULL, set access and modification to current time,
- * must be owner or have write permission.
- * Else, update from *times, must be owner or super user.
- */
-long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
+static int utimes_common(struct path *path, struct timespec *times)
 {
        int error;
-       struct nameidata nd;
-       struct dentry *dentry;
-       struct inode *inode;
        struct iattr newattrs;
-       struct file *f = NULL;
-       struct vfsmount *mnt;
-
-       error = -EINVAL;
-       if (times && (!nsec_valid(times[0].tv_nsec) ||
-                     !nsec_valid(times[1].tv_nsec))) {
-               goto out;
-       }
-
-       if (flags & ~AT_SYMLINK_NOFOLLOW)
-               goto out;
-
-       if (filename == NULL && dfd != AT_FDCWD) {
-               error = -EINVAL;
-               if (flags & AT_SYMLINK_NOFOLLOW)
-                       goto out;
+       struct inode *inode = path->dentry->d_inode;
 
-               error = -EBADF;
-               f = fget(dfd);
-               if (!f)
-                       goto out;
-               dentry = f->f_path.dentry;
-               mnt = f->f_path.mnt;
-       } else {
-               error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
-               if (error)
-                       goto out;
-
-               dentry = nd.path.dentry;
-               mnt = nd.path.mnt;
-       }
-
-       inode = dentry->d_inode;
-
-       error = mnt_want_write(mnt);
+       error = mnt_want_write(path->mnt);
        if (error)
-               goto dput_and_out;
+               goto out;
 
        if (times && times[0].tv_nsec == UTIME_NOW &&
                     times[1].tv_nsec == UTIME_NOW)
                times = NULL;
 
-       /* In most cases, the checks are done in inode_change_ok() */
        newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
        if (times) {
-               error = -EPERM;
-                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-                       goto mnt_drop_write_and_out;
-
                if (times[0].tv_nsec == UTIME_OMIT)
                        newattrs.ia_valid &= ~ATTR_ATIME;
                else if (times[0].tv_nsec != UTIME_NOW) {
@@ -123,21 +79,13 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags
                        newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
                        newattrs.ia_valid |= ATTR_MTIME_SET;
                }
-
                /*
-                * For the UTIME_OMIT/UTIME_NOW and UTIME_NOW/UTIME_OMIT
-                * cases, we need to make an extra check that is not done by
-                * inode_change_ok().
+                * Tell inode_change_ok(), that this is an explicit time
+                * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET
+                * were used.
                 */
-               if (((times[0].tv_nsec == UTIME_NOW &&
-                           times[1].tv_nsec == UTIME_OMIT)
-                    ||
-                    (times[0].tv_nsec == UTIME_OMIT &&
-                           times[1].tv_nsec == UTIME_NOW))
-                   && !is_owner_or_cap(inode))
-                       goto mnt_drop_write_and_out;
+               newattrs.ia_valid |= ATTR_TIMES_SET;
        } else {
-
                /*
                 * If times is NULL (or both times are UTIME_NOW),
                 * then we need to check permissions, because
@@ -148,21 +96,76 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags
                        goto mnt_drop_write_and_out;
 
                if (!is_owner_or_cap(inode)) {
-                       error = permission(inode, MAY_WRITE, NULL);
+                       error = inode_permission(inode, MAY_WRITE);
                        if (error)
                                goto mnt_drop_write_and_out;
                }
        }
        mutex_lock(&inode->i_mutex);
-       error = notify_change(dentry, &newattrs);
+       error = notify_change(path->dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
+
 mnt_drop_write_and_out:
-       mnt_drop_write(mnt);
-dput_and_out:
-       if (f)
-               fput(f);
-       else
-               path_put(&nd.path);
+       mnt_drop_write(path->mnt);
+out:
+       return error;
+}
+
+/*
+ * do_utimes - change times on filename or file descriptor
+ * @dfd: open file descriptor, -1 or AT_FDCWD
+ * @filename: path name or NULL
+ * @times: new times or NULL
+ * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment)
+ *
+ * If filename is NULL and dfd refers to an open file, then operate on
+ * the file.  Otherwise look up filename, possibly using dfd as a
+ * starting point.
+ *
+ * If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
+{
+       int error = -EINVAL;
+
+       if (times && (!nsec_valid(times[0].tv_nsec) ||
+                     !nsec_valid(times[1].tv_nsec))) {
+               goto out;
+       }
+
+       if (flags & ~AT_SYMLINK_NOFOLLOW)
+               goto out;
+
+       if (filename == NULL && dfd != AT_FDCWD) {
+               struct file *file;
+
+               if (flags & AT_SYMLINK_NOFOLLOW)
+                       goto out;
+
+               file = fget(dfd);
+               error = -EBADF;
+               if (!file)
+                       goto out;
+
+               error = utimes_common(&file->f_path, times);
+               fput(file);
+       } else {
+               struct path path;
+               int lookup_flags = 0;
+
+               if (!(flags & AT_SYMLINK_NOFOLLOW))
+                       lookup_flags |= LOOKUP_FOLLOW;
+
+               error = user_path_at(dfd, filename, lookup_flags, &path);
+               if (error)
+                       goto out;
+
+               error = utimes_common(&path, times);
+               path_put(&path);
+       }
+
 out:
        return error;
 }
index 4706a8b1f4959cdf955cc8b625ee5bac3b53ccf7..468377e665314434fd4f5aa6fe407f50a68096fc 100644 (file)
@@ -63,7 +63,7 @@ xattr_permission(struct inode *inode, const char *name, int mask)
                        return -EPERM;
        }
 
-       return permission(inode, mask, NULL);
+       return inode_permission(inode, mask);
 }
 
 int
@@ -252,40 +252,40 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
 }
 
 asmlinkage long
-sys_setxattr(const char __user *path, const char __user *name,
+sys_setxattr(const char __user *pathname, const char __user *name,
             const void __user *value, size_t size, int flags)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (error)
                return error;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (!error) {
-               error = setxattr(nd.path.dentry, name, value, size, flags);
-               mnt_drop_write(nd.path.mnt);
+               error = setxattr(path.dentry, name, value, size, flags);
+               mnt_drop_write(path.mnt);
        }
-       path_put(&nd.path);
+       path_put(&path);
        return error;
 }
 
 asmlinkage long
-sys_lsetxattr(const char __user *path, const char __user *name,
+sys_lsetxattr(const char __user *pathname, const char __user *name,
              const void __user *value, size_t size, int flags)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk_link(path, &nd);
+       error = user_lpath(pathname, &path);
        if (error)
                return error;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (!error) {
-               error = setxattr(nd.path.dentry, name, value, size, flags);
-               mnt_drop_write(nd.path.mnt);
+               error = setxattr(path.dentry, name, value, size, flags);
+               mnt_drop_write(path.mnt);
        }
-       path_put(&nd.path);
+       path_put(&path);
        return error;
 }
 
@@ -350,32 +350,32 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
 }
 
 asmlinkage ssize_t
-sys_getxattr(const char __user *path, const char __user *name,
+sys_getxattr(const char __user *pathname, const char __user *name,
             void __user *value, size_t size)
 {
-       struct nameidata nd;
+       struct path path;
        ssize_t error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (error)
                return error;
-       error = getxattr(nd.path.dentry, name, value, size);
-       path_put(&nd.path);
+       error = getxattr(path.dentry, name, value, size);
+       path_put(&path);
        return error;
 }
 
 asmlinkage ssize_t
-sys_lgetxattr(const char __user *path, const char __user *name, void __user *value,
+sys_lgetxattr(const char __user *pathname, const char __user *name, void __user *value,
              size_t size)
 {
-       struct nameidata nd;
+       struct path path;
        ssize_t error;
 
-       error = user_path_walk_link(path, &nd);
+       error = user_lpath(pathname, &path);
        if (error)
                return error;
-       error = getxattr(nd.path.dentry, name, value, size);
-       path_put(&nd.path);
+       error = getxattr(path.dentry, name, value, size);
+       path_put(&path);
        return error;
 }
 
@@ -425,30 +425,30 @@ listxattr(struct dentry *d, char __user *list, size_t size)
 }
 
 asmlinkage ssize_t
-sys_listxattr(const char __user *path, char __user *list, size_t size)
+sys_listxattr(const char __user *pathname, char __user *list, size_t size)
 {
-       struct nameidata nd;
+       struct path path;
        ssize_t error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (error)
                return error;
-       error = listxattr(nd.path.dentry, list, size);
-       path_put(&nd.path);
+       error = listxattr(path.dentry, list, size);
+       path_put(&path);
        return error;
 }
 
 asmlinkage ssize_t
-sys_llistxattr(const char __user *path, char __user *list, size_t size)
+sys_llistxattr(const char __user *pathname, char __user *list, size_t size)
 {
-       struct nameidata nd;
+       struct path path;
        ssize_t error;
 
-       error = user_path_walk_link(path, &nd);
+       error = user_lpath(pathname, &path);
        if (error)
                return error;
-       error = listxattr(nd.path.dentry, list, size);
-       path_put(&nd.path);
+       error = listxattr(path.dentry, list, size);
+       path_put(&path);
        return error;
 }
 
@@ -486,38 +486,38 @@ removexattr(struct dentry *d, const char __user *name)
 }
 
 asmlinkage long
-sys_removexattr(const char __user *path, const char __user *name)
+sys_removexattr(const char __user *pathname, const char __user *name)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk(path, &nd);
+       error = user_path(pathname, &path);
        if (error)
                return error;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (!error) {
-               error = removexattr(nd.path.dentry, name);
-               mnt_drop_write(nd.path.mnt);
+               error = removexattr(path.dentry, name);
+               mnt_drop_write(path.mnt);
        }
-       path_put(&nd.path);
+       path_put(&path);
        return error;
 }
 
 asmlinkage long
-sys_lremovexattr(const char __user *path, const char __user *name)
+sys_lremovexattr(const char __user *pathname, const char __user *name)
 {
-       struct nameidata nd;
+       struct path path;
        int error;
 
-       error = user_path_walk_link(path, &nd);
+       error = user_lpath(pathname, &path);
        if (error)
                return error;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (!error) {
-               error = removexattr(nd.path.dentry, name);
-               mnt_drop_write(nd.path.mnt);
+               error = removexattr(path.dentry, name);
+               mnt_drop_write(path.mnt);
        }
-       path_put(&nd.path);
+       path_put(&path);
        return error;
 }
 
index a42ba9d71156f84101e1068a33d4bd05e055bcd9..01939ba2d8dea65c43b04b0754b9df4513e8864e 100644 (file)
@@ -84,17 +84,15 @@ xfs_find_handle(
        switch (cmd) {
        case XFS_IOC_PATH_TO_FSHANDLE:
        case XFS_IOC_PATH_TO_HANDLE: {
-               struct nameidata        nd;
-               int                     error;
-
-               error = user_path_walk_link((const char __user *)hreq.path, &nd);
+               struct path path;
+               int error = user_lpath((const char __user *)hreq.path, &path);
                if (error)
                        return error;
 
-               ASSERT(nd.path.dentry);
-               ASSERT(nd.path.dentry->d_inode);
-               inode = igrab(nd.path.dentry->d_inode);
-               path_put(&nd.path);
+               ASSERT(path.dentry);
+               ASSERT(path.dentry->d_inode);
+               inode = igrab(path.dentry->d_inode);
+               path_put(&path);
                break;
        }
 
index 2bf287ef54897af2651f24b39792404270179a19..5fc61c824bb9bbe1b53cca02ab6b72c427c900db 100644 (file)
@@ -589,8 +589,7 @@ xfs_check_acl(
 STATIC int
 xfs_vn_permission(
        struct inode            *inode,
-       int                     mask,
-       struct nameidata        *nd)
+       int                     mask)
 {
        return generic_permission(inode, mask, xfs_check_acl);
 }
index 5e3b57516ec7293fe8bffe42cc995b9025780b8e..82333b3e118e7d6491589c5ad524b179e4feec96 100644 (file)
@@ -711,7 +711,7 @@ start:
             !capable(CAP_FSETID)) {
                error = xfs_write_clear_setuid(xip);
                if (likely(!error))
-                       error = -remove_suid(file->f_path.dentry);
+                       error = -file_remove_suid(file);
                if (unlikely(error)) {
                        goto out_unlock_internal;
                }
index bdca155028ecd343b4ebf0182904dc83f1e61469..d8c3e3cbf41603eb983e5922da3ee796c7fece84 100644 (file)
@@ -1,3 +1,6 @@
+# Top-level Makefile calls into asm-$(ARCH)
+# List only non-arch directories below
+
 header-y += asm-generic/
 header-y += linux/
 header-y += sound/
@@ -5,5 +8,3 @@ header-y += mtd/
 header-y += rdma/
 header-y += video/
 header-y += drm/
-
-header-y += asm-$(ARCH)/
diff --git a/include/asm-alpha/namei.h b/include/asm-alpha/namei.h
deleted file mode 100644 (file)
index 5cc9bb3..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $
- * linux/include/asm-alpha/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ALPHA_NAMEI_H
-#define __ALPHA_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __ALPHA_NAMEI_H */
diff --git a/include/asm-arm/arch-at91/at91_ecc.h b/include/asm-arm/arch-at91/at91_ecc.h
deleted file mode 100644 (file)
index 1e5a8ca..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * include/asm-arm/arch-at91/at91_ecc.h
- *
- * Error Corrected Code Controller (ECC) - System peripherals regsters.
- * Based on AT91SAM9260 datasheet revision B.
- *
- * 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 AT91_ECC_H
-#define AT91_ECC_H
-
-#define AT91_ECC_CR            0x00                    /* Control register */
-#define                AT91_ECC_RST            (1 << 0)                /* Reset parity */
-
-#define AT91_ECC_MR            0x04                    /* Mode register */
-#define                AT91_ECC_PAGESIZE       (3 << 0)                /* Page Size */
-#define                        AT91_ECC_PAGESIZE_528           (0)
-#define                        AT91_ECC_PAGESIZE_1056          (1)
-#define                        AT91_ECC_PAGESIZE_2112          (2)
-#define                        AT91_ECC_PAGESIZE_4224          (3)
-
-#define AT91_ECC_SR            0x08                    /* Status register */
-#define                AT91_ECC_RECERR         (1 << 0)                /* Recoverable Error */
-#define                AT91_ECC_ECCERR         (1 << 1)                /* ECC Single Bit Error */
-#define                AT91_ECC_MULERR         (1 << 2)                /* Multiple Errors */
-
-#define AT91_ECC_PR            0x0c                    /* Parity register */
-#define                AT91_ECC_BITADDR        (0xf << 0)              /* Bit Error Address */
-#define                AT91_ECC_WORDADDR       (0xfff << 4)            /* Word Error Address */
-
-#define AT91_ECC_NPR           0x10                    /* NParity register */
-#define                AT91_ECC_NPARITY        (0xffff << 0)           /* NParity */
-
-#endif
index 94de788da76ed55a64b0a1698ff790f322e545cb..48bbd854f57d7690e1f3dbbf34ecf41b65560e4d 100644 (file)
@@ -89,7 +89,7 @@ struct at91_usbh_data {
 extern void __init at91_add_device_usbh(struct at91_usbh_data *data);
 
  /* NAND / SmartMedia */
-struct at91_nand_data {
+struct atmel_nand_data {
        u8              enable_pin;     /* chip enable */
        u8              det_pin;        /* card detect */
        u8              rdy_pin;        /* ready/busy */
@@ -98,7 +98,7 @@ struct at91_nand_data {
        u8              bus_width_16;   /* buswidth is 16 bit */
        struct mtd_partition* (*partition_info)(int, int*);
 };
-extern void __init at91_add_device_nand(struct at91_nand_data *data);
+extern void __init at91_add_device_nand(struct atmel_nand_data *data);
 
  /* I2C*/
 extern void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices);
diff --git a/include/asm-arm/namei.h b/include/asm-arm/namei.h
deleted file mode 100644 (file)
index a402d3b..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 
- * linux/include/asm-arm/namei.h
- *
- * Routines to handle famous /usr/gnemul
- * Derived from the Sparc version of this file
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ASMARM_NAMEI_H
-#define __ASMARM_NAMEI_H
-
-#define ARM_BSD_EMUL "usr/gnemul/bsd/"
-
-static inline char *__emul_prefix(void)
-{
-       switch (current->personality) {
-       case PER_BSD:
-               return ARM_BSD_EMUL;
-       default:
-               return NULL;
-       }
-}
-
-#endif /* __ASMARM_NAMEI_H */
index a3783861cdd269ab80101e1a2acdf162f8fdecef..e60e9076544d08ace7891dbe1d1ffbf31df4f45f 100644 (file)
@@ -82,7 +82,15 @@ struct mci_platform_data;
 struct platform_device *
 at32_add_device_mci(unsigned int id, struct mci_platform_data *data);
 
-struct platform_device *at32_add_device_ac97c(unsigned int id);
+struct ac97c_platform_data {
+       unsigned short dma_rx_periph_id;
+       unsigned short dma_tx_periph_id;
+       unsigned short dma_controller_id;
+       int reset_pin;
+};
+struct platform_device *
+at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data);
+
 struct platform_device *at32_add_device_abdac(unsigned int id);
 struct platform_device *at32_add_device_psif(unsigned int id);
 
@@ -97,4 +105,17 @@ struct platform_device *
 at32_add_device_cf(unsigned int id, unsigned int extint,
                struct cf_platform_data *data);
 
+/* NAND / SmartMedia */
+struct atmel_nand_data {
+       int     enable_pin;     /* chip enable */
+       int     det_pin;        /* card detect */
+       int     rdy_pin;        /* ready/busy */
+       u8      ale;            /* address line number connected to ALE */
+       u8      cle;            /* address line number connected to CLE */
+       u8      bus_width_16;   /* buswidth is 16 bit */
+       struct mtd_partition *(*partition_info)(int size, int *num_partitions);
+};
+struct platform_device *
+at32_add_device_nand(unsigned int id, struct atmel_nand_data *data);
+
 #endif /* __ASM_ARCH_BOARD_H */
diff --git a/include/asm-avr32/namei.h b/include/asm-avr32/namei.h
deleted file mode 100644 (file)
index f0a26de..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_AVR32_NAMEI_H
-#define __ASM_AVR32_NAMEI_H
-
-/* This dummy routine may be changed to something useful */
-#define __emul_prefix() NULL
-
-#endif /* __ASM_AVR32_NAMEI_H */
diff --git a/include/asm-blackfin/namei.h b/include/asm-blackfin/namei.h
deleted file mode 100644 (file)
index 8b89a2d..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * linux/include/asm/namei.h
- *
- * Included from linux/fs/namei.c
- *
- * Changes made by Lineo Inc.    May 2001
- */
-
-#ifndef __BFIN_NAMEI_H
-#define __BFIN_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
diff --git a/include/asm-cris/namei.h b/include/asm-cris/namei.h
deleted file mode 100644 (file)
index 8a3be7a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $
- * linux/include/asm-cris/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __CRIS_NAMEI_H
-#define __CRIS_NAMEI_H
-
-/* used to find file-system prefixes for doing emulations
- * see for example asm-sparc/namei.h
- * we don't use it...
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __CRIS_NAMEI_H */
diff --git a/include/asm-frv/namei.h b/include/asm-frv/namei.h
deleted file mode 100644 (file)
index 4ea5717..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * include/asm-frv/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ASM_NAMEI_H
-#define __ASM_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
-
index a3034d20ebd5ef2f7d264b62311afa06a7ac7327..c764a8fcb058e33fb703e56af17eaa7fb966d7ea 100644 (file)
@@ -13,7 +13,7 @@
  *
  * While the GPIO programming interface defines valid GPIO numbers
  * to be in the range 0..MAX_INT, this library restricts them to the
- * smaller range 0..ARCH_NR_GPIOS.
+ * smaller range 0..ARCH_NR_GPIOS-1.
  */
 
 #ifndef ARCH_NR_GPIOS
index 9cd44b162ba1809af8d28093da19b353f7be7cd0..6d88a923c945cea5b860bb8dc8051c236740f771 100644 (file)
  * during second ld run in second ld pass when generating System.map */
 #define TEXT_TEXT                                                      \
                ALIGN_FUNCTION();                                       \
+               *(.text.hot)                                            \
                *(.text)                                                \
                *(.ref.text)                                            \
                *(.text.init.refok)                                     \
        CPU_KEEP(init.text)                                             \
        CPU_KEEP(exit.text)                                             \
        MEM_KEEP(init.text)                                             \
-       MEM_KEEP(exit.text)
+       MEM_KEEP(exit.text)                                             \
+               *(.text.unlikely)
 
 
 /* sched.text is aling to function alignment to secure we have same
diff --git a/include/asm-h8300/namei.h b/include/asm-h8300/namei.h
deleted file mode 100644 (file)
index ab6f196..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/include/asm-h8300/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __H8300_NAMEI_H
-#define __H8300_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
diff --git a/include/asm-ia64/namei.h b/include/asm-ia64/namei.h
deleted file mode 100644 (file)
index 78e7680..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef _ASM_IA64_NAMEI_H
-#define _ASM_IA64_NAMEI_H
-
-/*
- * Modified 1998, 1999, 2001
- *     David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
- */
-
-#include <asm/ptrace.h>
-#include <asm/system.h>
-
-#define EMUL_PREFIX_LINUX_IA32 "/emul/ia32-linux/"
-
-static inline char *
-__emul_prefix (void)
-{
-       switch (current->personality) {
-             case PER_LINUX32:
-               return EMUL_PREFIX_LINUX_IA32;
-             default:
-               return NULL;
-       }
-}
-
-#endif /* _ASM_IA64_NAMEI_H */
diff --git a/include/asm-m32r/namei.h b/include/asm-m32r/namei.h
deleted file mode 100644 (file)
index 210f805..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef _ASM_M32R_NAMEI_H
-#define _ASM_M32R_NAMEI_H
-
-/*
- * linux/include/asm-m32r/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_M32R_NAMEI_H */
diff --git a/include/asm-m68k/namei.h b/include/asm-m68k/namei.h
deleted file mode 100644 (file)
index f33f243..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/include/asm-m68k/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __M68K_NAMEI_H
-#define __M68K_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
diff --git a/include/asm-m68knommu/namei.h b/include/asm-m68knommu/namei.h
deleted file mode 100644 (file)
index 31a85d2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-m68k/namei.h>
diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h
deleted file mode 100644 (file)
index a6605a7..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _ASM_NAMEI_H
-#define _ASM_NAMEI_H
-
-/*
- * This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_NAMEI_H */
diff --git a/include/asm-mn10300/namei.h b/include/asm-mn10300/namei.h
deleted file mode 100644 (file)
index bd9ce94..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Emulation stuff
- *
- * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
- */
-
-#ifndef _ASM_NAMEI_H
-#define _ASM_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_NAMEI_H */
diff --git a/include/asm-parisc/namei.h b/include/asm-parisc/namei.h
deleted file mode 100644 (file)
index 8d29b3d..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $
- * linux/include/asm-parisc/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __PARISC_NAMEI_H
-#define __PARISC_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __PARISC_NAMEI_H */
index 5a21115228af7947c333eb5c4e4cc230623ba243..a8b068792260a56a5371fcec5845a66a634d0453 100644 (file)
@@ -61,7 +61,8 @@ extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu);
 
 extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn,
                            u64 asid, u32 flags);
-extern void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, u64 eaddr, u64 asid);
+extern void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr,
+                                  gva_t eend, u32 asid);
 extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode);
 
 extern void kvmppc_check_and_deliver_interrupts(struct kvm_vcpu *vcpu);
diff --git a/include/asm-powerpc/namei.h b/include/asm-powerpc/namei.h
deleted file mode 100644 (file)
index 6574434..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _ASM_POWERPC_NAMEI_H
-#define _ASM_POWERPC_NAMEI_H
-
-#ifdef __KERNEL__
-
-/*
- * Adapted from include/asm-alpha/namei.h
- *
- * Included from fs/namei.c
- */
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __KERNEL__ */
-#endif /* _ASM_POWERPC_NAMEI_H */
index 3234dd5b3511275521f8f22b3e4f808b432eb257..3c55e4107dcc1864f232aff79d7e8d6b726ca084 100644 (file)
@@ -111,7 +111,7 @@ struct kvm_vcpu_stat {
        u32 exit_validity;
        u32 exit_instruction;
        u32 instruction_lctl;
-       u32 instruction_lctg;
+       u32 instruction_lctlg;
        u32 exit_program_interruption;
        u32 exit_instr_and_program;
        u32 deliver_emergency_signal;
@@ -231,5 +231,5 @@ struct kvm_arch{
        struct kvm_s390_float_interrupt float_int;
 };
 
-extern int sie64a(struct kvm_s390_sie_block *, __u64 *);
+extern int sie64a(struct kvm_s390_sie_block *, unsigned long *);
 #endif
diff --git a/include/asm-s390/namei.h b/include/asm-s390/namei.h
deleted file mode 100644 (file)
index 3e286bd..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *  include/asm-s390/namei.h
- *
- *  S390 version
- *
- *  Derived from "include/asm-i386/namei.h"
- *
- *  Included from linux/fs/namei.c
- */
-
-#ifndef __S390_NAMEI_H
-#define __S390_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __S390_NAMEI_H */
diff --git a/include/asm-sh/namei.h b/include/asm-sh/namei.h
deleted file mode 100644 (file)
index 338a5d9..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.3 2000/07/04 06:24:49 gniibe Exp $
- * linux/include/asm-sh/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ASM_SH_NAMEI_H
-#define __ASM_SH_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __ASM_SH_NAMEI_H */
diff --git a/include/asm-sparc/namei.h b/include/asm-sparc/namei.h
deleted file mode 100644 (file)
index eff944b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef ___ASM_SPARC_NAMEI_H
-#define ___ASM_SPARC_NAMEI_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm-sparc/namei_64.h>
-#else
-#include <asm-sparc/namei_32.h>
-#endif
-#endif
diff --git a/include/asm-sparc/namei_32.h b/include/asm-sparc/namei_32.h
deleted file mode 100644 (file)
index 0646102..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * linux/include/asm-sparc/namei.h
- *
- * Routines to handle famous /usr/gnemul/s*.
- * Included from linux/fs/namei.c
- */
-
-#ifndef __SPARC_NAMEI_H
-#define __SPARC_NAMEI_H
-
-#define __emul_prefix() NULL
-
-#endif /* __SPARC_NAMEI_H */
diff --git a/include/asm-sparc/namei_64.h b/include/asm-sparc/namei_64.h
deleted file mode 100644 (file)
index cbc1b4c..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * linux/include/asm-sparc64/namei.h
- *
- * Routines to handle famous /usr/gnemul/s*.
- * Included from linux/fs/namei.c
- */
-
-#ifndef __SPARC64_NAMEI_H
-#define __SPARC64_NAMEI_H
-
-#define __emul_prefix() NULL
-
-#endif /* __SPARC64_NAMEI_H */
diff --git a/include/asm-sparc64/namei.h b/include/asm-sparc64/namei.h
deleted file mode 100644 (file)
index 1344a91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-sparc/namei.h>
diff --git a/include/asm-um/namei.h b/include/asm-um/namei.h
deleted file mode 100644 (file)
index 002984d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __UM_NAMEI_H
-#define __UM_NAMEI_H
-
-#include "asm/arch/namei.h"
-
-#endif
diff --git a/include/asm-v850/namei.h b/include/asm-v850/namei.h
deleted file mode 100644 (file)
index ee8339b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/include/asm-v850/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __V850_NAMEI_H__
-#define __V850_NAMEI_H__
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __V850_NAMEI_H__ */
index fdde0bedaa90dd310e60862a52ef8a73f09a2f47..bc34dc21f178a0381441f49a580e788df7d8d2cd 100644 (file)
@@ -556,6 +556,7 @@ int kvm_fix_hypercall(struct kvm_vcpu *vcpu);
 int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code);
 
 void kvm_enable_tdp(void);
+void kvm_disable_tdp(void);
 
 int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3);
 int complete_pio(struct kvm_vcpu *vcpu);
diff --git a/include/asm-x86/namei.h b/include/asm-x86/namei.h
deleted file mode 100644 (file)
index 415ef5d..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _ASM_X86_NAMEI_H
-#define _ASM_X86_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_X86_NAMEI_H */
diff --git a/include/asm-xtensa/namei.h b/include/asm-xtensa/namei.h
deleted file mode 100644 (file)
index 3fdff03..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * include/asm-xtensa/namei.h
- *
- * Included from linux/fs/namei.c
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
- */
-
-#ifndef _XTENSA_NAMEI_H
-#define _XTENSA_NAMEI_H
-
-#ifdef __KERNEL__
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __KERNEL__ */
-#endif /* _XTENSA_NAMEI_H */
index 31b75311e2ca381bc4d12fef44f577919a7e57a5..dcc228aa335ae43b77febde77dbecf4d031773b4 100644 (file)
@@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations;
 /* operations shared over more than one file */
 int coda_open(struct inode *i, struct file *f);
 int coda_release(struct inode *i, struct file *f);
-int coda_permission(struct inode *inode, int mask, struct nameidata *nd);
+int coda_permission(struct inode *inode, int mask);
 int coda_revalidate_inode(struct dentry *);
 int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 int coda_setattr(struct dentry *, struct iattr *);
index 53d2edb709b3767f1b1ac6eaf7d9d52ace6bcf4b..8252b045e62489523452bdec777d8bd630e20cfc 100644 (file)
@@ -60,6 +60,8 @@ extern int dir_notify_enable;
 #define MAY_WRITE 2
 #define MAY_READ 4
 #define MAY_APPEND 8
+#define MAY_ACCESS 16
+#define MAY_OPEN 32
 
 #define FMODE_READ 1
 #define FMODE_WRITE 2
@@ -277,7 +279,7 @@ extern int dir_notify_enable;
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 #include <linux/dcache.h>
-#include <linux/namei.h>
+#include <linux/path.h>
 #include <linux/stat.h>
 #include <linux/cache.h>
 #include <linux/kobject.h>
@@ -318,22 +320,23 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
  * Attribute flags.  These should be or-ed together to figure out what
  * has been changed!
  */
-#define ATTR_MODE      1
-#define ATTR_UID       2
-#define ATTR_GID       4
-#define ATTR_SIZE      8
-#define ATTR_ATIME     16
-#define ATTR_MTIME     32
-#define ATTR_CTIME     64
-#define ATTR_ATIME_SET 128
-#define ATTR_MTIME_SET 256
-#define ATTR_FORCE     512     /* Not a change, but a change it */
-#define ATTR_ATTR_FLAG 1024
-#define ATTR_KILL_SUID 2048
-#define ATTR_KILL_SGID 4096
-#define ATTR_FILE      8192
-#define ATTR_KILL_PRIV 16384
-#define ATTR_OPEN      32768   /* Truncating from open(O_TRUNC) */
+#define ATTR_MODE      (1 << 0)
+#define ATTR_UID       (1 << 1)
+#define ATTR_GID       (1 << 2)
+#define ATTR_SIZE      (1 << 3)
+#define ATTR_ATIME     (1 << 4)
+#define ATTR_MTIME     (1 << 5)
+#define ATTR_CTIME     (1 << 6)
+#define ATTR_ATIME_SET (1 << 7)
+#define ATTR_MTIME_SET (1 << 8)
+#define ATTR_FORCE     (1 << 9) /* Not a change, but a change it */
+#define ATTR_ATTR_FLAG (1 << 10)
+#define ATTR_KILL_SUID (1 << 11)
+#define ATTR_KILL_SGID (1 << 12)
+#define ATTR_FILE      (1 << 13)
+#define ATTR_KILL_PRIV (1 << 14)
+#define ATTR_OPEN      (1 << 15) /* Truncating from open(O_TRUNC) */
+#define ATTR_TIMES_SET (1 << 16)
 
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
@@ -792,7 +795,7 @@ struct file {
 #define f_dentry       f_path.dentry
 #define f_vfsmnt       f_path.mnt
        const struct file_operations    *f_op;
-       atomic_t                f_count;
+       atomic_long_t           f_count;
        unsigned int            f_flags;
        mode_t                  f_mode;
        loff_t                  f_pos;
@@ -821,8 +824,8 @@ extern spinlock_t files_lock;
 #define file_list_lock() spin_lock(&files_lock);
 #define file_list_unlock() spin_unlock(&files_lock);
 
-#define get_file(x)    atomic_inc(&(x)->f_count)
-#define file_count(x)  atomic_read(&(x)->f_count)
+#define get_file(x)    atomic_long_inc(&(x)->f_count)
+#define file_count(x)  atomic_long_read(&(x)->f_count)
 
 #ifdef CONFIG_DEBUG_WRITECOUNT
 static inline void file_take_write(struct file *f)
@@ -1136,7 +1139,7 @@ extern int vfs_permission(struct nameidata *, int);
 extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
 extern int vfs_mkdir(struct inode *, struct dentry *, int);
 extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
-extern int vfs_symlink(struct inode *, struct dentry *, const char *, int);
+extern int vfs_symlink(struct inode *, struct dentry *, const char *);
 extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *);
@@ -1272,7 +1275,7 @@ struct inode_operations {
        void * (*follow_link) (struct dentry *, struct nameidata *);
        void (*put_link) (struct dentry *, struct nameidata *, void *);
        void (*truncate) (struct inode *);
-       int (*permission) (struct inode *, int, struct nameidata *);
+       int (*permission) (struct inode *, int);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -1696,9 +1699,9 @@ extern void init_special_inode(struct inode *, umode_t, dev_t);
 extern void make_bad_inode(struct inode *);
 extern int is_bad_inode(struct inode *);
 
-extern const struct file_operations read_fifo_fops;
-extern const struct file_operations write_fifo_fops;
-extern const struct file_operations rdwr_fifo_fops;
+extern const struct file_operations read_pipefifo_fops;
+extern const struct file_operations write_pipefifo_fops;
+extern const struct file_operations rdwr_pipefifo_fops;
 
 extern int fs_may_remount_ro(struct super_block *);
 
@@ -1767,7 +1770,7 @@ extern int do_remount_sb(struct super_block *sb, int flags,
 extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct iattr *);
-extern int permission(struct inode *, int, struct nameidata *);
+extern int inode_permission(struct inode *, int);
 extern int generic_permission(struct inode *, int,
                int (*check_acl)(struct inode *, int));
 
@@ -1831,7 +1834,7 @@ extern void clear_inode(struct inode *);
 extern void destroy_inode(struct inode *);
 extern struct inode *new_inode(struct super_block *);
 extern int should_remove_suid(struct dentry *);
-extern int remove_suid(struct dentry *);
+extern int file_remove_suid(struct file *);
 
 extern void __insert_inode_hash(struct inode *, unsigned long hashval);
 extern void remove_inode_hash(struct inode *);
index 282f542191294112610661ac76f0e5fa015435f8..9e5a06e78d0290870a5504e0ba277de476beae5a 100644 (file)
@@ -7,7 +7,7 @@ struct fs_struct {
        atomic_t count;
        rwlock_t lock;
        int umask;
-       struct path root, pwd, altroot;
+       struct path root, pwd;
 };
 
 #define INIT_FS {                              \
@@ -19,7 +19,6 @@ struct fs_struct {
 extern struct kmem_cache *fs_cachep;
 
 extern void exit_fs(struct task_struct *);
-extern void set_fs_altroot(void);
 extern void set_fs_root(struct fs_struct *, struct path *);
 extern void set_fs_pwd(struct fs_struct *, struct path *);
 extern struct fs_struct *copy_fs_struct(struct fs_struct *);
index 9a71d4cc88c8f399c63109a21a5dcc5ec9847270..32e0ef0f6e1fcdd7f67d83ffe985e6cbe8eb9a08 100644 (file)
@@ -273,7 +273,10 @@ struct hstate {};
 #define huge_page_mask(h) PAGE_MASK
 #define huge_page_order(h) 0
 #define huge_page_shift(h) PAGE_SHIFT
-#define pages_per_huge_page(h) 1
+static inline unsigned int pages_per_huge_page(struct hstate *h)
+{
+       return 1;
+}
 #endif
 
 #endif /* _LINUX_HUGETLB_H */
index 6b563cae23df22c768f63cbb5610d35cd6771a26..da720bc3eb15c2ec7f930a7c9ac8335a6f8ec3c4 100644 (file)
@@ -7,9 +7,6 @@
  *
  * For licensing information, see the file 'LICENCE' in the
  * jffs2 directory.
- *
- * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $
- *
  */
 
 #ifndef __LINUX_JFFS2_H__
diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h
new file mode 100644 (file)
index 0000000..6b71d2d
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __mISDNdsp_H__
+#define __mISDNdsp_H__
+
+struct mISDN_dsp_element_arg {
+       char    *name;
+       char    *def;
+       char    *desc;
+};
+
+struct mISDN_dsp_element {
+       char    *name;
+       void    *(*new)(const char *arg);
+       void    (*free)(void *p);
+       void    (*process_tx)(void *p, unsigned char *data, int len);
+       void    (*process_rx)(void *p, unsigned char *data, int len);
+       int     num_args;
+       struct mISDN_dsp_element_arg
+               *args;
+};
+
+extern int  mISDN_dsp_element_register(struct mISDN_dsp_element *elem);
+extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem);
+
+struct dsp_features {
+       int     hfc_id; /* unique id to identify the chip (or -1) */
+       int     hfc_dtmf; /* set if HFCmulti card supports dtmf */
+       int     hfc_loops; /* set if card supports tone loops */
+       int     hfc_echocanhw; /* set if card supports echocancelation*/
+       int     pcm_id; /* unique id to identify the pcm bus (or -1) */
+       int     pcm_slots; /* number of slots on the pcm bus */
+       int     pcm_banks; /* number of IO banks of pcm bus */
+       int     unclocked; /* data is not clocked (has jitter/loss) */
+       int     unordered; /* data is unordered (packets have index) */
+};
+
+#endif
+
diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h
new file mode 100644 (file)
index 0000000..e794dfb
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ *   Basic declarations for the mISDN HW channels
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MISDNHW_H
+#define MISDNHW_H
+#include <linux/mISDNif.h>
+#include <linux/timer.h>
+
+/*
+ * HW DEBUG 0xHHHHGGGG
+ * H - hardware driver specific bits
+ * G - for all drivers
+ */
+
+#define DEBUG_HW               0x00000001
+#define DEBUG_HW_OPEN          0x00000002
+#define DEBUG_HW_DCHANNEL      0x00000100
+#define DEBUG_HW_DFIFO         0x00000200
+#define DEBUG_HW_BCHANNEL      0x00001000
+#define DEBUG_HW_BFIFO         0x00002000
+
+#define MAX_DFRAME_LEN_L1      300
+#define MAX_MON_FRAME          32
+#define MAX_LOG_SPACE          2048
+#define MISDN_COPY_SIZE                32
+
+/* channel->Flags bit field */
+#define FLG_TX_BUSY            0       /* tx_buf in use */
+#define FLG_TX_NEXT            1       /* next_skb in use */
+#define FLG_L1_BUSY            2       /* L1 is permanent busy */
+#define FLG_L2_ACTIVATED       3       /* activated from L2 */
+#define FLG_OPEN               5       /* channel is in use */
+#define FLG_ACTIVE             6       /* channel is activated */
+#define FLG_BUSY_TIMER         7
+/* channel type */
+#define FLG_DCHANNEL           8       /* channel is D-channel */
+#define FLG_BCHANNEL           9       /* channel is B-channel */
+#define FLG_ECHANNEL           10      /* channel is E-channel */
+#define FLG_TRANSPARENT                12      /* channel use transparent data */
+#define FLG_HDLC               13      /* channel use hdlc data */
+#define FLG_L2DATA             14      /* channel use L2 DATA primitivs */
+#define FLG_ORIGIN             15      /* channel is on origin site */
+/* channel specific stuff */
+/* arcofi specific */
+#define FLG_ARCOFI_TIMER       16
+#define FLG_ARCOFI_ERROR       17
+/* isar specific */
+#define FLG_INITIALIZED                16
+#define FLG_DLEETX             17
+#define FLG_LASTDLE            18
+#define FLG_FIRST              19
+#define FLG_LASTDATA           20
+#define FLG_NMD_DATA           21
+#define FLG_FTI_RUN            22
+#define FLG_LL_OK              23
+#define FLG_LL_CONN            24
+#define FLG_DTMFSEND           25
+
+/* workq events */
+#define FLG_RECVQUEUE          30
+#define        FLG_PHCHANGE            31
+
+#define schedule_event(s, ev)  do { \
+                                       test_and_set_bit(ev, &((s)->Flags)); \
+                                       schedule_work(&((s)->workq)); \
+                               } while (0)
+
+struct dchannel {
+       struct mISDNdevice      dev;
+       u_long                  Flags;
+       struct work_struct      workq;
+       void                    (*phfunc) (struct dchannel *);
+       u_int                   state;
+       void                    *l1;
+       /* HW access */
+       u_char                  (*read_reg) (void *, u_char);
+       void                    (*write_reg) (void *, u_char, u_char);
+       void                    (*read_fifo) (void *, u_char *, int);
+       void                    (*write_fifo) (void *, u_char *, int);
+       void                    *hw;
+       int                     slot;   /* multiport card channel slot */
+       struct timer_list       timer;
+       /* receive data */
+       struct sk_buff          *rx_skb;
+       int                     maxlen;
+       /* send data */
+       struct sk_buff_head     squeue;
+       struct sk_buff_head     rqueue;
+       struct sk_buff          *tx_skb;
+       int                     tx_idx;
+       int                     debug;
+       /* statistics */
+       int                     err_crc;
+       int                     err_tx;
+       int                     err_rx;
+};
+
+typedef int    (dchannel_l1callback)(struct dchannel *, u_int);
+extern int     create_l1(struct dchannel *, dchannel_l1callback *);
+
+/* private L1 commands */
+#define INFO0          0x8002
+#define INFO1          0x8102
+#define INFO2          0x8202
+#define INFO3_P8       0x8302
+#define INFO3_P10      0x8402
+#define INFO4_P8       0x8502
+#define INFO4_P10      0x8602
+#define LOSTFRAMING    0x8702
+#define ANYSIGNAL      0x8802
+#define HW_POWERDOWN   0x8902
+#define HW_RESET_REQ   0x8a02
+#define HW_POWERUP_REQ 0x8b02
+#define HW_DEACT_REQ   0x8c02
+#define HW_ACTIVATE_REQ        0x8e02
+#define HW_D_NOBLOCKED  0x8f02
+#define HW_RESET_IND   0x9002
+#define HW_POWERUP_IND 0x9102
+#define HW_DEACT_IND   0x9202
+#define HW_ACTIVATE_IND        0x9302
+#define HW_DEACT_CNF   0x9402
+#define HW_TESTLOOP    0x9502
+#define HW_TESTRX_RAW  0x9602
+#define HW_TESTRX_HDLC 0x9702
+#define HW_TESTRX_OFF  0x9802
+
+struct layer1;
+extern int     l1_event(struct layer1 *, u_int);
+
+
+struct bchannel {
+       struct mISDNchannel     ch;
+       int                     nr;
+       u_long                  Flags;
+       struct work_struct      workq;
+       u_int                   state;
+       /* HW access */
+       u_char                  (*read_reg) (void *, u_char);
+       void                    (*write_reg) (void *, u_char, u_char);
+       void                    (*read_fifo) (void *, u_char *, int);
+       void                    (*write_fifo) (void *, u_char *, int);
+       void                    *hw;
+       int                     slot;   /* multiport card channel slot */
+       struct timer_list       timer;
+       /* receive data */
+       struct sk_buff          *rx_skb;
+       int                     maxlen;
+       /* send data */
+       struct sk_buff          *next_skb;
+       struct sk_buff          *tx_skb;
+       struct sk_buff_head     rqueue;
+       int                     rcount;
+       int                     tx_idx;
+       int                     debug;
+       /* statistics */
+       int                     err_crc;
+       int                     err_tx;
+       int                     err_rx;
+};
+
+extern int     mISDN_initdchannel(struct dchannel *, int, void *);
+extern int     mISDN_initbchannel(struct bchannel *, int);
+extern int     mISDN_freedchannel(struct dchannel *);
+extern int     mISDN_freebchannel(struct bchannel *);
+extern void    queue_ch_frame(struct mISDNchannel *, u_int,
+                       int, struct sk_buff *);
+extern int     dchannel_senddata(struct dchannel *, struct sk_buff *);
+extern int     bchannel_senddata(struct bchannel *, struct sk_buff *);
+extern void    recv_Dchannel(struct dchannel *);
+extern void    recv_Bchannel(struct bchannel *);
+extern void    recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
+extern void    recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
+extern void    confirm_Bsend(struct bchannel *bch);
+extern int     get_next_bframe(struct bchannel *);
+extern int     get_next_dframe(struct dchannel *);
+
+#endif
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
new file mode 100644 (file)
index 0000000..5c948f3
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This code 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 LESSER GENERAL PUBLIC LICENSE for more details.
+ *
+ */
+
+#ifndef mISDNIF_H
+#define mISDNIF_H
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+
+/*
+ * ABI Version 32 bit
+ *
+ * <8 bit> Major version
+ *             - changed if any interface become backwards incompatible
+ *
+ * <8 bit> Minor version
+ *              - changed if any interface is extended but backwards compatible
+ *
+ * <16 bit> Release number
+ *              - should be incremented on every checkin
+ */
+#define        MISDN_MAJOR_VERSION     1
+#define        MISDN_MINOR_VERSION     0
+#define MISDN_RELEASE          18
+
+/* primitives for information exchange
+ * generell format
+ * <16  bit  0 >
+ * <8  bit command>
+ *    BIT 8 = 1 LAYER private
+ *    BIT 7 = 1 answer
+ *    BIT 6 = 1 DATA
+ * <8  bit target layer mask>
+ *
+ * Layer = 00 is reserved for general commands
+   Layer = 01  L2 -> HW
+   Layer = 02  HW -> L2
+   Layer = 04  L3 -> L2
+   Layer = 08  L2 -> L3
+ * Layer = FF is reserved for broadcast commands
+ */
+
+#define MISDN_CMDMASK          0xff00
+#define MISDN_LAYERMASK                0x00ff
+
+/* generell commands */
+#define OPEN_CHANNEL           0x0100
+#define CLOSE_CHANNEL          0x0200
+#define CONTROL_CHANNEL                0x0300
+#define CHECK_DATA             0x0400
+
+/* layer 2 -> layer 1 */
+#define PH_ACTIVATE_REQ                0x0101
+#define PH_DEACTIVATE_REQ      0x0201
+#define PH_DATA_REQ            0x2001
+#define MPH_ACTIVATE_REQ       0x0501
+#define MPH_DEACTIVATE_REQ     0x0601
+#define MPH_INFORMATION_REQ    0x0701
+#define PH_CONTROL_REQ         0x0801
+
+/* layer 1 -> layer 2 */
+#define PH_ACTIVATE_IND                0x0102
+#define PH_ACTIVATE_CNF                0x4102
+#define PH_DEACTIVATE_IND      0x0202
+#define PH_DEACTIVATE_CNF      0x4202
+#define PH_DATA_IND            0x2002
+#define MPH_ACTIVATE_IND       0x0502
+#define MPH_DEACTIVATE_IND     0x0602
+#define MPH_INFORMATION_IND    0x0702
+#define PH_DATA_CNF            0x6002
+#define PH_CONTROL_IND         0x0802
+#define PH_CONTROL_CNF         0x4802
+
+/* layer 3 -> layer 2 */
+#define DL_ESTABLISH_REQ       0x1004
+#define DL_RELEASE_REQ         0x1104
+#define DL_DATA_REQ            0x3004
+#define DL_UNITDATA_REQ                0x3104
+#define DL_INFORMATION_REQ     0x0004
+
+/* layer 2 -> layer 3 */
+#define DL_ESTABLISH_IND       0x1008
+#define DL_ESTABLISH_CNF       0x5008
+#define DL_RELEASE_IND         0x1108
+#define DL_RELEASE_CNF         0x5108
+#define DL_DATA_IND            0x3008
+#define DL_UNITDATA_IND                0x3108
+#define DL_INFORMATION_IND     0x0008
+
+/* intern layer 2 managment */
+#define MDL_ASSIGN_REQ         0x1804
+#define MDL_ASSIGN_IND         0x1904
+#define MDL_REMOVE_REQ         0x1A04
+#define MDL_REMOVE_IND         0x1B04
+#define MDL_STATUS_UP_IND      0x1C04
+#define MDL_STATUS_DOWN_IND    0x1D04
+#define MDL_STATUS_UI_IND      0x1E04
+#define MDL_ERROR_IND          0x1F04
+#define MDL_ERROR_RSP          0x5F04
+
+/* DL_INFORMATION_IND types */
+#define DL_INFO_L2_CONNECT     0x0001
+#define DL_INFO_L2_REMOVED     0x0002
+
+/* PH_CONTROL types */
+/* TOUCH TONE IS 0x20XX  XX "0"..."9", "A","B","C","D","*","#" */
+#define DTMF_TONE_VAL          0x2000
+#define DTMF_TONE_MASK         0x007F
+#define DTMF_TONE_START                0x2100
+#define DTMF_TONE_STOP         0x2200
+#define DTMF_HFC_COEF          0x4000
+#define DSP_CONF_JOIN          0x2403
+#define DSP_CONF_SPLIT         0x2404
+#define DSP_RECEIVE_OFF                0x2405
+#define DSP_RECEIVE_ON         0x2406
+#define DSP_ECHO_ON            0x2407
+#define DSP_ECHO_OFF           0x2408
+#define DSP_MIX_ON             0x2409
+#define DSP_MIX_OFF            0x240a
+#define DSP_DELAY              0x240b
+#define DSP_JITTER             0x240c
+#define DSP_TXDATA_ON          0x240d
+#define DSP_TXDATA_OFF         0x240e
+#define DSP_TX_DEJITTER                0x240f
+#define DSP_TX_DEJ_OFF         0x2410
+#define DSP_TONE_PATT_ON       0x2411
+#define DSP_TONE_PATT_OFF      0x2412
+#define DSP_VOL_CHANGE_TX      0x2413
+#define DSP_VOL_CHANGE_RX      0x2414
+#define DSP_BF_ENABLE_KEY      0x2415
+#define DSP_BF_DISABLE         0x2416
+#define DSP_BF_ACCEPT          0x2416
+#define DSP_BF_REJECT          0x2417
+#define DSP_PIPELINE_CFG       0x2418
+#define HFC_VOL_CHANGE_TX      0x2601
+#define HFC_VOL_CHANGE_RX      0x2602
+#define HFC_SPL_LOOP_ON                0x2603
+#define HFC_SPL_LOOP_OFF       0x2604
+
+/* DSP_TONE_PATT_ON parameter */
+#define TONE_OFF                       0x0000
+#define TONE_GERMAN_DIALTONE           0x0001
+#define TONE_GERMAN_OLDDIALTONE                0x0002
+#define TONE_AMERICAN_DIALTONE         0x0003
+#define TONE_GERMAN_DIALPBX            0x0004
+#define TONE_GERMAN_OLDDIALPBX         0x0005
+#define TONE_AMERICAN_DIALPBX          0x0006
+#define TONE_GERMAN_RINGING            0x0007
+#define TONE_GERMAN_OLDRINGING         0x0008
+#define TONE_AMERICAN_RINGPBX          0x000b
+#define TONE_GERMAN_RINGPBX            0x000c
+#define TONE_GERMAN_OLDRINGPBX         0x000d
+#define TONE_AMERICAN_RINGING          0x000e
+#define TONE_GERMAN_BUSY               0x000f
+#define TONE_GERMAN_OLDBUSY            0x0010
+#define TONE_AMERICAN_BUSY             0x0011
+#define TONE_GERMAN_HANGUP             0x0012
+#define TONE_GERMAN_OLDHANGUP          0x0013
+#define TONE_AMERICAN_HANGUP           0x0014
+#define TONE_SPECIAL_INFO              0x0015
+#define TONE_GERMAN_GASSENBESETZT      0x0016
+#define TONE_GERMAN_AUFSCHALTTON       0x0016
+
+/* MPH_INFORMATION_IND */
+#define L1_SIGNAL_LOS_OFF      0x0010
+#define L1_SIGNAL_LOS_ON       0x0011
+#define L1_SIGNAL_AIS_OFF      0x0012
+#define L1_SIGNAL_AIS_ON       0x0013
+#define L1_SIGNAL_RDI_OFF      0x0014
+#define L1_SIGNAL_RDI_ON       0x0015
+#define L1_SIGNAL_SLIP_RX      0x0020
+#define L1_SIGNAL_SLIP_TX      0x0021
+
+/*
+ * protocol ids
+ * D channel 1-31
+ * B channel 33 - 63
+ */
+
+#define ISDN_P_NONE            0
+#define ISDN_P_BASE            0
+#define ISDN_P_TE_S0           0x01
+#define ISDN_P_NT_S0           0x02
+#define ISDN_P_TE_E1           0x03
+#define ISDN_P_NT_E1           0x04
+#define ISDN_P_LAPD_TE         0x10
+#define        ISDN_P_LAPD_NT          0x11
+
+#define ISDN_P_B_MASK          0x1f
+#define ISDN_P_B_START         0x20
+
+#define ISDN_P_B_RAW           0x21
+#define ISDN_P_B_HDLC          0x22
+#define ISDN_P_B_X75SLP                0x23
+#define ISDN_P_B_L2DTMF                0x24
+#define ISDN_P_B_L2DSP         0x25
+#define ISDN_P_B_L2DSPHDLC     0x26
+
+#define OPTION_L2_PMX          1
+#define OPTION_L2_PTP          2
+#define OPTION_L2_FIXEDTEI     3
+#define OPTION_L2_CLEANUP      4
+
+/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
+#define MISDN_MAX_IDLEN                20
+
+struct mISDNhead {
+       unsigned int    prim;
+       unsigned int    id;
+}  __attribute__((packed));
+
+#define MISDN_HEADER_LEN       sizeof(struct mISDNhead)
+#define MAX_DATA_SIZE          2048
+#define MAX_DATA_MEM           (MAX_DATA_SIZE + MISDN_HEADER_LEN)
+#define MAX_DFRAME_LEN         260
+
+#define MISDN_ID_ADDR_MASK     0xFFFF
+#define MISDN_ID_TEI_MASK      0xFF00
+#define MISDN_ID_SAPI_MASK     0x00FF
+#define MISDN_ID_TEI_ANY       0x7F00
+
+#define MISDN_ID_ANY           0xFFFF
+#define MISDN_ID_NONE          0xFFFE
+
+#define GROUP_TEI              127
+#define TEI_SAPI               63
+#define CTRL_SAPI              0
+
+#define MISDN_CHMAP_SIZE       4
+
+#define SOL_MISDN      0
+
+struct sockaddr_mISDN {
+       sa_family_t    family;
+       unsigned char   dev;
+       unsigned char   channel;
+       unsigned char   sapi;
+       unsigned char   tei;
+};
+
+/* timer device ioctl */
+#define IMADDTIMER     _IOR('I', 64, int)
+#define IMDELTIMER     _IOR('I', 65, int)
+/* socket ioctls */
+#define        IMGETVERSION    _IOR('I', 66, int)
+#define        IMGETCOUNT      _IOR('I', 67, int)
+#define IMGETDEVINFO   _IOR('I', 68, int)
+#define IMCTRLREQ      _IOR('I', 69, int)
+#define IMCLEAR_L2     _IOR('I', 70, int)
+
+struct mISDNversion {
+       unsigned char   major;
+       unsigned char   minor;
+       unsigned short  release;
+};
+
+struct mISDN_devinfo {
+       u_int                   id;
+       u_int                   Dprotocols;
+       u_int                   Bprotocols;
+       u_int                   protocol;
+       u_long                  channelmap[MISDN_CHMAP_SIZE];
+       u_int                   nrbchan;
+       char                    name[MISDN_MAX_IDLEN];
+};
+
+/* CONTROL_CHANNEL parameters */
+#define MISDN_CTRL_GETOP               0x0000
+#define MISDN_CTRL_LOOP                        0x0001
+#define MISDN_CTRL_CONNECT             0x0002
+#define MISDN_CTRL_DISCONNECT          0x0004
+#define MISDN_CTRL_PCMCONNECT          0x0010
+#define MISDN_CTRL_PCMDISCONNECT       0x0020
+#define MISDN_CTRL_SETPEER             0x0040
+#define MISDN_CTRL_UNSETPEER           0x0080
+#define MISDN_CTRL_RX_OFF              0x0100
+#define MISDN_CTRL_HW_FEATURES_OP      0x2000
+#define MISDN_CTRL_HW_FEATURES         0x2001
+#define MISDN_CTRL_HFC_OP              0x4000
+#define MISDN_CTRL_HFC_PCM_CONN                0x4001
+#define MISDN_CTRL_HFC_PCM_DISC                0x4002
+#define MISDN_CTRL_HFC_CONF_JOIN       0x4003
+#define MISDN_CTRL_HFC_CONF_SPLIT      0x4004
+#define MISDN_CTRL_HFC_RECEIVE_OFF     0x4005
+#define MISDN_CTRL_HFC_RECEIVE_ON      0x4006
+#define MISDN_CTRL_HFC_ECHOCAN_ON      0x4007
+#define MISDN_CTRL_HFC_ECHOCAN_OFF     0x4008
+
+
+/* socket options */
+#define MISDN_TIME_STAMP               0x0001
+
+struct mISDN_ctrl_req {
+       int             op;
+       int             channel;
+       int             p1;
+       int             p2;
+};
+
+/* muxer options */
+#define MISDN_OPT_ALL          1
+#define MISDN_OPT_TEIMGR       2
+
+#ifdef __KERNEL__
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/completion.h>
+
+#define DEBUG_CORE             0x000000ff
+#define DEBUG_CORE_FUNC                0x00000002
+#define DEBUG_SOCKET           0x00000004
+#define DEBUG_MANAGER          0x00000008
+#define DEBUG_SEND_ERR         0x00000010
+#define DEBUG_MSG_THREAD       0x00000020
+#define DEBUG_QUEUE_FUNC       0x00000040
+#define DEBUG_L1               0x0000ff00
+#define DEBUG_L1_FSM           0x00000200
+#define DEBUG_L2               0x00ff0000
+#define DEBUG_L2_FSM           0x00020000
+#define DEBUG_L2_CTRL          0x00040000
+#define DEBUG_L2_RECV          0x00080000
+#define DEBUG_L2_TEI           0x00100000
+#define DEBUG_L2_TEIFSM                0x00200000
+#define DEBUG_TIMER            0x01000000
+
+#define mISDN_HEAD_P(s)                ((struct mISDNhead *)&s->cb[0])
+#define mISDN_HEAD_PRIM(s)     (((struct mISDNhead *)&s->cb[0])->prim)
+#define mISDN_HEAD_ID(s)       (((struct mISDNhead *)&s->cb[0])->id)
+
+/* socket states */
+#define MISDN_OPEN     1
+#define MISDN_BOUND    2
+#define MISDN_CLOSED   3
+
+struct mISDNchannel;
+struct mISDNdevice;
+struct mISDNstack;
+
+struct channel_req {
+       u_int                   protocol;
+       struct sockaddr_mISDN   adr;
+       struct mISDNchannel     *ch;
+};
+
+typedef        int     (ctrl_func_t)(struct mISDNchannel *, u_int, void *);
+typedef        int     (send_func_t)(struct mISDNchannel *, struct sk_buff *);
+typedef int    (create_func_t)(struct channel_req *);
+
+struct Bprotocol {
+       struct list_head        list;
+       char                    *name;
+       u_int                   Bprotocols;
+       create_func_t           *create;
+};
+
+struct mISDNchannel {
+       struct list_head        list;
+       u_int                   protocol;
+       u_int                   nr;
+       u_long                  opt;
+       u_int                   addr;
+       struct mISDNstack       *st;
+       struct mISDNchannel     *peer;
+       send_func_t             *send;
+       send_func_t             *recv;
+       ctrl_func_t             *ctrl;
+};
+
+struct mISDN_sock_list {
+       struct hlist_head       head;
+       rwlock_t                lock;
+};
+
+struct mISDN_sock {
+       struct sock             sk;
+       struct mISDNchannel     ch;
+       u_int                   cmask;
+       struct mISDNdevice      *dev;
+};
+
+
+
+struct mISDNdevice {
+       struct mISDNchannel     D;
+       u_int                   id;
+       char                    name[MISDN_MAX_IDLEN];
+       u_int                   Dprotocols;
+       u_int                   Bprotocols;
+       u_int                   nrbchan;
+       u_long                  channelmap[MISDN_CHMAP_SIZE];
+       struct list_head        bchannels;
+       struct mISDNchannel     *teimgr;
+       struct device           dev;
+};
+
+struct mISDNstack {
+       u_long                  status;
+       struct mISDNdevice      *dev;
+       struct task_struct      *thread;
+       struct completion       *notify;
+       wait_queue_head_t       workq;
+       struct sk_buff_head     msgq;
+       struct list_head        layer2;
+       struct mISDNchannel     *layer1;
+       struct mISDNchannel     own;
+       struct mutex            lmutex; /* protect lists */
+       struct mISDN_sock_list  l1sock;
+#ifdef MISDN_MSG_STATS
+       u_int                   msg_cnt;
+       u_int                   sleep_cnt;
+       u_int                   stopped_cnt;
+#endif
+};
+
+/* global alloc/queue dunctions */
+
+static inline struct sk_buff *
+mI_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+       struct sk_buff  *skb;
+
+       skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask);
+       if (likely(skb))
+               skb_reserve(skb, MISDN_HEADER_LEN);
+       return skb;
+}
+
+static inline struct sk_buff *
+_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask)
+{
+       struct sk_buff  *skb = mI_alloc_skb(len, gfp_mask);
+       struct mISDNhead *hh;
+
+       if (!skb)
+               return NULL;
+       if (len)
+               memcpy(skb_put(skb, len), dp, len);
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = id;
+       return skb;
+}
+
+static inline void
+_queue_data(struct mISDNchannel *ch, u_int prim,
+    u_int id, u_int len, void *dp, gfp_t gfp_mask)
+{
+       struct sk_buff          *skb;
+
+       if (!ch->peer)
+               return;
+       skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask);
+       if (!skb)
+               return;
+       if (ch->recv(ch->peer, skb))
+               dev_kfree_skb(skb);
+}
+
+/* global register/unregister functions */
+
+extern int     mISDN_register_device(struct mISDNdevice *, char *name);
+extern void    mISDN_unregister_device(struct mISDNdevice *);
+extern int     mISDN_register_Bprotocol(struct Bprotocol *);
+extern void    mISDN_unregister_Bprotocol(struct Bprotocol *);
+
+extern void    set_channel_address(struct mISDNchannel *, u_int, u_int);
+
+#endif /* __KERNEL__ */
+#endif /* mISDNIF_H */
index e27082cd650e09aaa05aa9bde9afa58dc7b0bc35..bf8f11982dae374f70f3b8b79494823ca96d2e36 100644 (file)
@@ -164,11 +164,13 @@ enum {
        MLX4_WQE_CTRL_SOLICITED         = 1 << 1,
        MLX4_WQE_CTRL_IP_CSUM           = 1 << 4,
        MLX4_WQE_CTRL_TCP_UDP_CSUM      = 1 << 5,
+       MLX4_WQE_CTRL_INS_VLAN          = 1 << 6,
 };
 
 struct mlx4_wqe_ctrl_seg {
        __be32                  owner_opcode;
-       u8                      reserved2[3];
+       __be16                  vlan_tag;
+       u8                      ins_vlan;
        u8                      fence_size;
        /*
         * High 24 bits are SRC remote buffer; low 8 bits are flags:
index 0d508ac17d6452b5a35429fd38704c8de2c13de4..ee6e822d59947312d3bc464508e9f38ff1686e91 100644 (file)
@@ -111,6 +111,8 @@ struct mmc_card {
        unsigned                num_info;       /* number of info strings */
        const char              **info;         /* info strings */
        struct sdio_func_tuple  *tuples;        /* unknown common tuples */
+
+       struct dentry           *debugfs_root;
 };
 
 #define mmc_card_mmc(c)                ((c)->type == MMC_TYPE_MMC)
index 10a2080086ca9e6402eec37178bd8dea384f3f92..9c288c9098783cfc3ef1b4d6357081b7d3f44849 100644 (file)
@@ -157,6 +157,8 @@ struct mmc_host {
        struct led_trigger      *led;           /* activity led */
 #endif
 
+       struct dentry           *debugfs_root;
+
        unsigned long           private[0] ____cacheline_aligned;
 };
 
index 4374d1adeb4b755b4227bccf1f3afaa0b214fc35..b5efaa2132ab2ff79b624e09ab7eba0adcd18a04 100644 (file)
@@ -47,7 +47,7 @@ struct vfsmount {
        struct list_head mnt_child;     /* and going through their mnt_child */
        int mnt_flags;
        /* 4 bytes hole on 64bits arches */
-       char *mnt_devname;              /* Name of device e.g. /dev/dsk/hda1 */
+       const char *mnt_devname;        /* Name of device e.g. /dev/dsk/hda1 */
        struct list_head mnt_list;
        struct list_head mnt_expire;    /* link in fs-specific expiry list */
        struct list_head mnt_share;     /* circular list of shared mounts */
index 9a6e2f953cba17292fb222bc616c70a1563b4439..310e616064158d68afb3e3fcdfcc31a56421b1bb 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: blktrans.h,v 1.6 2005/11/07 11:14:54 gleixner Exp $
- *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
  * Interface to Linux block layer for MTD 'translation layers'.
index b0ddf4b25862392d90443a8ad88a9d6c7ad68b95..d6fb115f5a0714794c077ea6ff98ea18243d537e 100644 (file)
@@ -1,7 +1,6 @@
 
 /* Common Flash Interface structures
  * See http://support.intel.com/design/flash/technote/index.htm
- * $Id: cfi.h,v 1.57 2005/11/15 23:28:17 tpoynor Exp $
  */
 
 #ifndef __MTD_CFI_H__
index 25724f7d3867a5ee69eb671c9e9713061a165fd4..d802f7736be3a474b0cee222b5d3648d8caf1812 100644 (file)
@@ -1,8 +1,3 @@
-/*
- * $Id: cfi_endian.h,v 1.11 2002/01/30 23:20:48 awozniak Exp $
- *
- */
-
 #include <asm/byteorder.h>
 
 #ifndef CONFIG_MTD_CFI_ADV_OPTIONS
index ed8dc675521917b14d12a492abe5a7c8f754a8e6..c02f3d264ecf8d24181463497aafddaa66197447 100644 (file)
@@ -4,8 +4,6 @@
  * (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
  *
  * This code is GPL
- *
- * $Id: concat.h,v 1.1 2002/03/08 16:34:36 rkaiser Exp $
  */
 
 #ifndef MTD_CONCAT_H
index 9addd073bf15957ba8ce3d11675e614b042f4748..0a6d516ab71d9ba2eb97a7789593b040fb99dcb8 100644 (file)
@@ -6,8 +6,6 @@
  * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
  * Copyright (C) 2002-2003 SnapGear Inc
  *
- * $Id: doc2000.h,v 1.25 2005/11/07 11:14:54 gleixner Exp $
- *
  * Released under GPL
  */
 
index 39e7d2a1be9a5a93657e3af6d62c8f9dd901ac88..08dd131301c1fe98e87d900f9173cc054f953a84 100644 (file)
@@ -5,9 +5,6 @@
  * Contains information about the location and state of a given flash device
  *
  * (C) 2000 Red Hat. GPLd.
- *
- * $Id: flashchip.h,v 1.18 2005/11/07 11:14:54 gleixner Exp $
- *
  */
 
 #ifndef __MTD_FLASHCHIP_H__
index d996091133076141ab35abef365017fd956ae556..0be442f881dd66eb9e324f4f9da0790afa1ab4d4 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ftl.h,v 1.7 2005/11/07 11:14:54 gleixner Exp $
- *
  * Derived from (and probably identical to):
  * ftl.h 1.7 1999/10/25 20:23:17
  *
index 256e7342ed1eead34a474f13d762f7b91885127b..df362ddf2949f0f9a49bbd278f87dfa0ec8f5bae 100644 (file)
@@ -1,7 +1,6 @@
 /*
  * (C) 2001, 2001 Red Hat, Inc.
  * GPL'd
- * $Id: gen_probe.h,v 1.4 2005/11/07 11:14:54 gleixner Exp $
  */
 
 #ifndef __LINUX_MTD_GEN_PROBE_H__
index 85fd041d44ad30ca9fea26eef7dfafb164d611f2..64ee53ce95a94cbe483ec39cbe8a5d50ecad3dd1 100644 (file)
@@ -2,8 +2,6 @@
  *     inftl.h -- defines to support the Inverse NAND Flash Translation Layer
  *
  *     (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
- *
- *     $Id: inftl.h,v 1.7 2005/06/13 13:08:45 sean Exp $
  */
 
 #ifndef __MTD_INFTL_H__
@@ -52,8 +50,6 @@ struct INFTLrecord {
 int INFTL_mount(struct INFTLrecord *s);
 int INFTL_formatblock(struct INFTLrecord *s, int block);
 
-extern char inftlmountrev[];
-
 void INFTL_dumptables(struct INFTLrecord *s);
 void INFTL_dumpVUchains(struct INFTLrecord *s);
 
index 9c1d95491f8b0a7d208257b39f6fd794d713be84..aa30244492c6770a5ccdaf9c6a21536c9f6ea579 100644 (file)
@@ -1,6 +1,5 @@
 
 /* Overhauled routines for dealing with different mmap regions of flash */
-/* $Id: map.h,v 1.54 2005/11/07 11:14:54 gleixner Exp $ */
 
 #ifndef __LINUX_MTD_MAP_H__
 #define __LINUX_MTD_MAP_H__
index 8b5d49133ec616a80fdc7f375b95e9cc244e16cc..4ed40caff4e58161a8e5d545421e375d0b81f7e2 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtd.h,v 1.61 2005/11/07 11:14:54 gleixner Exp $
- *
  * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
  *
  * Released under GPL
index 53ea3dc8b0e8ac98347d28170533de9a77dd55f7..83f678702dff3903a262956e21c306bc068c9bbf 100644 (file)
@@ -5,8 +5,6 @@
  *                     Steven J. Hill <sjhill@realitydiluted.com>
  *                    Thomas Gleixner <tglx@linutronix.de>
  *
- * $Id: nand.h,v 1.74 2005/09/15 13:58:50 vwool Exp $
- *
  * 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.
@@ -179,6 +177,7 @@ typedef enum {
 #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
 #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
 #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
+#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT))
 
 /* Mask to zero out the chip options, which come from the id table */
 #define NAND_CHIPOPTIONS_MSK   (0x0000ffff & ~NAND_NO_AUTOINCR)
@@ -276,6 +275,10 @@ struct nand_ecc_ctrl {
        int                     (*read_page)(struct mtd_info *mtd,
                                             struct nand_chip *chip,
                                             uint8_t *buf);
+       int                     (*read_subpage)(struct mtd_info *mtd,
+                                            struct nand_chip *chip,
+                                            uint32_t offs, uint32_t len,
+                                            uint8_t *buf);
        void                    (*write_page)(struct mtd_info *mtd,
                                              struct nand_chip *chip,
                                              const uint8_t *buf);
index 12c5bc342ead0341838b2053071df6b80d55dd6f..090da505425d77d105acb6133d63dfaaa7c95b7b 100644 (file)
@@ -3,8 +3,6 @@
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
- * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $
- *
  * 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.
index 001eec50cac6d12c12e3362ef1ca9687400357b8..dcaf611ed74826a5b2a018482e84b38449e08681 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $
- *
  * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
  */
 
index 7c37d7e55abcbc119f3496c08e92868343dcd782..5014f7a9f5dfe57a8df546d4cee4edb2e6a065df 100644 (file)
@@ -4,8 +4,6 @@
  * (C) 2000 Nicolas Pitre <nico@cam.org>
  *
  * This code is GPL
- *
- * $Id: partitions.h,v 1.17 2005/11/07 11:14:55 gleixner Exp $
  */
 
 #ifndef MTD_PARTITIONS_H
index 0dc07d5f33545ae37ec667f74ef16a3b593f7ad6..c8e63a5ee72e1a0cbbffc06a02a327d2c0772ed5 100644 (file)
@@ -2,8 +2,6 @@
  * For boards with physically mapped flash and using
  * drivers/mtd/maps/physmap.c mapping driver.
  *
- * $Id: physmap.h,v 1.4 2005/11/07 11:14:55 gleixner Exp $
- *
  * Copyright (C) 2003 MontaVista Software Inc.
  * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
  *
index 0e37ad07bce2bb4895d9913350958d80016b83d5..e07890aff1cf9523cb0fe4d07b0c06353da046c9 100644 (file)
@@ -6,8 +6,6 @@
  *
  * Generic platform device based RAM map
  *
- * $Id: plat-ram.h,v 1.2 2005/01/24 00:37:40 bjd Exp $
- *
  * 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.
index 5cc070c24d88646229bfd7f1f64aa06b76ebdbac..27ad40aed19f34faa11b0dcca3f9251290382413 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: pmc551.h,v 1.6 2005/11/07 11:14:55 gleixner Exp $
- *
  * PMC551 PCI Mezzanine Ram Device
  *
  * Author:
@@ -17,7 +15,7 @@
 
 #include <linux/mtd/mtd.h>
 
-#define PMC551_VERSION "$Id: pmc551.h,v 1.6 2005/11/07 11:14:55 gleixner Exp $\n"\
+#define PMC551_VERSION \
        "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"
 
 /*
index e9d40bdde48cba0f5d3083028e152aa6bac60269..36efcba15ecddc9a0557883b8690e1fe74bbf224 100644 (file)
@@ -11,8 +11,6 @@
  * 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.
- *
- * $Id: xip.h,v 1.5 2005/11/07 11:14:55 gleixner Exp $
  */
 
 #ifndef __LINUX_MTD_XIP_H__
index 24d88e98a62648180a553fdfb457837bcbcb5c00..68f8c3203c89d4d7c42404e4084051342206b875 100644 (file)
@@ -47,27 +47,24 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_DIRECTORY        2
 #define LOOKUP_CONTINUE                 4
 #define LOOKUP_PARENT          16
-#define LOOKUP_NOALT           32
 #define LOOKUP_REVAL           64
 /*
  * Intent data
  */
 #define LOOKUP_OPEN            (0x0100)
 #define LOOKUP_CREATE          (0x0200)
-#define LOOKUP_ACCESS          (0x0400)
-#define LOOKUP_CHDIR           (0x0800)
-
-extern int __user_walk(const char __user *, unsigned, struct nameidata *);
-extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *);
-#define user_path_walk(name,nd) \
-       __user_walk_fd(AT_FDCWD, name, LOOKUP_FOLLOW, nd)
-#define user_path_walk_link(name,nd) \
-       __user_walk_fd(AT_FDCWD, name, 0, nd)
+
+extern int user_path_at(int, const char __user *, unsigned, struct path *);
+
+#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
+#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
+#define user_path_dir(name, path) \
+       user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path)
+
 extern int path_lookup(const char *, unsigned, struct nameidata *);
 extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
                           const char *, unsigned int, struct nameidata *);
 
-extern int __user_path_lookup_open(const char __user *, unsigned lookup_flags, struct nameidata *nd, int open_flags);
 extern int path_lookup_open(int dfd, const char *name, unsigned lookup_flags, struct nameidata *, int open_flags);
 extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
                int (*open)(struct inode *, struct file *));
index 29d26191873428cecde2b29f3fd68b855684f614..78a5922a2f111dc7d08b8d7d5bef59c2511cef15 100644 (file)
@@ -42,7 +42,6 @@
 #include <linux/in.h>
 #include <linux/kref.h>
 #include <linux/mm.h>
-#include <linux/namei.h>
 #include <linux/pagemap.h>
 #include <linux/rbtree.h>
 #include <linux/rwsem.h>
@@ -332,7 +331,7 @@ extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
-extern int nfs_permission(struct inode *, int, struct nameidata *);
+extern int nfs_permission(struct inode *, int);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int nfs_attribute_timeout(struct inode *inode);
index c3b1761aba2693d431bc3f65e8987eaaaf8070b5..35a78415acccfab8f58a56fb3e6ef14449f0ee04 100644 (file)
 #define PCI_VENDOR_ID_TI               0x104c
 #define PCI_DEVICE_ID_TI_TVP4020       0x3d07
 #define PCI_DEVICE_ID_TI_4450          0x8011
+#define PCI_DEVICE_ID_TI_TSB43AB22     0x8023
 #define PCI_DEVICE_ID_TI_XX21_XX11     0x8031
 #define PCI_DEVICE_ID_TI_XX21_XX11_FM  0x8033
 #define PCI_DEVICE_ID_TI_XX21_XX11_SD  0x8034
 #define PCI_DEVICE_ID_MOXA_C320                0x3200
 
 #define PCI_VENDOR_ID_CCD              0x1397
+#define PCI_DEVICE_ID_CCD_HFC4S                0x08B4
+#define PCI_SUBDEVICE_ID_CCD_PMX2S     0x1234
+#define PCI_DEVICE_ID_CCD_HFC8S                0x16B8
 #define PCI_DEVICE_ID_CCD_2BD0         0x2bd0
+#define PCI_DEVICE_ID_CCD_HFCE1                0x30B1
+#define PCI_SUBDEVICE_ID_CCD_SPD4S     0x3136
+#define PCI_SUBDEVICE_ID_CCD_SPDE1     0x3137
 #define PCI_DEVICE_ID_CCD_B000         0xb000
 #define PCI_DEVICE_ID_CCD_B006         0xb006
 #define PCI_DEVICE_ID_CCD_B007         0xb007
 #define PCI_DEVICE_ID_CCD_B00B         0xb00b
 #define PCI_DEVICE_ID_CCD_B00C         0xb00c
 #define PCI_DEVICE_ID_CCD_B100         0xb100
+#define PCI_SUBDEVICE_ID_CCD_IOB4ST    0xB520
+#define PCI_SUBDEVICE_ID_CCD_IOB8STR   0xB521
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST    0xB522
+#define PCI_SUBDEVICE_ID_CCD_IOB1E1    0xB523
+#define PCI_SUBDEVICE_ID_CCD_SWYX4S    0xB540
+#define PCI_SUBDEVICE_ID_CCD_JH4S20    0xB550
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1  0xB552
+#define PCI_SUBDEVICE_ID_CCD_BN4S      0xB560
+#define PCI_SUBDEVICE_ID_CCD_BN8S      0xB562
+#define PCI_SUBDEVICE_ID_CCD_BNE1      0xB563
+#define PCI_SUBDEVICE_ID_CCD_BNE1D     0xB564
+#define PCI_SUBDEVICE_ID_CCD_BNE1DP    0xB565
+#define PCI_SUBDEVICE_ID_CCD_BN2S      0xB566
+#define PCI_SUBDEVICE_ID_CCD_BN1SM     0xB567
+#define PCI_SUBDEVICE_ID_CCD_BN4SM     0xB568
+#define PCI_SUBDEVICE_ID_CCD_BN2SM     0xB569
+#define PCI_SUBDEVICE_ID_CCD_BNE1M     0xB56A
+#define PCI_SUBDEVICE_ID_CCD_BN8SP     0xB56B
+#define PCI_SUBDEVICE_ID_CCD_HFC4S     0xB620
+#define PCI_SUBDEVICE_ID_CCD_HFC8S     0xB622
 #define PCI_DEVICE_ID_CCD_B700         0xb700
 #define PCI_DEVICE_ID_CCD_B701         0xb701
+#define PCI_SUBDEVICE_ID_CCD_HFCE1     0xC523
+#define PCI_SUBDEVICE_ID_CCD_OV2S      0xE884
+#define PCI_SUBDEVICE_ID_CCD_OV4S      0xE888
+#define PCI_SUBDEVICE_ID_CCD_OV8S      0xE998
 
 #define PCI_VENDOR_ID_EXAR             0x13a8
 #define PCI_DEVICE_ID_EXAR_XR17C152    0x0152
 
 #define PCI_VENDOR_ID_3COM_2           0xa727
 
+#define PCI_VENDOR_ID_DIGIUM           0xd161
+#define PCI_DEVICE_ID_DIGIUM_HFC4S     0xb410
+
 #define PCI_SUBVENDOR_ID_EXSYS         0xd84d
 #define PCI_SUBDEVICE_ID_EXSYS_4014    0x4014
 #define PCI_SUBDEVICE_ID_EXSYS_4055    0x4055
index f560d1705afec2f2ab9c6c9103d7547961808864..fb61850d1cfcf7b04352c6fa2a9d12c6c91e77f0 100644 (file)
@@ -282,11 +282,16 @@ union proc_op {
                struct task_struct *task);
 };
 
+struct ctl_table_header;
+struct ctl_table;
+
 struct proc_inode {
        struct pid *pid;
        int fd;
        union proc_op op;
        struct proc_dir_entry *pde;
+       struct ctl_table_header *sysctl;
+       struct ctl_table *sysctl_entry;
        struct inode vfs_inode;
 };
 
index 66a96814d614c7dd23c4371d80a54a1a829b6a84..af135ae895db2d3523839c5f1a0857ed54db3aca 100644 (file)
@@ -55,7 +55,7 @@ int reiserfs_removexattr(struct dentry *dentry, const char *name);
 int reiserfs_delete_xattrs(struct inode *inode);
 int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs);
 int reiserfs_xattr_init(struct super_block *sb, int mount_flags);
-int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd);
+int reiserfs_permission(struct inode *inode, int mask);
 
 int reiserfs_xattr_del(struct inode *, const char *);
 int reiserfs_xattr_get(const struct inode *, const char *, void *, size_t);
index f4d386c191f587507dac62b17de0ea26cfe3fbee..ca643b13b02635bfd33bae20588418ca52ff3f3b 100644 (file)
@@ -755,13 +755,6 @@ extern void __rtnl_unlock(void);
        } \
 } while(0)
 
-#define BUG_TRAP(x) do { \
-       if (unlikely(!(x))) { \
-               printk(KERN_ERR "KERNEL: assertion (%s) failed at %s (%d)\n", \
-                       #x,  __FILE__ , __LINE__); \
-       } \
-} while(0)
-
 static inline u32 rtm_get_table(struct rtattr **rta, u8 table)
 {
        return RTA_GET_U32(rta[RTA_TABLE-1]);
index f59318a0099bb5b002001bdf3615cef4340c988e..5270d449ff9deb9036f0b4d7852c4877dc561262 100644 (file)
@@ -505,9 +505,6 @@ struct signal_struct {
        unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw;
        unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt;
        unsigned long inblock, oublock, cinblock, coublock;
-#ifdef CONFIG_TASK_XACCT
-       u64 rchar, wchar, syscr, syscw;
-#endif
        struct task_io_accounting ioac;
 
        /*
@@ -1256,10 +1253,6 @@ struct task_struct {
 
        unsigned long ptrace_message;
        siginfo_t *last_siginfo; /* For ptrace use.  */
-#ifdef CONFIG_TASK_XACCT
-/* i/o counters(bytes read/written, #syscalls */
-       u64 rchar, wchar, syscr, syscw;
-#endif
        struct task_io_accounting ioac;
 #if defined(CONFIG_TASK_XACCT)
        u64 acct_rss_mem1;      /* accumulated rss usage */
@@ -2190,22 +2183,22 @@ extern long sched_group_rt_period(struct task_group *tg);
 #ifdef CONFIG_TASK_XACCT
 static inline void add_rchar(struct task_struct *tsk, ssize_t amt)
 {
-       tsk->rchar += amt;
+       tsk->ioac.rchar += amt;
 }
 
 static inline void add_wchar(struct task_struct *tsk, ssize_t amt)
 {
-       tsk->wchar += amt;
+       tsk->ioac.wchar += amt;
 }
 
 static inline void inc_syscr(struct task_struct *tsk)
 {
-       tsk->syscr++;
+       tsk->ioac.syscr++;
 }
 
 static inline void inc_syscw(struct task_struct *tsk)
 {
-       tsk->syscw++;
+       tsk->ioac.syscw++;
 }
 #else
 static inline void add_rchar(struct task_struct *tsk, ssize_t amt)
index f0e9adb22ac212542075116cce0e60f9cf215eb3..fd96e7f8a6f9c9ecad138a0eaae84c5f6c82dc64 100644 (file)
@@ -1362,7 +1362,7 @@ struct security_operations {
                             struct inode *new_dir, struct dentry *new_dentry);
        int (*inode_readlink) (struct dentry *dentry);
        int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
-       int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
+       int (*inode_permission) (struct inode *inode, int mask);
        int (*inode_setattr)    (struct dentry *dentry, struct iattr *attr);
        int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
        void (*inode_delete) (struct inode *inode);
@@ -1628,7 +1628,7 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
                          struct inode *new_dir, struct dentry *new_dentry);
 int security_inode_readlink(struct dentry *dentry);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd);
+int security_inode_permission(struct inode *inode, int mask);
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 void security_inode_delete(struct inode *inode);
@@ -2021,8 +2021,7 @@ static inline int security_inode_follow_link(struct dentry *dentry,
        return 0;
 }
 
-static inline int security_inode_permission(struct inode *inode, int mask,
-                                            struct nameidata *nd)
+static inline int security_inode_permission(struct inode *inode, int mask)
 {
        return 0;
 }
index f2d12d5a21b891cf53cbe19da6d10ded434aab59..fd83f2584b157b5e76b593344c55f3381f149014 100644 (file)
@@ -43,7 +43,7 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
 }
 
 #ifdef CONFIG_TMPFS_POSIX_ACL
-int shmem_permission(struct inode *, int, struct nameidata *);
+int shmem_permission(struct inode *, int);
 int shmem_acl_init(struct inode *, struct inode *);
 void shmem_acl_destroy_inode(struct inode *);
 
index 9ff8e849940396ed48b26a534af638e04accd54c..5ff9676c1e2ca83e93511891230189772fafe2a0 100644 (file)
@@ -96,6 +96,7 @@ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr);
 /*
  * Common kmalloc functions provided by all allocators
  */
+void * __must_check __krealloc(const void *, size_t, gfp_t);
 void * __must_check krealloc(const void *, size_t, gfp_t);
 void kfree(const void *);
 size_t ksize(const void *);
index 950af631e7fb17981e94d65b21345a3d737c2cfb..dc5086fe7736c10f40bfbab0996b7d589ccf5bbe 100644 (file)
@@ -189,7 +189,8 @@ struct ucred {
 #define AF_BLUETOOTH   31      /* Bluetooth sockets            */
 #define AF_IUCV                32      /* IUCV sockets                 */
 #define AF_RXRPC       33      /* RxRPC sockets                */
-#define AF_MAX         34      /* For now.. */
+#define AF_ISDN                34      /* mISDN sockets                */
+#define AF_MAX         35      /* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC      AF_UNSPEC
@@ -225,6 +226,7 @@ struct ucred {
 #define PF_BLUETOOTH   AF_BLUETOOTH
 #define PF_IUCV                AF_IUCV
 #define PF_RXRPC       AF_RXRPC
+#define PF_ISDN                AF_ISDN
 #define PF_MAX         AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
index 24141b4d1a116a4523ab9085e8f830cdd26cb471..d0437f36921f803e1bd8be49d1066a7ad1796928 100644 (file)
@@ -947,6 +947,22 @@ struct ctl_table;
 struct nsproxy;
 struct ctl_table_root;
 
+struct ctl_table_set {
+       struct list_head list;
+       struct ctl_table_set *parent;
+       int (*is_seen)(struct ctl_table_set *);
+};
+
+extern void setup_sysctl_set(struct ctl_table_set *p,
+       struct ctl_table_set *parent,
+       int (*is_seen)(struct ctl_table_set *));
+
+struct ctl_table_header;
+
+extern void sysctl_head_get(struct ctl_table_header *);
+extern void sysctl_head_put(struct ctl_table_header *);
+extern int sysctl_is_seen(struct ctl_table_header *);
+extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *);
 extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev);
 extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
                                                struct ctl_table_header *prev);
@@ -1049,8 +1065,8 @@ struct ctl_table
 
 struct ctl_table_root {
        struct list_head root_list;
-       struct list_head header_list;
-       struct list_head *(*lookup)(struct ctl_table_root *root,
+       struct ctl_table_set default_set;
+       struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
                                           struct nsproxy *namespaces);
        int (*permissions)(struct ctl_table_root *root,
                        struct nsproxy *namespaces, struct ctl_table *table);
@@ -1063,9 +1079,14 @@ struct ctl_table_header
        struct ctl_table *ctl_table;
        struct list_head ctl_entry;
        int used;
+       int count;
        struct completion *unregistering;
        struct ctl_table *ctl_table_arg;
        struct ctl_table_root *root;
+       struct ctl_table_set *set;
+       struct ctl_table *attached_by;
+       struct ctl_table *attached_to;
+       struct ctl_table_header *parent;
 };
 
 /* struct ctl_path describes where in the hierarchy a table is added */
index 44d00e9cceea551256df7428be8856e719d04ef1..5e88afc9a2fbd5b5e82bc9486087d9f9017cadcd 100644 (file)
@@ -8,8 +8,19 @@
  * Blame akpm@osdl.org for all this.
  */
 
-#ifdef CONFIG_TASK_IO_ACCOUNTING
 struct task_io_accounting {
+#ifdef CONFIG_TASK_XACCT
+       /* bytes read */
+       u64 rchar;
+       /*  bytes written */
+       u64 wchar;
+       /* # of read syscalls */
+       u64 syscr;
+       /* # of write syscalls */
+       u64 syscw;
+#endif /* CONFIG_TASK_XACCT */
+
+#ifdef CONFIG_TASK_IO_ACCOUNTING
        /*
         * The number of bytes which this task has caused to be read from
         * storage.
@@ -30,8 +41,5 @@ struct task_io_accounting {
         * information loss in doing that.
         */
        u64 cancelled_write_bytes;
+#endif /* CONFIG_TASK_IO_ACCOUNTING */
 };
-#else
-struct task_io_accounting {
-};
-#endif
index ff46c6fad79da300afaa621a41abcfb1bf4c787f..4d090f9ee60878683b1b3542eda603a975114f4d 100644 (file)
@@ -40,9 +40,17 @@ static inline void task_io_account_cancelled_write(size_t bytes)
        current->ioac.cancelled_write_bytes += bytes;
 }
 
-static inline void task_io_accounting_init(struct task_struct *tsk)
+static inline void task_io_accounting_init(struct task_io_accounting *ioac)
 {
-       memset(&tsk->ioac, 0, sizeof(tsk->ioac));
+       memset(ioac, 0, sizeof(*ioac));
+}
+
+static inline void task_blk_io_accounting_add(struct task_io_accounting *dst,
+                                               struct task_io_accounting *src)
+{
+       dst->read_bytes += src->read_bytes;
+       dst->write_bytes += src->write_bytes;
+       dst->cancelled_write_bytes += src->cancelled_write_bytes;
 }
 
 #else
@@ -69,9 +77,37 @@ static inline void task_io_account_cancelled_write(size_t bytes)
 {
 }
 
-static inline void task_io_accounting_init(struct task_struct *tsk)
+static inline void task_io_accounting_init(struct task_io_accounting *ioac)
+{
+}
+
+static inline void task_blk_io_accounting_add(struct task_io_accounting *dst,
+                                               struct task_io_accounting *src)
 {
 }
 
-#endif         /* CONFIG_TASK_IO_ACCOUNTING */
-#endif         /* __TASK_IO_ACCOUNTING_OPS_INCLUDED */
+#endif /* CONFIG_TASK_IO_ACCOUNTING */
+
+#ifdef CONFIG_TASK_XACCT
+static inline void task_chr_io_accounting_add(struct task_io_accounting *dst,
+                                               struct task_io_accounting *src)
+{
+       dst->rchar += src->rchar;
+       dst->wchar += src->wchar;
+       dst->syscr += src->syscr;
+       dst->syscw += src->syscw;
+}
+#else
+static inline void task_chr_io_accounting_add(struct task_io_accounting *dst,
+                                               struct task_io_accounting *src)
+{
+}
+#endif /* CONFIG_TASK_XACCT */
+
+static inline void task_io_accounting_add(struct task_io_accounting *dst,
+                                               struct task_io_accounting *src)
+{
+       task_chr_io_accounting_add(dst, src);
+       task_blk_io_accounting_add(dst, src);
+}
+#endif /* __TASK_IO_ACCOUNTING_OPS_INCLUDED */
index 589f429619c917ded1d60b07d99fcb3c20659fce..b1875582c1a1466c558f3ef7831e3186bd766be8 100644 (file)
@@ -244,7 +244,7 @@ static inline int tracehook_prepare_clone(unsigned clone_flags)
  * tracehook_finish_clone - new child created and being attached
  * @child:             new child task
  * @clone_flags:       %CLONE_* flags from clone/fork/vfork system call
- * @trace:             return value from tracehook_clone_prepare()
+ * @trace:             return value from tracehook_prepare_clone()
  *
  * This is called immediately after adding @child to its parent's children list.
  * The @trace value is that returned by tracehook_prepare_clone().
@@ -259,19 +259,20 @@ static inline void tracehook_finish_clone(struct task_struct *child,
 
 /**
  * tracehook_report_clone - in parent, new child is about to start running
- * @trace:             return value from tracehook_clone_prepare()
+ * @trace:             return value from tracehook_prepare_clone()
  * @regs:              parent's user register state
  * @clone_flags:       flags from parent's system call
  * @pid:               new child's PID in the parent's namespace
  * @child:             new child task
  *
- * Called after a child is set up, but before it has been started running.
- * The @trace value is that returned by tracehook_clone_prepare().
- * This is not a good place to block, because the child has not started yet.
- * Suspend the child here if desired, and block in tracehook_clone_complete().
- * This must prevent the child from self-reaping if tracehook_clone_complete()
- * uses the @child pointer; otherwise it might have died and been released by
- * the time tracehook_report_clone_complete() is called.
+ * Called after a child is set up, but before it has been started
+ * running.  @trace is the value returned by tracehook_prepare_clone().
+ * This is not a good place to block, because the child has not started
+ * yet.  Suspend the child here if desired, and then block in
+ * tracehook_report_clone_complete().  This must prevent the child from
+ * self-reaping if tracehook_report_clone_complete() uses the @child
+ * pointer; otherwise it might have died and been released by the time
+ * tracehook_report_report_clone_complete() is called.
  *
  * Called with no locks held, but the child cannot run until this returns.
  */
@@ -290,7 +291,7 @@ static inline void tracehook_report_clone(int trace, struct pt_regs *regs,
 
 /**
  * tracehook_report_clone_complete - new child is running
- * @trace:             return value from tracehook_clone_prepare()
+ * @trace:             return value from tracehook_prepare_clone()
  * @regs:              parent's user register state
  * @clone_flags:       flags from parent's system call
  * @pid:               new child's PID in the parent's namespace
@@ -347,7 +348,7 @@ static inline void tracehook_prepare_release_task(struct task_struct *task)
 }
 
 /**
- * tracehook_finish_release_task - task is being reaped, clean up tracing
+ * tracehook_finish_release_task - final tracing clean-up
  * @task:              task in %EXIT_DEAD state
  *
  * This is called in release_task() when @task is being in the middle of
index 9b1e2526b45ec2be45e58768f8ad3e801dc8dcd5..e17eda302b2d6c505902d8992f89712f362c9197 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $
- *
  * Parts of INFTL headers shared with userspace
  *
  */
index d508ef0ae091bd2c76b549461f3065679cb012dc..001685d7fa889b8fc8f325df833fc5140f9831a6 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
- *
  * JFFS2 definitions for use in user space only
  */
 
index 615072c4da04415e8f14e02372078abb3d987941..c6c61cd5a254aaa03348924d0f0f864d8bf845e0 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $
- *
  * Portions of MTD ABI definition which are shared by kernel and user space
  */
 
index 713f34d3e62e184aa6712ee53ac8308412d26593..170ceca3b2d08fb2c77362d8c061f6dc6d51f59d 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $
- *
  * MTD ABI header for use by user space only.
  */
 
index b2bca18e7311bf1670271b3cf5988b0c933278fe..390d21c080aa98108d4cfef8bc5f10c2a1e84a0e 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $
- *
  * Parts of NFTL headers shared with userspace
  *
  */
index 2dfa96b0575e638233475bd7af33a457319dd1c9..7dd29b7e461dead402b25d6b0fb27451d6896441 100644 (file)
@@ -51,7 +51,7 @@ struct unix_sock {
         struct sock            *peer;
         struct sock            *other;
        struct list_head        link;
-        atomic_t                inflight;
+        atomic_long_t           inflight;
         spinlock_t             lock;
        unsigned int            gc_candidate : 1;
         wait_queue_head_t       peer_wait;
index b5862b975207950c91e0af30b361b63c86fcd0db..250e6ef025a460e16e58636645a71cd51dbce887 100644 (file)
@@ -188,6 +188,8 @@ extern int sysctl_ip_dynaddr;
 
 extern void ipfrag_init(void);
 
+extern void ip_static_sysctl_init(void);
+
 #ifdef CONFIG_INET
 #include <net/dst.h>
 
index 2d5c18514a2d2d3b3e77e0a0d141d07dafec0c6d..113028fb8f6610c912e57326dbaf6f4bd1c5c649 100644 (file)
@@ -608,6 +608,8 @@ extern struct ctl_table *ipv6_icmp_sysctl_init(struct net *net);
 extern struct ctl_table *ipv6_route_sysctl_init(struct net *net);
 extern int ipv6_sysctl_register(void);
 extern void ipv6_sysctl_unregister(void);
+extern int ipv6_static_sysctl_register(void);
+extern void ipv6_static_sysctl_unregister(void);
 #endif
 
 #endif /* __KERNEL__ */
index 3855620b78a9760081c590d44bf9794f0e993390..a8eb43cf0c7ed8fe895f901ddbc25f85bc608806 100644 (file)
@@ -38,7 +38,9 @@ struct net {
        struct proc_dir_entry   *proc_net;
        struct proc_dir_entry   *proc_net_stat;
 
-       struct list_head        sysctl_table_headers;
+#ifdef CONFIG_SYSCTL
+       struct ctl_table_set    sysctls;
+#endif
 
        struct net_device       *loopback_dev;          /* The loopback */
 
index 0c96e7bed5db39e7eff4718353697ce52e06df96..8d6e991ef4dfb79d2a8d75bd14cf4241bafbfc95 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <linux/bug.h>
 
 #include <net/sock.h>
 
@@ -170,7 +171,7 @@ static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue
 {
        struct request_sock *req = queue->rskq_accept_head;
 
-       BUG_TRAP(req != NULL);
+       WARN_ON(req == NULL);
 
        queue->rskq_accept_head = req->dl_next;
        if (queue->rskq_accept_head == NULL)
@@ -185,7 +186,7 @@ static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queu
        struct request_sock *req = reqsk_queue_remove(queue);
        struct sock *child = req->sk;
 
-       BUG_TRAP(child != NULL);
+       WARN_ON(child == NULL);
 
        sk_acceptq_removed(parent);
        __reqsk_free(req);
index 3140cc500854064b647245366ee112115dea40e2..4f0d8c14736cba916799c7793848d8d42595483f 100644 (file)
@@ -204,6 +204,4 @@ static inline struct inet_peer *rt_get_peer(struct rtable *rt)
        return rt->peer;
 }
 
-extern ctl_table ipv4_route_table[];
-
 #endif /* _ROUTE_H */
index 00137a7769ee267f76679966cdb4e5e6005d9f8d..5c40cc537d4cc625a9ad94d76190405c52694379 100644 (file)
 #define VARIABLE_LENGTH_CMD   0x7f
 #define REPORT_LUNS           0xa0
 #define MAINTENANCE_IN        0xa3
+#define MAINTENANCE_OUT       0xa4
 #define MOVE_MEDIUM           0xa5
 #define EXCHANGE_MEDIUM       0xa6
 #define READ_12               0xa8
 #define        SAI_READ_CAPACITY_16  0x10
 /* values for maintenance in */
 #define MI_REPORT_TARGET_PGS  0x0a
+/* values for maintenance out */
+#define MO_SET_TARGET_PGS     0x0a
 
 /* Values for T10/04-262r7 */
 #define        ATA_16                0x85      /* 16-byte pass-thru */
index 66c944849d6b385f1e6d25fe81be0eff59e1b471..f9f6e793575c0b7114c3162b25e430978977d2eb 100644 (file)
@@ -77,6 +77,9 @@ struct scsi_cmnd {
        int allowed;
        int timeout_per_command;
 
+       unsigned char prot_op;
+       unsigned char prot_type;
+
        unsigned short cmd_len;
        enum dma_data_direction sc_data_direction;
 
@@ -87,6 +90,8 @@ struct scsi_cmnd {
 
        /* These elements define the operation we ultimately want to perform */
        struct scsi_data_buffer sdb;
+       struct scsi_data_buffer *prot_sdb;
+
        unsigned underflow;     /* Return error if less than
                                   this amount is transferred */
 
@@ -208,4 +213,85 @@ static inline int scsi_sg_copy_to_buffer(struct scsi_cmnd *cmd,
                                 buf, buflen);
 }
 
+/*
+ * The operations below are hints that tell the controller driver how
+ * to handle I/Os with DIF or similar types of protection information.
+ */
+enum scsi_prot_operations {
+       /* Normal I/O */
+       SCSI_PROT_NORMAL = 0,
+
+       /* OS-HBA: Protected, HBA-Target: Unprotected */
+       SCSI_PROT_READ_INSERT,
+       SCSI_PROT_WRITE_STRIP,
+
+       /* OS-HBA: Unprotected, HBA-Target: Protected */
+       SCSI_PROT_READ_STRIP,
+       SCSI_PROT_WRITE_INSERT,
+
+       /* OS-HBA: Protected, HBA-Target: Protected */
+       SCSI_PROT_READ_PASS,
+       SCSI_PROT_WRITE_PASS,
+
+       /* OS-HBA: Protected, HBA-Target: Protected, checksum conversion */
+       SCSI_PROT_READ_CONVERT,
+       SCSI_PROT_WRITE_CONVERT,
+};
+
+static inline void scsi_set_prot_op(struct scsi_cmnd *scmd, unsigned char op)
+{
+       scmd->prot_op = op;
+}
+
+static inline unsigned char scsi_get_prot_op(struct scsi_cmnd *scmd)
+{
+       return scmd->prot_op;
+}
+
+/*
+ * The controller usually does not know anything about the target it
+ * is communicating with.  However, when DIX is enabled the controller
+ * must be know target type so it can verify the protection
+ * information passed along with the I/O.
+ */
+enum scsi_prot_target_type {
+       SCSI_PROT_DIF_TYPE0 = 0,
+       SCSI_PROT_DIF_TYPE1,
+       SCSI_PROT_DIF_TYPE2,
+       SCSI_PROT_DIF_TYPE3,
+};
+
+static inline void scsi_set_prot_type(struct scsi_cmnd *scmd, unsigned char type)
+{
+       scmd->prot_type = type;
+}
+
+static inline unsigned char scsi_get_prot_type(struct scsi_cmnd *scmd)
+{
+       return scmd->prot_type;
+}
+
+static inline sector_t scsi_get_lba(struct scsi_cmnd *scmd)
+{
+       return scmd->request->sector;
+}
+
+static inline unsigned scsi_prot_sg_count(struct scsi_cmnd *cmd)
+{
+       return cmd->prot_sdb ? cmd->prot_sdb->table.nents : 0;
+}
+
+static inline struct scatterlist *scsi_prot_sglist(struct scsi_cmnd *cmd)
+{
+       return cmd->prot_sdb ? cmd->prot_sdb->table.sgl : NULL;
+}
+
+static inline struct scsi_data_buffer *scsi_prot(struct scsi_cmnd *cmd)
+{
+       return cmd->prot_sdb;
+}
+
+#define scsi_for_each_prot_sg(cmd, sg, nseg, __i)              \
+       for_each_sg(scsi_prot_sglist(cmd), sg, nseg, __i)
+
 #endif /* _SCSI_SCSI_CMND_H */
index 6467f78b191fdf3334d714341c01c78044871175..291d56a19167db9b8688dbbba446d051daae3b17 100644 (file)
@@ -140,7 +140,8 @@ struct scsi_device {
        unsigned fix_capacity:1;        /* READ_CAPACITY is too high by 1 */
        unsigned guess_capacity:1;      /* READ_CAPACITY might be too high by 1 */
        unsigned retry_hwerror:1;       /* Retry HARDWARE_ERROR */
-       unsigned last_sector_bug:1;     /* Always read last sector in a 1 sector read */
+       unsigned last_sector_bug:1;     /* do not use multisector accesses on
+                                          SD_LAST_BUGGY_SECTORS */
 
        DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
        struct list_head event_list;    /* asserted events */
@@ -167,15 +168,22 @@ struct scsi_device {
        unsigned long           sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
+struct scsi_dh_devlist {
+       char *vendor;
+       char *model;
+};
+
 struct scsi_device_handler {
        /* Used by the infrastructure */
        struct list_head list; /* list of scsi_device_handlers */
-       struct notifier_block nb;
 
        /* Filled by the hardware handler */
        struct module *module;
        const char *name;
+       const struct scsi_dh_devlist *devlist;
        int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
+       int (*attach)(struct scsi_device *);
+       void (*detach)(struct scsi_device *);
        int (*activate)(struct scsi_device *);
        int (*prep_fn)(struct scsi_device *, struct request *);
 };
@@ -416,6 +424,11 @@ static inline int scsi_device_enclosure(struct scsi_device *sdev)
        return sdev->inquiry[6] & (1<<6);
 }
 
+static inline int scsi_device_protection(struct scsi_device *sdev)
+{
+       return sdev->inquiry[5] & (1<<0);
+}
+
 #define MODULE_ALIAS_SCSI_DEVICE(type) \
        MODULE_ALIAS("scsi:t-" __stringify(type) "*")
 #define SCSI_DEVICE_MODALIAS_FMT "scsi:t-0x%02x"
index 3ad2303d1a166ec69efb9350e63d8ec77444c565..33efce20c26c937c08a89380dd1949b9eed60806 100644 (file)
@@ -32,6 +32,7 @@ enum {
         */
        SCSI_DH_DEV_FAILED,     /* generic device error */
        SCSI_DH_DEV_TEMP_BUSY,
+       SCSI_DH_DEV_UNSUPP,     /* device handler not supported */
        SCSI_DH_DEVICE_MAX,     /* max device blkerr definition */
 
        /*
@@ -57,6 +58,8 @@ enum {
 #if defined(CONFIG_SCSI_DH) || defined(CONFIG_SCSI_DH_MODULE)
 extern int scsi_dh_activate(struct request_queue *);
 extern int scsi_dh_handler_exist(const char *);
+extern int scsi_dh_attach(struct request_queue *, const char *);
+extern void scsi_dh_detach(struct request_queue *);
 #else
 static inline int scsi_dh_activate(struct request_queue *req)
 {
@@ -66,4 +69,12 @@ static inline int scsi_dh_handler_exist(const char *name)
 {
        return 0;
 }
+static inline int scsi_dh_attach(struct request_queue *req, const char *name)
+{
+       return SCSI_DH_NOSYS;
+}
+static inline void scsi_dh_detach(struct request_queue *q)
+{
+       return;
+}
 #endif
index 2a9add21267d7fddfc4f443b6d5f475eba65b31e..06a8790893ef60e15cdd100550134e698e6a5e2e 100644 (file)
@@ -74,7 +74,9 @@ struct scsi_eh_save {
        /* saved state */
        int result;
        enum dma_data_direction data_direction;
+       unsigned underflow;
        unsigned char cmd_len;
+       unsigned char prot_op;
        unsigned char *cmnd;
        struct scsi_data_buffer sdb;
        struct request *next_rq;
index a594bac4a77db2b5f311470cc2e2324d5cf6bedb..44a55d1bf530c070e86eb8381901a3ba63dfd51c 100644 (file)
@@ -547,7 +547,7 @@ struct Scsi_Host {
        unsigned int host_failed;          /* commands that failed. */
        unsigned int host_eh_scheduled;    /* EH scheduled without command */
     
-       unsigned short host_no;  /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */
+       unsigned int host_no;  /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */
        int resetting; /* if set, it means that last_reset is a valid value */
        unsigned long last_reset;
 
@@ -636,6 +636,10 @@ struct Scsi_Host {
         */
        unsigned int max_host_blocked;
 
+       /* Protection Information */
+       unsigned int prot_capabilities;
+       unsigned char prot_guard_type;
+
        /*
         * q used for scsi_tgt msgs, async events or any other requests that
         * need to be processed in userspace
@@ -756,6 +760,86 @@ extern struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,
 extern void scsi_free_host_dev(struct scsi_device *);
 extern struct scsi_device *scsi_get_host_dev(struct Scsi_Host *);
 
+/*
+ * DIF defines the exchange of protection information between
+ * initiator and SBC block device.
+ *
+ * DIX defines the exchange of protection information between OS and
+ * initiator.
+ */
+enum scsi_host_prot_capabilities {
+       SHOST_DIF_TYPE1_PROTECTION = 1 << 0, /* T10 DIF Type 1 */
+       SHOST_DIF_TYPE2_PROTECTION = 1 << 1, /* T10 DIF Type 2 */
+       SHOST_DIF_TYPE3_PROTECTION = 1 << 2, /* T10 DIF Type 3 */
+
+       SHOST_DIX_TYPE0_PROTECTION = 1 << 3, /* DIX between OS and HBA only */
+       SHOST_DIX_TYPE1_PROTECTION = 1 << 4, /* DIX with DIF Type 1 */
+       SHOST_DIX_TYPE2_PROTECTION = 1 << 5, /* DIX with DIF Type 2 */
+       SHOST_DIX_TYPE3_PROTECTION = 1 << 6, /* DIX with DIF Type 3 */
+};
+
+/*
+ * SCSI hosts which support the Data Integrity Extensions must
+ * indicate their capabilities by setting the prot_capabilities using
+ * this call.
+ */
+static inline void scsi_host_set_prot(struct Scsi_Host *shost, unsigned int mask)
+{
+       shost->prot_capabilities = mask;
+}
+
+static inline unsigned int scsi_host_get_prot(struct Scsi_Host *shost)
+{
+       return shost->prot_capabilities;
+}
+
+static inline unsigned int scsi_host_dif_capable(struct Scsi_Host *shost, unsigned int target_type)
+{
+       switch (target_type) {
+       case 1: return shost->prot_capabilities & SHOST_DIF_TYPE1_PROTECTION;
+       case 2: return shost->prot_capabilities & SHOST_DIF_TYPE2_PROTECTION;
+       case 3: return shost->prot_capabilities & SHOST_DIF_TYPE3_PROTECTION;
+       }
+
+       return 0;
+}
+
+static inline unsigned int scsi_host_dix_capable(struct Scsi_Host *shost, unsigned int target_type)
+{
+       switch (target_type) {
+       case 0: return shost->prot_capabilities & SHOST_DIX_TYPE0_PROTECTION;
+       case 1: return shost->prot_capabilities & SHOST_DIX_TYPE1_PROTECTION;
+       case 2: return shost->prot_capabilities & SHOST_DIX_TYPE2_PROTECTION;
+       case 3: return shost->prot_capabilities & SHOST_DIX_TYPE3_PROTECTION;
+       }
+
+       return 0;
+}
+
+/*
+ * All DIX-capable initiators must support the T10-mandated CRC
+ * checksum.  Controllers can optionally implement the IP checksum
+ * scheme which has much lower impact on system performance.  Note
+ * that the main rationale for the checksum is to match integrity
+ * metadata with data.  Detecting bit errors are a job for ECC memory
+ * and buses.
+ */
+
+enum scsi_host_guard_type {
+       SHOST_DIX_GUARD_CRC = 1 << 0,
+       SHOST_DIX_GUARD_IP  = 1 << 1,
+};
+
+static inline void scsi_host_set_guard(struct Scsi_Host *shost, unsigned char type)
+{
+       shost->prot_guard_type = type;
+}
+
+static inline unsigned char scsi_host_get_guard(struct Scsi_Host *shost)
+{
+       return shost->prot_guard_type;
+}
+
 /* legacy interfaces */
 extern struct Scsi_Host *scsi_register(struct scsi_host_template *, int);
 extern void scsi_unregister(struct Scsi_Host *);
index a50bdfed2df7664eacbb4a655b07b381caf64d99..43d6989c275f3432a0404771af92015f13717a01 100644 (file)
@@ -171,7 +171,7 @@ config BSD_PROCESS_ACCT_V3
          process and it's parent. Note that this file format is incompatible
          with previous v0/v1/v2 file formats, so you will need updated tools
          for processing it. A preliminary version of these tools is available
-         at <http://www.physik3.uni-rostock.de/tim/kernel/utils/acct/>.
+         at <http://www.gnu.org/software/acct/>.
 
 config TASKSTATS
        bool "Export task/process statistics through netlink (EXPERIMENTAL)"
@@ -486,7 +486,7 @@ config PID_NS
        default n
        depends on NAMESPACES && EXPERIMENTAL
        help
-         Suport process id namespaces.  This allows having multiple
+         Support process id namespaces.  This allows having multiple
          process with the same pid as long as they are in different
          pid namespaces.  This is a building block of containers.
 
index 474984f9e0328a016b8496a6caa83e7f74655537..96fb36cd9874cb26b242933178426244d1ed7e13 100644 (file)
@@ -638,7 +638,7 @@ static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
                return ERR_PTR(-EINVAL);
        }
 
-       if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) {
+       if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) {
                dput(dentry);
                mntput(mqueue_mnt);
                return ERR_PTR(-EACCES);
index 89bd6fb7894fc87f4c2505894daf0241862fa3af..657f8f8d93a5fa947e79d0b8567a9c5288f425d9 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/delayacct.h>
 #include <linux/cgroupstats.h>
 #include <linux/hash.h>
+#include <linux/namei.h>
 
 #include <asm/atomic.h>
 
index c1ef192aa65592b6286e6c224969e8af1eed3d81..0d407e886735d0b35c36a836f3bc97afa15ad9c6 100644 (file)
@@ -168,7 +168,6 @@ __set_personality(u_long personality)
        current->personality = personality;
        oep = current_thread_info()->exec_domain;
        current_thread_info()->exec_domain = ep;
-       set_fs_altroot();
 
        module_put(oep->module);
        return 0;
index 6cdf60712bd2efdf923238281390e0030b2b185f..eb4d6470d1d01f79a33a94df89833c3ba7d4387a 100644 (file)
@@ -121,18 +121,7 @@ static void __exit_signal(struct task_struct *tsk)
                sig->nivcsw += tsk->nivcsw;
                sig->inblock += task_io_get_inblock(tsk);
                sig->oublock += task_io_get_oublock(tsk);
-#ifdef CONFIG_TASK_XACCT
-               sig->rchar += tsk->rchar;
-               sig->wchar += tsk->wchar;
-               sig->syscr += tsk->syscr;
-               sig->syscw += tsk->syscw;
-#endif /* CONFIG_TASK_XACCT */
-#ifdef CONFIG_TASK_IO_ACCOUNTING
-               sig->ioac.read_bytes += tsk->ioac.read_bytes;
-               sig->ioac.write_bytes += tsk->ioac.write_bytes;
-               sig->ioac.cancelled_write_bytes +=
-                                       tsk->ioac.cancelled_write_bytes;
-#endif /* CONFIG_TASK_IO_ACCOUNTING */
+               task_io_accounting_add(&sig->ioac, &tsk->ioac);
                sig->sum_sched_runtime += tsk->se.sum_exec_runtime;
                sig = NULL; /* Marker for below. */
        }
@@ -565,8 +554,6 @@ void put_fs_struct(struct fs_struct *fs)
        if (atomic_dec_and_test(&fs->count)) {
                path_put(&fs->root);
                path_put(&fs->pwd);
-               if (fs->altroot.dentry)
-                       path_put(&fs->altroot);
                kmem_cache_free(fs_cachep, fs);
        }
 }
@@ -1365,21 +1352,8 @@ static int wait_task_zombie(struct task_struct *p, int options,
                psig->coublock +=
                        task_io_get_oublock(p) +
                        sig->oublock + sig->coublock;
-#ifdef CONFIG_TASK_XACCT
-               psig->rchar += p->rchar + sig->rchar;
-               psig->wchar += p->wchar + sig->wchar;
-               psig->syscr += p->syscr + sig->syscr;
-               psig->syscw += p->syscw + sig->syscw;
-#endif /* CONFIG_TASK_XACCT */
-#ifdef CONFIG_TASK_IO_ACCOUNTING
-               psig->ioac.read_bytes +=
-                       p->ioac.read_bytes + sig->ioac.read_bytes;
-               psig->ioac.write_bytes +=
-                       p->ioac.write_bytes + sig->ioac.write_bytes;
-               psig->ioac.cancelled_write_bytes +=
-                               p->ioac.cancelled_write_bytes +
-                               sig->ioac.cancelled_write_bytes;
-#endif /* CONFIG_TASK_IO_ACCOUNTING */
+               task_io_accounting_add(&psig->ioac, &p->ioac);
+               task_io_accounting_add(&psig->ioac, &sig->ioac);
                spin_unlock_irq(&p->parent->sighand->siglock);
        }
 
index abb3ed6298f6678150550a6a2af899cf823662c7..8214ba7c8bb1ad78e110687ade3459bff8b40073 100644 (file)
@@ -657,13 +657,6 @@ static struct fs_struct *__copy_fs_struct(struct fs_struct *old)
                path_get(&old->root);
                fs->pwd = old->pwd;
                path_get(&old->pwd);
-               if (old->altroot.dentry) {
-                       fs->altroot = old->altroot;
-                       path_get(&old->altroot);
-               } else {
-                       fs->altroot.mnt = NULL;
-                       fs->altroot.dentry = NULL;
-               }
                read_unlock(&old->lock);
        }
        return fs;
@@ -813,12 +806,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
        sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;
        sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0;
        sig->inblock = sig->oublock = sig->cinblock = sig->coublock = 0;
-#ifdef CONFIG_TASK_XACCT
-       sig->rchar = sig->wchar = sig->syscr = sig->syscw = 0;
-#endif
-#ifdef CONFIG_TASK_IO_ACCOUNTING
-       memset(&sig->ioac, 0, sizeof(sig->ioac));
-#endif
+       task_io_accounting_init(&sig->ioac);
        sig->sum_sched_runtime = 0;
        INIT_LIST_HEAD(&sig->cpu_timers[0]);
        INIT_LIST_HEAD(&sig->cpu_timers[1]);
@@ -1001,13 +989,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        p->last_switch_timestamp = 0;
 #endif
 
-#ifdef CONFIG_TASK_XACCT
-       p->rchar = 0;           /* I/O counter: bytes read */
-       p->wchar = 0;           /* I/O counter: bytes written */
-       p->syscr = 0;           /* I/O counter: read syscalls */
-       p->syscw = 0;           /* I/O counter: write syscalls */
-#endif
-       task_io_accounting_init(p);
+       task_io_accounting_init(&p->ioac);
        acct_clear_integrals(p);
 
        p->it_virt_expires = cputime_zero;
index 35a50db9b6cee1267b5865f543a8a8d86157a65a..fe47133472758853b039c47ac9390e5eadd34bed 100644 (file)
@@ -160,12 +160,13 @@ static struct ctl_table root_table[];
 static struct ctl_table_root sysctl_table_root;
 static struct ctl_table_header root_table_header = {
        .ctl_table = root_table,
-       .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list),
+       .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),
        .root = &sysctl_table_root,
+       .set = &sysctl_table_root.default_set,
 };
 static struct ctl_table_root sysctl_table_root = {
        .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list),
-       .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry),
+       .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry),
 };
 
 static struct ctl_table kern_table[];
@@ -1386,6 +1387,9 @@ static void start_unregistering(struct ctl_table_header *p)
                spin_unlock(&sysctl_lock);
                wait_for_completion(&wait);
                spin_lock(&sysctl_lock);
+       } else {
+               /* anything non-NULL; we'll never dereference it */
+               p->unregistering = ERR_PTR(-EINVAL);
        }
        /*
         * do not remove from the list until nobody holds it; walking the
@@ -1394,6 +1398,32 @@ static void start_unregistering(struct ctl_table_header *p)
        list_del_init(&p->ctl_entry);
 }
 
+void sysctl_head_get(struct ctl_table_header *head)
+{
+       spin_lock(&sysctl_lock);
+       head->count++;
+       spin_unlock(&sysctl_lock);
+}
+
+void sysctl_head_put(struct ctl_table_header *head)
+{
+       spin_lock(&sysctl_lock);
+       if (!--head->count)
+               kfree(head);
+       spin_unlock(&sysctl_lock);
+}
+
+struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head)
+{
+       if (!head)
+               BUG();
+       spin_lock(&sysctl_lock);
+       if (!use_table(head))
+               head = ERR_PTR(-ENOENT);
+       spin_unlock(&sysctl_lock);
+       return head;
+}
+
 void sysctl_head_finish(struct ctl_table_header *head)
 {
        if (!head)
@@ -1403,14 +1433,20 @@ void sysctl_head_finish(struct ctl_table_header *head)
        spin_unlock(&sysctl_lock);
 }
 
+static struct ctl_table_set *
+lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces)
+{
+       struct ctl_table_set *set = &root->default_set;
+       if (root->lookup)
+               set = root->lookup(root, namespaces);
+       return set;
+}
+
 static struct list_head *
 lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces)
 {
-       struct list_head *header_list;
-       header_list = &root->header_list;
-       if (root->lookup)
-               header_list = root->lookup(root, namespaces);
-       return header_list;
+       struct ctl_table_set *set = lookup_header_set(root, namespaces);
+       return &set->list;
 }
 
 struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
@@ -1480,9 +1516,9 @@ static int do_sysctl_strategy(struct ctl_table_root *root,
        int op = 0, rc;
 
        if (oldval)
-               op |= 004;
+               op |= MAY_READ;
        if (newval)
-               op |= 002;
+               op |= MAY_WRITE;
        if (sysctl_perm(root, table, op))
                return -EPERM;
 
@@ -1524,7 +1560,7 @@ repeat:
                if (n == table->ctl_name) {
                        int error;
                        if (table->child) {
-                               if (sysctl_perm(root, table, 001))
+                               if (sysctl_perm(root, table, MAY_EXEC))
                                        return -EPERM;
                                name++;
                                nlen--;
@@ -1599,7 +1635,7 @@ static int test_perm(int mode, int op)
                mode >>= 6;
        else if (in_egroup_p(0))
                mode >>= 3;
-       if ((mode & op & 0007) == op)
+       if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0)
                return 0;
        return -EACCES;
 }
@@ -1609,7 +1645,7 @@ int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op)
        int error;
        int mode;
 
-       error = security_sysctl(table, op);
+       error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC));
        if (error)
                return error;
 
@@ -1644,6 +1680,54 @@ static __init int sysctl_init(void)
 
 core_initcall(sysctl_init);
 
+static struct ctl_table *is_branch_in(struct ctl_table *branch,
+                                     struct ctl_table *table)
+{
+       struct ctl_table *p;
+       const char *s = branch->procname;
+
+       /* branch should have named subdirectory as its first element */
+       if (!s || !branch->child)
+               return NULL;
+
+       /* ... and nothing else */
+       if (branch[1].procname || branch[1].ctl_name)
+               return NULL;
+
+       /* table should contain subdirectory with the same name */
+       for (p = table; p->procname || p->ctl_name; p++) {
+               if (!p->child)
+                       continue;
+               if (p->procname && strcmp(p->procname, s) == 0)
+                       return p;
+       }
+       return NULL;
+}
+
+/* see if attaching q to p would be an improvement */
+static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q)
+{
+       struct ctl_table *to = p->ctl_table, *by = q->ctl_table;
+       struct ctl_table *next;
+       int is_better = 0;
+       int not_in_parent = !p->attached_by;
+
+       while ((next = is_branch_in(by, to)) != NULL) {
+               if (by == q->attached_by)
+                       is_better = 1;
+               if (to == p->attached_by)
+                       not_in_parent = 1;
+               by = by->child;
+               to = next->child;
+       }
+
+       if (is_better && not_in_parent) {
+               q->attached_by = by;
+               q->attached_to = to;
+               q->parent = p;
+       }
+}
+
 /**
  * __register_sysctl_paths - register a sysctl hierarchy
  * @root: List of sysctl headers to register on
@@ -1720,10 +1804,10 @@ struct ctl_table_header *__register_sysctl_paths(
        struct nsproxy *namespaces,
        const struct ctl_path *path, struct ctl_table *table)
 {
-       struct list_head *header_list;
        struct ctl_table_header *header;
        struct ctl_table *new, **prevp;
        unsigned int n, npath;
+       struct ctl_table_set *set;
 
        /* Count the path components */
        for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath)
@@ -1765,6 +1849,7 @@ struct ctl_table_header *__register_sysctl_paths(
        header->unregistering = NULL;
        header->root = root;
        sysctl_set_parent(NULL, header->ctl_table);
+       header->count = 1;
 #ifdef CONFIG_SYSCTL_SYSCALL_CHECK
        if (sysctl_check_table(namespaces, header->ctl_table)) {
                kfree(header);
@@ -1772,8 +1857,20 @@ struct ctl_table_header *__register_sysctl_paths(
        }
 #endif
        spin_lock(&sysctl_lock);
-       header_list = lookup_header_list(root, namespaces);
-       list_add_tail(&header->ctl_entry, header_list);
+       header->set = lookup_header_set(root, namespaces);
+       header->attached_by = header->ctl_table;
+       header->attached_to = root_table;
+       header->parent = &root_table_header;
+       for (set = header->set; set; set = set->parent) {
+               struct ctl_table_header *p;
+               list_for_each_entry(p, &set->list, ctl_entry) {
+                       if (p->unregistering)
+                               continue;
+                       try_attach(p, header);
+               }
+       }
+       header->parent->count++;
+       list_add_tail(&header->ctl_entry, &header->set->list);
        spin_unlock(&sysctl_lock);
 
        return header;
@@ -1828,8 +1925,37 @@ void unregister_sysctl_table(struct ctl_table_header * header)
 
        spin_lock(&sysctl_lock);
        start_unregistering(header);
+       if (!--header->parent->count) {
+               WARN_ON(1);
+               kfree(header->parent);
+       }
+       if (!--header->count)
+               kfree(header);
        spin_unlock(&sysctl_lock);
-       kfree(header);
+}
+
+int sysctl_is_seen(struct ctl_table_header *p)
+{
+       struct ctl_table_set *set = p->set;
+       int res;
+       spin_lock(&sysctl_lock);
+       if (p->unregistering)
+               res = 0;
+       else if (!set->is_seen)
+               res = 1;
+       else
+               res = set->is_seen(set);
+       spin_unlock(&sysctl_lock);
+       return res;
+}
+
+void setup_sysctl_set(struct ctl_table_set *p,
+       struct ctl_table_set *parent,
+       int (*is_seen)(struct ctl_table_set *))
+{
+       INIT_LIST_HEAD(&p->list);
+       p->parent = parent ? parent : &sysctl_table_root.default_set;
+       p->is_seen = is_seen;
 }
 
 #else /* !CONFIG_SYSCTL */
@@ -1848,6 +1974,16 @@ void unregister_sysctl_table(struct ctl_table_header * table)
 {
 }
 
+void setup_sysctl_set(struct ctl_table_set *p,
+       struct ctl_table_set *parent,
+       int (*is_seen)(struct ctl_table_set *))
+{
+}
+
+void sysctl_head_put(struct ctl_table_header *head)
+{
+}
+
 #endif /* CONFIG_SYSCTL */
 
 /*
index fc20e09a6cb1438f8c7b854e9cadb01aa109db02..8f3fb3db61c39306bdbffd36303cfed7dab02420 100644 (file)
@@ -1183,7 +1183,6 @@ static void *find_next_entry_inc(struct trace_iterator *iter)
 static void *s_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct trace_iterator *iter = m->private;
-       void *last_ent = iter->ent;
        int i = (int)*pos;
        void *ent;
 
index 3da47ccdc5e594b242dc30f1e3471a27822c53f8..8ebcd8532dfb33f80518feb2fc7e1cad1314bc31 100644 (file)
@@ -94,10 +94,10 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p)
                stats->hiwater_vm    = mm->hiwater_vm * PAGE_SIZE / KB;
                mmput(mm);
        }
-       stats->read_char        = p->rchar;
-       stats->write_char       = p->wchar;
-       stats->read_syscalls    = p->syscr;
-       stats->write_syscalls   = p->syscw;
+       stats->read_char        = p->ioac.rchar;
+       stats->write_char       = p->ioac.wchar;
+       stats->read_syscalls    = p->ioac.syscr;
+       stats->write_syscalls   = p->ioac.syscw;
 #ifdef CONFIG_TASK_IO_ACCOUNTING
        stats->read_bytes       = p->ioac.read_bytes;
        stats->write_bytes      = p->ioac.write_bytes;
index 2ed8b0389c51a91d7dbc24fa210310d3986ca5fe..5de7633e1dbe5c4da6b749f9fbd5bfd9841a382b 100644 (file)
@@ -1758,8 +1758,9 @@ static int __remove_suid(struct dentry *dentry, int kill)
        return notify_change(dentry, &newattrs);
 }
 
-int remove_suid(struct dentry *dentry)
+int file_remove_suid(struct file *file)
 {
+       struct dentry *dentry = file->f_path.dentry;
        int killsuid = should_remove_suid(dentry);
        int killpriv = security_inode_need_killpriv(dentry);
        int error = 0;
@@ -1773,7 +1774,7 @@ int remove_suid(struct dentry *dentry)
 
        return error;
 }
-EXPORT_SYMBOL(remove_suid);
+EXPORT_SYMBOL(file_remove_suid);
 
 static size_t __iovec_copy_from_user_inatomic(char *vaddr,
                        const struct iovec *iov, size_t base, size_t bytes)
@@ -2529,7 +2530,7 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
        if (count == 0)
                goto out;
 
-       err = remove_suid(file->f_path.dentry);
+       err = file_remove_suid(file);
        if (err)
                goto out;
 
index 3e744abcce9daa8aada1487bd42e31b769c414bf..98a3f31ccd6ab2070aed454351c22279efa338f5 100644 (file)
@@ -380,7 +380,7 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        if (count == 0)
                goto out_backing;
 
-       ret = remove_suid(filp->f_path.dentry);
+       ret = file_remove_suid(filp);
        if (ret)
                goto out_backing;
 
index f5664c5b9eb1433425501486562c3c6bb98b2237..8e5aadd7dcd6aef09f581d3ff925072cc494b0c0 100644 (file)
@@ -191,7 +191,7 @@ shmem_check_acl(struct inode *inode, int mask)
  * shmem_permission  -  permission() inode operation
  */
 int
-shmem_permission(struct inode *inode, int mask, struct nameidata *nd)
+shmem_permission(struct inode *inode, int mask)
 {
        return generic_permission(inode, mask, shmem_check_acl);
 }
index 0efd83097ecf94aa7bceeb08737bc29caa74e91b..9341ca77bd88240741afbcbabed9019f247a7531 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -3,6 +3,7 @@
 #include <linux/string.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/sched.h>
 #include <asm/uaccess.h>
 
 /**
@@ -69,25 +70,22 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp)
 EXPORT_SYMBOL(kmemdup);
 
 /**
- * krealloc - reallocate memory. The contents will remain unchanged.
+ * __krealloc - like krealloc() but don't free @p.
  * @p: object to reallocate memory for.
  * @new_size: how many bytes of memory are required.
  * @flags: the type of memory to allocate.
  *
- * The contents of the object pointed to are preserved up to the
- * lesser of the new and old sizes.  If @p is %NULL, krealloc()
- * behaves exactly like kmalloc().  If @size is 0 and @p is not a
- * %NULL pointer, the object pointed to is freed.
+ * This function is like krealloc() except it never frees the originally
+ * allocated buffer. Use this if you don't want to free the buffer immediately
+ * like, for example, with RCU.
  */
-void *krealloc(const void *p, size_t new_size, gfp_t flags)
+void *__krealloc(const void *p, size_t new_size, gfp_t flags)
 {
        void *ret;
        size_t ks = 0;
 
-       if (unlikely(!new_size)) {
-               kfree(p);
+       if (unlikely(!new_size))
                return ZERO_SIZE_PTR;
-       }
 
        if (p)
                ks = ksize(p);
@@ -96,10 +94,37 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags)
                return (void *)p;
 
        ret = kmalloc_track_caller(new_size, flags);
-       if (ret && p) {
+       if (ret && p)
                memcpy(ret, p, ks);
+
+       return ret;
+}
+EXPORT_SYMBOL(__krealloc);
+
+/**
+ * krealloc - reallocate memory. The contents will remain unchanged.
+ * @p: object to reallocate memory for.
+ * @new_size: how many bytes of memory are required.
+ * @flags: the type of memory to allocate.
+ *
+ * The contents of the object pointed to are preserved up to the
+ * lesser of the new and old sizes.  If @p is %NULL, krealloc()
+ * behaves exactly like kmalloc().  If @size is 0 and @p is not a
+ * %NULL pointer, the object pointed to is freed.
+ */
+void *krealloc(const void *p, size_t new_size, gfp_t flags)
+{
+       void *ret;
+
+       if (unlikely(!new_size)) {
                kfree(p);
+               return ZERO_SIZE_PTR;
        }
+
+       ret = __krealloc(p, new_size, flags);
+       if (ret && p != ret)
+               kfree(p);
+
        return ret;
 }
 EXPORT_SYMBOL(krealloc);
index 07b5b82c5eabae0e2be2158b7fb738bd95668e3c..0c850427a85b437e4e82e80b6c5f7f5bc52e06d0 100644 (file)
@@ -959,7 +959,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -986,7 +986,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 690bc3ab186c67cee4ba7844bc19b32ba2c2aafb..1a58af51a2e24a374d2d558e65e8206a5c96f329 100644 (file)
@@ -93,28 +93,20 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
 
 static int __init ebtable_filter_init(void)
 {
-       int i, j, ret;
+       int ret;
 
        ret = ebt_register_table(&frame_filter);
        if (ret < 0)
                return ret;
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
-               if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
-                       goto cleanup;
-       return ret;
-cleanup:
-       for (j = 0; j < i; j++)
-               nf_unregister_hook(&ebt_ops_filter[j]);
-       ebt_unregister_table(&frame_filter);
+       ret = nf_register_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
+       if (ret < 0)
+               ebt_unregister_table(&frame_filter);
        return ret;
 }
 
 static void __exit ebtable_filter_fini(void)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
-               nf_unregister_hook(&ebt_ops_filter[i]);
+       nf_unregister_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
        ebt_unregister_table(&frame_filter);
 }
 
index 5b495fe2d0b6bb6326d583766c28af7ff272ff93..f60c1e78e57537541e9e1d2ddbd186ff4e681df5 100644 (file)
@@ -100,28 +100,20 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
 
 static int __init ebtable_nat_init(void)
 {
-       int i, ret, j;
+       int ret;
 
        ret = ebt_register_table(&frame_nat);
        if (ret < 0)
                return ret;
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
-               if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
-                       goto cleanup;
-       return ret;
-cleanup:
-       for (j = 0; j < i; j++)
-               nf_unregister_hook(&ebt_ops_nat[j]);
-       ebt_unregister_table(&frame_nat);
+       ret = nf_register_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
+       if (ret < 0)
+               ebt_unregister_table(&frame_nat);
        return ret;
 }
 
 static void __exit ebtable_nat_fini(void)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
-               nf_unregister_hook(&ebt_ops_nat[i]);
+       nf_unregister_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
        ebt_unregister_table(&frame_nat);
 }
 
index 8a28fc93b72424bafe31a63e336ca4ead747dc89..dd61dcad60199a9ae29242f0c240e4bf4160df77 100644 (file)
@@ -285,7 +285,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -315,7 +315,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -366,7 +366,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -402,7 +402,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
                for (; list; list=list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 53af7841018a73573bfb89819b392bcd92434b42..8d13a9b9f1df66a51daf0aefe71a6a854ea47095 100644 (file)
@@ -1973,7 +1973,7 @@ static void net_tx_action(struct softirq_action *h)
                        struct sk_buff *skb = clist;
                        clist = clist->next;
 
-                       BUG_TRAP(!atomic_read(&skb->users));
+                       WARN_ON(atomic_read(&skb->users));
                        __kfree_skb(skb);
                }
        }
@@ -3847,7 +3847,7 @@ static void rollback_registered(struct net_device *dev)
                dev->uninit(dev);
 
        /* Notifier chain MUST detach us from master device. */
-       BUG_TRAP(!dev->master);
+       WARN_ON(dev->master);
 
        /* Remove entries from kobject tree */
        netdev_unregister_kobject(dev);
@@ -4169,9 +4169,9 @@ void netdev_run_todo(void)
 
                /* paranoia */
                BUG_ON(atomic_read(&dev->refcnt));
-               BUG_TRAP(!dev->ip_ptr);
-               BUG_TRAP(!dev->ip6_ptr);
-               BUG_TRAP(!dev->dn_ptr);
+               WARN_ON(dev->ip_ptr);
+               WARN_ON(dev->ip6_ptr);
+               WARN_ON(dev->dn_ptr);
 
                if (dev->destructor)
                        dev->destructor(dev);
index 2d3035d3abd7a923eef8849f0b31590ee418bdd4..7552495aff7aef090d99654312d5365afa421e95 100644 (file)
@@ -123,7 +123,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue)
                }
        }
 
-       BUG_TRAP(lopt->qlen == 0);
+       WARN_ON(lopt->qlen != 0);
        if (lopt_size > PAGE_SIZE)
                vfree(lopt);
        else
index e4115672b6cfb1094de109c3fd56db796fc3741b..4e0c922741897eb1ad158880ea9cb96f692894fe 100644 (file)
@@ -1200,7 +1200,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -1229,7 +1229,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -1475,7 +1475,7 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
                skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + frag->size;
                if ((copy = end - offset) > 0) {
@@ -1503,7 +1503,7 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -1552,7 +1552,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -1581,7 +1581,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -1629,7 +1629,7 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -1662,7 +1662,7 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
                        __wsum csum2;
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -2373,7 +2373,7 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -2397,7 +2397,7 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 4a0ad152c9c4492c35fc3abc6ac21bf4580abb20..a6b3437ff082d9cf98b59bb754b409817f3b4597 100644 (file)
@@ -192,13 +192,13 @@ void sk_stream_kill_queues(struct sock *sk)
        __skb_queue_purge(&sk->sk_error_queue);
 
        /* Next, the write queue. */
-       BUG_TRAP(skb_queue_empty(&sk->sk_write_queue));
+       WARN_ON(!skb_queue_empty(&sk->sk_write_queue));
 
        /* Account for returned memory. */
        sk_mem_reclaim(sk);
 
-       BUG_TRAP(!sk->sk_wmem_queued);
-       BUG_TRAP(!sk->sk_forward_alloc);
+       WARN_ON(sk->sk_wmem_queued);
+       WARN_ON(sk->sk_forward_alloc);
 
        /* It is _impossible_ for the backlog to contain anything
         * when we get here.  All user references to this socket
index 8c6b706963ff01b02c87a54f2c70103e7f7f8f29..164b090d5ac3d5fed2b1d84753d11101f8a790f0 100644 (file)
@@ -27,7 +27,6 @@
 
 #include <linux/dmaengine.h>
 #include <linux/socket.h>
-#include <linux/rtnetlink.h> /* for BUG_TRAP */
 #include <net/tcp.h>
 #include <net/netdma.h>
 
@@ -72,7 +71,7 @@ int dma_skb_copy_datagram_iovec(struct dma_chan *chan,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                copy = end - offset;
@@ -101,7 +100,7 @@ int dma_skb_copy_datagram_iovec(struct dma_chan *chan,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        copy = end - offset;
index 32617e0576cb04e240d654f13c2cbecb64d9230a..743d85fcd6518d6551a88f6362da60d3895269c9 100644 (file)
@@ -164,7 +164,7 @@ static inline bool dccp_loss_free(const u64 s1, const u64 s2, const u64 ndp)
 {
        s64 delta = dccp_delta_seqno(s1, s2);
 
-       BUG_TRAP(delta >= 0);
+       WARN_ON(delta < 0);
        return (u64)delta <= ndp + 1;
 }
 
index 08392ed86c25403ea574c1e2b39786b8488e4625..df2f110df94a4bf2ccc1dcecb1d588fea65ff778 100644 (file)
@@ -413,7 +413,7 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
 
                /* Stop the REQUEST timer */
                inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
-               BUG_TRAP(sk->sk_send_head != NULL);
+               WARN_ON(sk->sk_send_head == NULL);
                __kfree_skb(sk->sk_send_head);
                sk->sk_send_head = NULL;
 
index 2622ace17c467d528efbc41de9705a5b4d4ec49e..a835b88237cbb77d295ba7a036c57b17a1871f91 100644 (file)
@@ -283,7 +283,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
                 * ICMPs are not backlogged, hence we cannot get an established
                 * socket here.
                 */
-               BUG_TRAP(!req->sk);
+               WARN_ON(req->sk);
 
                if (seq != dccp_rsk(req)->dreq_iss) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index b74e8b2cbe55938fe79873097ac17ae16111aba3..da509127e00cbc8077eb8058f74085e3beb03627 100644 (file)
@@ -186,7 +186,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                 * ICMPs are not backlogged, hence we cannot get an established
                 * socket here.
                 */
-               BUG_TRAP(req->sk == NULL);
+               WARN_ON(req->sk != NULL);
 
                if (seq != dccp_rsk(req)->dreq_iss) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index a0b56009611f149da6e172998e104cd62b740984..b622d9744856a9e8320f812cb9e9d85699f1a6a0 100644 (file)
@@ -327,7 +327,7 @@ int dccp_disconnect(struct sock *sk, int flags)
        inet_csk_delack_init(sk);
        __sk_dst_reset(sk);
 
-       BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
+       WARN_ON(inet->num && !icsk->icsk_bind_hash);
 
        sk->sk_error_report(sk);
        return err;
@@ -981,7 +981,7 @@ adjudge_to_death:
         */
        local_bh_disable();
        bh_lock_sock(sk);
-       BUG_TRAP(!sock_owned_by_user(sk));
+       WARN_ON(sock_owned_by_user(sk));
 
        /* Have we already been destroyed by a softirq or backlog? */
        if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
index 3608d5342ca22335bf474fcaac15d9ebb10f83a7..6a5b961b6f5c08fdec108f72e66a2352c0199c78 100644 (file)
@@ -106,7 +106,7 @@ static void dccp_retransmit_timer(struct sock *sk)
         *      -- Acks     in client-PARTOPEN state (sec. 8.1.5)
         *      -- CloseReq in server-CLOSEREQ state (sec. 8.3)
         *      -- Close    in   node-CLOSING  state (sec. 8.3)                */
-       BUG_TRAP(sk->sk_send_head != NULL);
+       WARN_ON(sk->sk_send_head == NULL);
 
        /*
         * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was
index f440a9f54924e8d802b55d4bd59170c10b28d666..8a3ac1fa71a976e8c8f4c4f2dfd9fa87af2f9a99 100644 (file)
@@ -148,10 +148,10 @@ void inet_sock_destruct(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(!sk->sk_wmem_queued);
-       BUG_TRAP(!sk->sk_forward_alloc);
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(sk->sk_wmem_queued);
+       WARN_ON(sk->sk_forward_alloc);
 
        kfree(inet->opt);
        dst_release(sk->sk_dst_cache);
@@ -338,7 +338,7 @@ lookup_protocol:
        answer_flags = answer->flags;
        rcu_read_unlock();
 
-       BUG_TRAP(answer_prot->slab != NULL);
+       WARN_ON(answer_prot->slab == NULL);
 
        err = -ENOBUFS;
        sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
@@ -658,8 +658,8 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 
        lock_sock(sk2);
 
-       BUG_TRAP((1 << sk2->sk_state) &
-                (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
+       WARN_ON(!((1 << sk2->sk_state) &
+                 (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));
 
        sock_graft(sk2, newsock);
 
@@ -1439,6 +1439,10 @@ static int __init inet_init(void)
 
        (void)sock_register(&inet_family_ops);
 
+#ifdef CONFIG_SYSCTL
+       ip_static_sysctl_init();
+#endif
+
        /*
         *      Add all the base protocols.
         */
index 2e667e2f90df2e3c97845e7eee59cabd180c55de..91d3d96805d03aef392e0f514b1bd1750a9757d4 100644 (file)
@@ -138,8 +138,8 @@ void in_dev_finish_destroy(struct in_device *idev)
 {
        struct net_device *dev = idev->dev;
 
-       BUG_TRAP(!idev->ifa_list);
-       BUG_TRAP(!idev->mc_list);
+       WARN_ON(idev->ifa_list);
+       WARN_ON(idev->mc_list);
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
               idev, dev ? dev->name : "NIL");
@@ -399,7 +399,7 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
        }
        ipv4_devconf_setall(in_dev);
        if (ifa->ifa_dev != in_dev) {
-               BUG_TRAP(!ifa->ifa_dev);
+               WARN_ON(ifa->ifa_dev);
                in_dev_hold(in_dev);
                ifa->ifa_dev = in_dev;
        }
index bb81c958b7447ecc1f7cee2b898ab727756cb927..0c1ae68ee84bb24a08b73d6798ceb66db63bd176 100644 (file)
@@ -167,7 +167,7 @@ tb_not_found:
 success:
        if (!inet_csk(sk)->icsk_bind_hash)
                inet_bind_hash(sk, tb, snum);
-       BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb);
+       WARN_ON(inet_csk(sk)->icsk_bind_hash != tb);
        ret = 0;
 
 fail_unlock:
@@ -260,7 +260,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
        }
 
        newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
-       BUG_TRAP(newsk->sk_state != TCP_SYN_RECV);
+       WARN_ON(newsk->sk_state == TCP_SYN_RECV);
 out:
        release_sock(sk);
        return newsk;
@@ -386,7 +386,7 @@ struct request_sock *inet_csk_search_req(const struct sock *sk,
                    ireq->rmt_addr == raddr &&
                    ireq->loc_addr == laddr &&
                    AF_INET_FAMILY(req->rsk_ops->family)) {
-                       BUG_TRAP(!req->sk);
+                       WARN_ON(req->sk);
                        *prevp = prev;
                        break;
                }
@@ -539,14 +539,14 @@ EXPORT_SYMBOL_GPL(inet_csk_clone);
  */
 void inet_csk_destroy_sock(struct sock *sk)
 {
-       BUG_TRAP(sk->sk_state == TCP_CLOSE);
-       BUG_TRAP(sock_flag(sk, SOCK_DEAD));
+       WARN_ON(sk->sk_state != TCP_CLOSE);
+       WARN_ON(!sock_flag(sk, SOCK_DEAD));
 
        /* It cannot be in hash table! */
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
 
        /* If it has not 0 inet_sk(sk)->num, it must be bound */
-       BUG_TRAP(!inet_sk(sk)->num || inet_csk(sk)->icsk_bind_hash);
+       WARN_ON(inet_sk(sk)->num && !inet_csk(sk)->icsk_bind_hash);
 
        sk->sk_prot->destroy(sk);
 
@@ -629,7 +629,7 @@ void inet_csk_listen_stop(struct sock *sk)
 
                local_bh_disable();
                bh_lock_sock(child);
-               BUG_TRAP(!sock_owned_by_user(child));
+               WARN_ON(sock_owned_by_user(child));
                sock_hold(child);
 
                sk->sk_prot->disconnect(child, O_NONBLOCK);
@@ -647,7 +647,7 @@ void inet_csk_listen_stop(struct sock *sk)
                sk_acceptq_removed(sk);
                __reqsk_free(req);
        }
-       BUG_TRAP(!sk->sk_ack_backlog);
+       WARN_ON(sk->sk_ack_backlog);
 }
 
 EXPORT_SYMBOL_GPL(inet_csk_listen_stop);
index 0546a0bc97ea37bdc1b9bef0c22d6b82ec190abf..6c52e08f786e6f574e71c589ddbb86b26f2f43a0 100644 (file)
@@ -134,8 +134,8 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f,
        struct sk_buff *fp;
        struct netns_frags *nf;
 
-       BUG_TRAP(q->last_in & INET_FRAG_COMPLETE);
-       BUG_TRAP(del_timer(&q->timer) == 0);
+       WARN_ON(!(q->last_in & INET_FRAG_COMPLETE));
+       WARN_ON(del_timer(&q->timer) != 0);
 
        /* Release all fragment data. */
        fp = q->fragments;
index 115f53722d2058d8847a53139ddf2fd635960e7d..44981906fb913e7afbb4f7e03cd0192b9fdcda12 100644 (file)
@@ -305,7 +305,7 @@ unique:
        inet->num = lport;
        inet->sport = htons(lport);
        sk->sk_hash = hash;
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        __sk_add_node(sk, &head->chain);
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
        write_unlock(lock);
@@ -342,7 +342,7 @@ void __inet_hash_nolisten(struct sock *sk)
        rwlock_t *lock;
        struct inet_ehash_bucket *head;
 
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
 
        sk->sk_hash = inet_sk_ehashfn(sk);
        head = inet_ehash_bucket(hashinfo, sk->sk_hash);
@@ -367,7 +367,7 @@ static void __inet_hash(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
        lock = &hashinfo->lhash_lock;
 
@@ -450,7 +450,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
                         */
                        inet_bind_bucket_for_each(tb, node, &head->chain) {
                                if (tb->ib_net == net && tb->port == port) {
-                                       BUG_TRAP(!hlist_empty(&tb->owners));
+                                       WARN_ON(hlist_empty(&tb->owners));
                                        if (tb->fastreuse >= 0)
                                                goto next_port;
                                        if (!check_established(death_row, sk,
index 75c2def8f9a0cbcd04a0a324e28cde2e756bb480..d985bd613d25d3895f3b7d32fee5f571062d52be 100644 (file)
@@ -86,7 +86,7 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
                        hashinfo->bhash_size)];
        spin_lock(&bhead->lock);
        tw->tw_tb = icsk->icsk_bind_hash;
-       BUG_TRAP(icsk->icsk_bind_hash);
+       WARN_ON(!icsk->icsk_bind_hash);
        inet_twsk_add_bind_node(tw, &tw->tw_tb->owners);
        spin_unlock(&bhead->lock);
 
index 38d38f058018ddf55b7a17257576cf9310c4af3b..2152d222b954b3eca22bba260e30a5a551316da0 100644 (file)
@@ -488,8 +488,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
                qp->q.fragments = head;
        }
 
-       BUG_TRAP(head != NULL);
-       BUG_TRAP(FRAG_CB(head)->offset == 0);
+       WARN_ON(head == NULL);
+       WARN_ON(FRAG_CB(head)->offset != 0);
 
        /* Allocate a new buffer for the datagram. */
        ihlen = ip_hdrlen(head);
index 465544f6281afbab2990bc561f946f60ae4615aa..d533a89e08de14d2e7417b41dddcb99d8e9c881b 100644 (file)
@@ -118,7 +118,7 @@ static int ip_dev_loopback_xmit(struct sk_buff *newskb)
        __skb_pull(newskb, skb_network_offset(newskb));
        newskb->pkt_type = PACKET_LOOPBACK;
        newskb->ip_summed = CHECKSUM_UNNECESSARY;
-       BUG_TRAP(newskb->dst);
+       WARN_ON(!newskb->dst);
        netif_rx(newskb);
        return 0;
 }
index 3be4d07e7ed9880a17d78a5476e240a1d1d40bf0..082f5dd3156c609b324af20330c7875b58ddfbb1 100644 (file)
@@ -55,32 +55,53 @@ static struct xt_table packet_filter = {
 };
 
 /* The work comes in here from netfilter.c */
-static unsigned int arpt_hook(unsigned int hook,
-                             struct sk_buff *skb,
-                             const struct net_device *in,
-                             const struct net_device *out,
-                             int (*okfn)(struct sk_buff *))
+static unsigned int arpt_in_hook(unsigned int hook,
+                                struct sk_buff *skb,
+                                const struct net_device *in,
+                                const struct net_device *out,
+                                int (*okfn)(struct sk_buff *))
 {
-       return arpt_do_table(skb, hook, in, out, init_net.ipv4.arptable_filter);
+       return arpt_do_table(skb, hook, in, out,
+                            dev_net(in)->ipv4.arptable_filter);
+}
+
+static unsigned int arpt_out_hook(unsigned int hook,
+                                 struct sk_buff *skb,
+                                 const struct net_device *in,
+                                 const struct net_device *out,
+                                 int (*okfn)(struct sk_buff *))
+{
+       return arpt_do_table(skb, hook, in, out,
+                            dev_net(out)->ipv4.arptable_filter);
+}
+
+static unsigned int arpt_forward_hook(unsigned int hook,
+                                     struct sk_buff *skb,
+                                     const struct net_device *in,
+                                     const struct net_device *out,
+                                     int (*okfn)(struct sk_buff *))
+{
+       return arpt_do_table(skb, hook, in, out,
+                            dev_net(in)->ipv4.arptable_filter);
 }
 
 static struct nf_hook_ops arpt_ops[] __read_mostly = {
        {
-               .hook           = arpt_hook,
+               .hook           = arpt_in_hook,
                .owner          = THIS_MODULE,
                .pf             = NF_ARP,
                .hooknum        = NF_ARP_IN,
                .priority       = NF_IP_PRI_FILTER,
        },
        {
-               .hook           = arpt_hook,
+               .hook           = arpt_out_hook,
                .owner          = THIS_MODULE,
                .pf             = NF_ARP,
                .hooknum        = NF_ARP_OUT,
                .priority       = NF_IP_PRI_FILTER,
        },
        {
-               .hook           = arpt_hook,
+               .hook           = arpt_forward_hook,
                .owner          = THIS_MODULE,
                .pf             = NF_ARP,
                .hooknum        = NF_ARP_FORWARD,
index 2b472ac2263a612a3d6776764abd1410e1a7ed01..db6d312128e1f3623d78a579bca55dc9c7d125fb 100644 (file)
@@ -32,7 +32,7 @@ static struct
        struct ipt_replace repl;
        struct ipt_standard entries[3];
        struct ipt_error term;
-} initial_table __initdata = {
+} initial_table __net_initdata = {
        .repl = {
                .name = "security",
                .valid_hooks = SECURITY_VALID_HOOKS,
index e4ab0ac94f9225f4ac9f6d2f12d4ece0c1383400..380d6474cf661d91fb45ef88699c526b23dabd01 100644 (file)
@@ -1502,7 +1502,7 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph,
                                    rth->fl.iif != 0 ||
                                    dst_metric_locked(&rth->u.dst, RTAX_MTU) ||
                                    !net_eq(dev_net(rth->u.dst.dev), net) ||
-                                   !rt_is_expired(rth))
+                                   rt_is_expired(rth))
                                        continue;
 
                                if (new_mtu < 68 || new_mtu >= old_mtu) {
@@ -2914,7 +2914,7 @@ static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table,
        return 0;
 }
 
-ctl_table ipv4_route_table[] = {
+static ctl_table ipv4_route_table[] = {
        {
                .ctl_name       = NET_IPV4_ROUTE_GC_THRESH,
                .procname       = "gc_thresh",
@@ -3216,6 +3216,15 @@ int __init ip_rt_init(void)
        return rc;
 }
 
+/*
+ * We really need to sanitize the damn ipv4 init order, then all
+ * this nonsense will go away.
+ */
+void __init ip_static_sysctl_init(void)
+{
+       register_sysctl_paths(ipv4_route_path, ipv4_route_table);
+}
+
 EXPORT_SYMBOL(__ip_select_ident);
 EXPORT_SYMBOL(ip_route_input);
 EXPORT_SYMBOL(ip_route_output_key);
index 51bc24d3b8a71580a097ff13df9a4781f10b0575..9d38005abbace6956bf3a5493aaacf58d7a9d90b 100644 (file)
@@ -299,6 +299,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
        ireq->rmt_port          = th->source;
        ireq->loc_addr          = ip_hdr(skb)->daddr;
        ireq->rmt_addr          = ip_hdr(skb)->saddr;
+       ireq->ecn_ok            = 0;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->rcv_wscale        = tcp_opt.rcv_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
index 14ef202a2254f21a57e4c7e5dfdd666e8ef65e63..770d827f5ab87a75ff6fcaa19a83f3fd7b7f112c 100644 (file)
@@ -401,13 +401,6 @@ static struct ctl_table ipv4_table[] = {
                .proc_handler   = &ipv4_local_port_range,
                .strategy       = &ipv4_sysctl_local_port_range,
        },
-       {
-               .ctl_name       = NET_IPV4_ROUTE,
-               .procname       = "route",
-               .maxlen         = 0,
-               .mode           = 0555,
-               .child          = ipv4_route_table
-       },
 #ifdef CONFIG_IP_MULTICAST
        {
                .ctl_name       = NET_IPV4_IGMP_MAX_MEMBERSHIPS,
index 0b491bf03db48e275e0b4ad2978d80e9ffd92d57..1ab341e5d3e0f70383fdfa460b0ee9071b282df7 100644 (file)
@@ -1096,7 +1096,7 @@ void tcp_cleanup_rbuf(struct sock *sk, int copied)
 #if TCP_DEBUG
        struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
 
-       BUG_TRAP(!skb || before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
+       WARN_ON(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
 #endif
 
        if (inet_csk_ack_scheduled(sk)) {
@@ -1358,7 +1358,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                                goto found_ok_skb;
                        if (tcp_hdr(skb)->fin)
                                goto found_fin_ok;
-                       BUG_TRAP(flags & MSG_PEEK);
+                       WARN_ON(!(flags & MSG_PEEK));
                        skb = skb->next;
                } while (skb != (struct sk_buff *)&sk->sk_receive_queue);
 
@@ -1421,8 +1421,8 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
                        tp->ucopy.len = len;
 
-                       BUG_TRAP(tp->copied_seq == tp->rcv_nxt ||
-                                (flags & (MSG_PEEK | MSG_TRUNC)));
+                       WARN_ON(tp->copied_seq != tp->rcv_nxt &&
+                               !(flags & (MSG_PEEK | MSG_TRUNC)));
 
                        /* Ugly... If prequeue is not empty, we have to
                         * process it before releasing socket, otherwise
@@ -1844,7 +1844,7 @@ adjudge_to_death:
         */
        local_bh_disable();
        bh_lock_sock(sk);
-       BUG_TRAP(!sock_owned_by_user(sk));
+       WARN_ON(sock_owned_by_user(sk));
 
        /* Have we already been destroyed by a softirq or backlog? */
        if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
@@ -1973,7 +1973,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
        __sk_dst_reset(sk);
 
-       BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
+       WARN_ON(inet->num && !icsk->icsk_bind_hash);
 
        sk->sk_error_report(sk);
        return err;
index 75efd244f2afc68138ed9c1ffc6c382670adf542..67ccce2a96bd0d7d506bc7bca08a7ac29c38fea6 100644 (file)
@@ -1629,10 +1629,10 @@ advance_sp:
 out:
 
 #if FASTRETRANS_DEBUG > 0
-       BUG_TRAP((int)tp->sacked_out >= 0);
-       BUG_TRAP((int)tp->lost_out >= 0);
-       BUG_TRAP((int)tp->retrans_out >= 0);
-       BUG_TRAP((int)tcp_packets_in_flight(tp) >= 0);
+       WARN_ON((int)tp->sacked_out < 0);
+       WARN_ON((int)tp->lost_out < 0);
+       WARN_ON((int)tp->retrans_out < 0);
+       WARN_ON((int)tcp_packets_in_flight(tp) < 0);
 #endif
        return flag;
 }
@@ -2181,7 +2181,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets)
        int err;
        unsigned int mss;
 
-       BUG_TRAP(packets <= tp->packets_out);
+       WARN_ON(packets > tp->packets_out);
        if (tp->lost_skb_hint) {
                skb = tp->lost_skb_hint;
                cnt = tp->lost_cnt_hint;
@@ -2610,7 +2610,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
        /* E. Check state exit conditions. State can be terminated
         *    when high_seq is ACKed. */
        if (icsk->icsk_ca_state == TCP_CA_Open) {
-               BUG_TRAP(tp->retrans_out == 0);
+               WARN_ON(tp->retrans_out != 0);
                tp->retrans_stamp = 0;
        } else if (!before(tp->snd_una, tp->high_seq)) {
                switch (icsk->icsk_ca_state) {
@@ -2972,9 +2972,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets)
        }
 
 #if FASTRETRANS_DEBUG > 0
-       BUG_TRAP((int)tp->sacked_out >= 0);
-       BUG_TRAP((int)tp->lost_out >= 0);
-       BUG_TRAP((int)tp->retrans_out >= 0);
+       WARN_ON((int)tp->sacked_out < 0);
+       WARN_ON((int)tp->lost_out < 0);
+       WARN_ON((int)tp->retrans_out < 0);
        if (!tp->packets_out && tcp_is_sack(tp)) {
                icsk = inet_csk(sk);
                if (tp->lost_out) {
@@ -3877,7 +3877,7 @@ static void tcp_sack_remove(struct tcp_sock *tp)
                        int i;
 
                        /* RCV.NXT must cover all the block! */
-                       BUG_TRAP(!before(tp->rcv_nxt, sp->end_seq));
+                       WARN_ON(before(tp->rcv_nxt, sp->end_seq));
 
                        /* Zap this SACK, by moving forward any other SACKS. */
                        for (i=this_sack+1; i < num_sacks; i++)
index a82df6307567e61675720a9bc6f5f03dcecbdb4b..a2b06d0cc26b70075a6cd649db7cb7c19451f76c 100644 (file)
@@ -418,7 +418,7 @@ void tcp_v4_err(struct sk_buff *skb, u32 info)
                /* ICMPs are not backlogged, hence we cannot get
                   an established socket here.
                 */
-               BUG_TRAP(!req->sk);
+               WARN_ON(req->sk);
 
                if (seq != tcp_rsk(req)->snt_isn) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index 328e0cf42b3c6e164c09da5d54b29566ff5c1480..5ab6ba19c3ce8547076f9fefbdf83c60f3c01c44 100644 (file)
@@ -287,7 +287,7 @@ static void tcp_retransmit_timer(struct sock *sk)
        if (!tp->packets_out)
                goto out;
 
-       BUG_TRAP(!tcp_write_queue_empty(sk));
+       WARN_ON(tcp_write_queue_empty(sk));
 
        if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
            !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
index 74d543d504a1d6fcfbe859235e70bd05418b4364..a7842c54f58a0905201751a9d32a7e270875aaf4 100644 (file)
@@ -313,8 +313,10 @@ static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
 void in6_dev_finish_destroy(struct inet6_dev *idev)
 {
        struct net_device *dev = idev->dev;
-       BUG_TRAP(idev->addr_list==NULL);
-       BUG_TRAP(idev->mc_list==NULL);
+
+       WARN_ON(idev->addr_list != NULL);
+       WARN_ON(idev->mc_list != NULL);
+
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");
 #endif
@@ -517,8 +519,9 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
 
 void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 {
-       BUG_TRAP(ifp->if_next==NULL);
-       BUG_TRAP(ifp->lst_next==NULL);
+       WARN_ON(ifp->if_next != NULL);
+       WARN_ON(ifp->lst_next != NULL);
+
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
 #endif
index 60461ad7fa6ff0deaad6d6d27f463594ae0d7825..95055f8c3f35eb18d17f4848fe0ae72ed648bbc2 100644 (file)
@@ -150,7 +150,7 @@ lookup_protocol:
        answer_flags = answer->flags;
        rcu_read_unlock();
 
-       BUG_TRAP(answer_prot->slab != NULL);
+       WARN_ON(answer_prot->slab == NULL);
 
        err = -ENOBUFS;
        sk = sk_alloc(net, PF_INET6, GFP_KERNEL, answer_prot);
@@ -934,6 +934,11 @@ static int __init inet6_init(void)
        if (err)
                goto out_unregister_sock;
 
+#ifdef CONFIG_SYSCTL
+       err = ipv6_static_sysctl_register();
+       if (err)
+               goto static_sysctl_fail;
+#endif
        /*
         *      ipngwg API draft makes clear that the correct semantics
         *      for TCP and UDP is to consider one TCP and UDP instance
@@ -1058,6 +1063,10 @@ ipmr_fail:
 icmp_fail:
        unregister_pernet_subsys(&inet6_net_ops);
 register_pernet_fail:
+#ifdef CONFIG_SYSCTL
+       ipv6_static_sysctl_unregister();
+static_sysctl_fail:
+#endif
        cleanup_ipv6_mibs();
 out_unregister_sock:
        sock_unregister(PF_INET6);
@@ -1113,6 +1122,9 @@ static void __exit inet6_exit(void)
        rawv6_exit();
 
        unregister_pernet_subsys(&inet6_net_ops);
+#ifdef CONFIG_SYSCTL
+       ipv6_static_sysctl_unregister();
+#endif
        cleanup_ipv6_mibs();
        proto_unregister(&rawv6_prot);
        proto_unregister(&udplitev6_prot);
index 87801cc1b2f88405b363ebb11b25d58c1d16eea5..16d43f20b32f83c5994cb4d1479c6393c5ae0961 100644 (file)
@@ -98,7 +98,7 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk,
                    ipv6_addr_equal(&treq->rmt_addr, raddr) &&
                    ipv6_addr_equal(&treq->loc_addr, laddr) &&
                    (!treq->iif || treq->iif == iif)) {
-                       BUG_TRAP(req->sk == NULL);
+                       WARN_ON(req->sk != NULL);
                        *prevp = prev;
                        return req;
                }
index 00a8a5f9380c3d454419c75074a5eccf3c2bc4f7..1646a565825513421332b02411f00aa863f9c261 100644 (file)
@@ -28,7 +28,7 @@ void __inet6_hash(struct sock *sk)
        struct hlist_head *list;
        rwlock_t *lock;
 
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
 
        if (sk->sk_state == TCP_LISTEN) {
                list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
@@ -202,7 +202,7 @@ unique:
         * in hash table socket with a funny identity. */
        inet->num = lport;
        inet->sport = htons(lport);
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        __sk_add_node(sk, &head->chain);
        sk->sk_hash = hash;
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
index 08ea2de28d63c358eeec0a3b59469914dd3fca4b..52dddc25d3e6e6c96a83fd7ccd6ade53b9160fa0 100644 (file)
@@ -287,7 +287,7 @@ static int fib6_dump_node(struct fib6_walker_t *w)
                        w->leaf = rt;
                        return 1;
                }
-               BUG_TRAP(res!=0);
+               WARN_ON(res == 0);
        }
        w->leaf = NULL;
        return 0;
@@ -778,7 +778,7 @@ out:
                        pn->leaf = fib6_find_prefix(info->nl_net, pn);
 #if RT6_DEBUG >= 2
                        if (!pn->leaf) {
-                               BUG_TRAP(pn->leaf != NULL);
+                               WARN_ON(pn->leaf == NULL);
                                pn->leaf = info->nl_net->ipv6.ip6_null_entry;
                        }
 #endif
@@ -942,7 +942,7 @@ struct fib6_node * fib6_locate(struct fib6_node *root,
 
 #ifdef CONFIG_IPV6_SUBTREES
        if (src_len) {
-               BUG_TRAP(saddr!=NULL);
+               WARN_ON(saddr == NULL);
                if (fn && fn->subtree)
                        fn = fib6_locate_1(fn->subtree, saddr, src_len,
                                           offsetof(struct rt6_info, rt6i_src));
@@ -996,9 +996,9 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
                RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
                iter++;
 
-               BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));
-               BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));
-               BUG_TRAP(fn->leaf==NULL);
+               WARN_ON(fn->fn_flags & RTN_RTINFO);
+               WARN_ON(fn->fn_flags & RTN_TL_ROOT);
+               WARN_ON(fn->leaf != NULL);
 
                children = 0;
                child = NULL;
@@ -1014,7 +1014,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
                        fn->leaf = fib6_find_prefix(net, fn);
 #if RT6_DEBUG >= 2
                        if (fn->leaf==NULL) {
-                               BUG_TRAP(fn->leaf);
+                               WARN_ON(!fn->leaf);
                                fn->leaf = net->ipv6.ip6_null_entry;
                        }
 #endif
@@ -1025,16 +1025,17 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
                pn = fn->parent;
 #ifdef CONFIG_IPV6_SUBTREES
                if (FIB6_SUBTREE(pn) == fn) {
-                       BUG_TRAP(fn->fn_flags&RTN_ROOT);
+                       WARN_ON(!(fn->fn_flags & RTN_ROOT));
                        FIB6_SUBTREE(pn) = NULL;
                        nstate = FWS_L;
                } else {
-                       BUG_TRAP(!(fn->fn_flags&RTN_ROOT));
+                       WARN_ON(fn->fn_flags & RTN_ROOT);
 #endif
                        if (pn->right == fn) pn->right = child;
                        else if (pn->left == fn) pn->left = child;
 #if RT6_DEBUG >= 2
-                       else BUG_TRAP(0);
+                       else
+                               WARN_ON(1);
 #endif
                        if (child)
                                child->parent = pn;
@@ -1154,14 +1155,14 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
 
 #if RT6_DEBUG >= 2
        if (rt->u.dst.obsolete>0) {
-               BUG_TRAP(fn==NULL);
+               WARN_ON(fn != NULL);
                return -ENOENT;
        }
 #endif
        if (fn == NULL || rt == net->ipv6.ip6_null_entry)
                return -ENOENT;
 
-       BUG_TRAP(fn->fn_flags&RTN_RTINFO);
+       WARN_ON(!(fn->fn_flags & RTN_RTINFO));
 
        if (!(rt->rt6i_flags&RTF_CACHE)) {
                struct fib6_node *pn = fn;
@@ -1266,7 +1267,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
                        w->node = pn;
 #ifdef CONFIG_IPV6_SUBTREES
                        if (FIB6_SUBTREE(pn) == fn) {
-                               BUG_TRAP(fn->fn_flags&RTN_ROOT);
+                               WARN_ON(!(fn->fn_flags & RTN_ROOT));
                                w->state = FWS_L;
                                continue;
                        }
@@ -1281,7 +1282,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
                                continue;
                        }
 #if RT6_DEBUG >= 2
-                       BUG_TRAP(0);
+                       WARN_ON(1);
 #endif
                }
        }
@@ -1323,7 +1324,7 @@ static int fib6_clean_node(struct fib6_walker_t *w)
                        }
                        return 0;
                }
-               BUG_TRAP(res==0);
+               WARN_ON(res != 0);
        }
        w->leaf = rt;
        return 0;
index 6407c64ea4a5c08f1e6260b04e70a4cc142fb3e0..6811901e6b1ec94c0e3084de16c7b8c408bb0503 100644 (file)
@@ -116,7 +116,7 @@ static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
        __skb_pull(newskb, skb_network_offset(newskb));
        newskb->pkt_type = PACKET_LOOPBACK;
        newskb->ip_summed = CHECKSUM_UNNECESSARY;
-       BUG_TRAP(newskb->dst);
+       WARN_ON(!newskb->dst);
 
        netif_rx(newskb);
        return 0;
index ad1cc5bbf977a9d766a78a065277d7aa457f7806..31295c8f619688099477dd5e230c61f8199d51da 100644 (file)
@@ -164,8 +164,8 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
                        calc_padlen(sizeof(*dstopt), 6));
 
        hao->type = IPV6_TLV_HAO;
+       BUILD_BUG_ON(sizeof(*hao) != 18);
        hao->length = sizeof(*hao) - 2;
-       BUG_TRAP(hao->length == 16);
 
        len = ((char *)hao - (char *)dstopt) + sizeof(*hao);
 
@@ -174,7 +174,7 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
        memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr));
        spin_unlock_bh(&x->lock);
 
-       BUG_TRAP(len == x->props.header_len);
+       WARN_ON(len != x->props.header_len);
        dstopt->hdrlen = (x->props.header_len >> 3) - 1;
 
        return 0;
@@ -317,7 +317,7 @@ static int mip6_destopt_init_state(struct xfrm_state *x)
        x->props.header_len = sizeof(struct ipv6_destopt_hdr) +
                calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) +
                sizeof(struct ipv6_destopt_hao);
-       BUG_TRAP(x->props.header_len == 24);
+       WARN_ON(x->props.header_len != 24);
 
        return 0;
 }
@@ -380,7 +380,7 @@ static int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb)
        rt2->rt_hdr.segments_left = 1;
        memset(&rt2->reserved, 0, sizeof(rt2->reserved));
 
-       BUG_TRAP(rt2->rt_hdr.hdrlen == 2);
+       WARN_ON(rt2->rt_hdr.hdrlen != 2);
 
        memcpy(&rt2->addr, &iph->daddr, sizeof(rt2->addr));
        spin_lock_bh(&x->lock);
index a07abee30497ad25dc8eeee9a98715d9deedb176..6e7131036bc6bf7603daa7401ff418fb836368c0 100644 (file)
@@ -31,7 +31,7 @@ static struct
        struct ip6t_replace repl;
        struct ip6t_standard entries[3];
        struct ip6t_error term;
-} initial_table __initdata = {
+} initial_table __net_initdata = {
        .repl = {
                .name = "security",
                .valid_hooks = SECURITY_VALID_HOOKS,
index cf20bc4fd60db346eab94b35ec3b84ee255dd265..52d06dd4b8176f01a4e0b796e38d1fedf5c8c7c8 100644 (file)
@@ -416,8 +416,8 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
 
        fq_kill(fq);
 
-       BUG_TRAP(head != NULL);
-       BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0);
+       WARN_ON(head == NULL);
+       WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
 
        /* Unfragmented part is taken from the first segment. */
        payload_len = ((head->data - skb_network_header(head)) -
index 6ab957ec2dd6d4a64bee43cc20d754e7e85ca579..89184b576e2377f1190c69a36476d320e263c265 100644 (file)
@@ -473,8 +473,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
                fq->q.fragments = head;
        }
 
-       BUG_TRAP(head != NULL);
-       BUG_TRAP(FRAG6_CB(head)->offset == 0);
+       WARN_ON(head == NULL);
+       WARN_ON(FRAG6_CB(head)->offset != 0);
 
        /* Unfragmented part is taken from the first segment. */
        payload_len = ((head->data - skb_network_header(head)) -
index 6a68eeb7bbf8ba76b5834d5960991aad4a3021da..a46badd1082d9c55a2388dd3e31d2882db68df06 100644 (file)
@@ -223,6 +223,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
 
        req->expires = 0UL;
        req->retrans = 0;
+       ireq->ecn_ok            = 0;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->rcv_wscale        = tcp_opt.rcv_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
index 5c99274558bf40412b738c4c71484c2a082e7029..e6dfaeac6be3f9a646b832a57938b7b5832da53b 100644 (file)
@@ -150,3 +150,19 @@ void ipv6_sysctl_unregister(void)
        unregister_net_sysctl_table(ip6_header);
        unregister_pernet_subsys(&ipv6_sysctl_net_ops);
 }
+
+static struct ctl_table_header *ip6_base;
+
+int ipv6_static_sysctl_register(void)
+{
+       static struct ctl_table empty[1];
+       ip6_base = register_net_sysctl_rotable(net_ipv6_ctl_path, empty);
+       if (ip6_base == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+void ipv6_static_sysctl_unregister(void)
+{
+       unregister_net_sysctl_table(ip6_base);
+}
index ae45f98350143e524798fa6ef4b8014c07685dc1..cff778b23a7feca249b9edab81c9d6afdee8597b 100644 (file)
@@ -421,7 +421,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                /* ICMPs are not backlogged, hence we cannot get
                 * an established socket here.
                 */
-               BUG_TRAP(req->sk == NULL);
+               WARN_ON(req->sk != NULL);
 
                if (seq != tcp_rsk(req)->snt_isn) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index f0fc46c8038d0632bca9fb3b031af5b908f1ff2d..d628df97e02ed5b81002e725bdda6f200649fc08 100644 (file)
@@ -96,8 +96,8 @@ static void pfkey_sock_destruct(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
        atomic_dec(&pfkey_socks_nr);
 }
index 3469bc71a385eacb66b2099c334833469ec889cf..4b2c769d555fce4d6131b08f5aac6d53b3a48be6 100644 (file)
@@ -95,7 +95,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
        newlen = newoff + t->len;
        rcu_read_unlock();
 
-       new = krealloc(ct->ext, newlen, gfp);
+       new = __krealloc(ct->ext, newlen, gfp);
        if (!new)
                return NULL;
 
@@ -115,10 +115,10 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
                ct->ext = new;
        }
 
-       ct->ext->offset[id] = newoff;
-       ct->ext->len = newlen;
-       memset((void *)ct->ext + newoff, 0, newlen - newoff);
-       return (void *)ct->ext + newoff;
+       new->offset[id] = newoff;
+       new->len = newlen;
+       memset((void *)new + newoff, 0, newlen - newoff);
+       return (void *)new + newoff;
 }
 EXPORT_SYMBOL(__nf_ct_ext_add);
 
index 98bfe277eab22ef15ac39cd08185f0f63cc8030e..b0eacc0007cc39b1c19ccf0c38800e0e50a8d608 100644 (file)
@@ -158,9 +158,10 @@ static void netlink_sock_destruct(struct sock *sk)
                printk(KERN_ERR "Freeing alive netlink socket %p\n", sk);
                return;
        }
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(!nlk_sk(sk)->groups);
+
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(nlk_sk(sk)->groups);
 }
 
 /* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on
index d56cae112dc83b4223b28924f508b426a02c0594..c718e7e3f7dea66facd1483b0b27dd5b4ef974bc 100644 (file)
@@ -260,8 +260,8 @@ static inline struct packet_sock *pkt_sk(struct sock *sk)
 
 static void packet_sock_destruct(struct sock *sk)
 {
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk("Attempt to release alive packet socket: %p\n", sk);
index 4b2682feeedcbcf61d2b1e9fb729ad9cfff9abc1..32e489118bebcc7462316456cf9a4e8dffeaae31 100644 (file)
@@ -660,9 +660,9 @@ static void rxrpc_sock_destructor(struct sock *sk)
 
        rxrpc_purge_queue(&sk->sk_receive_queue);
 
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(sk_unhashed(sk));
-       BUG_TRAP(!sk->sk_socket);
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(!sk_unhashed(sk));
+       WARN_ON(sk->sk_socket);
 
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk("Attempt to release alive rxrpc socket: %p\n", sk);
index 74e662cbb2c5b7a61a1c0815a358899c6abc694a..d308c19aa3f9be6d6087aecfa27dcc55dead6eed 100644 (file)
@@ -41,7 +41,7 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
                        return;
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
 }
 EXPORT_SYMBOL(tcf_hash_destroy);
 
index 32c3f9d9fb7ad89d06e3b45fc5c664936e9b56ad..38015b49394755d06cd1cfa56306ea358c968a49 100644 (file)
@@ -116,7 +116,7 @@ static void tcf_police_destroy(struct tcf_police *p)
                        return;
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
 }
 
 static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
index 527db2559dd28163173d4ee875be430d31cc7f1f..246f9065ce34b0ad8f7b176807dea7d50221b087 100644 (file)
@@ -345,7 +345,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key)
                        }
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
        return 0;
 }
 
@@ -368,7 +368,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode **hn;
 
-       BUG_TRAP(!ht->refcnt);
+       WARN_ON(ht->refcnt);
 
        u32_clear_hnode(tp, ht);
 
@@ -380,7 +380,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
                }
        }
 
-       BUG_TRAP(0);
+       WARN_ON(1);
        return -ENOENT;
 }
 
@@ -389,7 +389,7 @@ static void u32_destroy(struct tcf_proto *tp)
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *root_ht = xchg(&tp->root, NULL);
 
-       BUG_TRAP(root_ht != NULL);
+       WARN_ON(root_ht == NULL);
 
        if (root_ht && --root_ht->refcnt == 0)
                u32_destroy_hnode(tp, root_ht);
@@ -407,7 +407,7 @@ static void u32_destroy(struct tcf_proto *tp)
                while ((ht = tp_c->hlist) != NULL) {
                        tp_c->hlist = ht->next;
 
-                       BUG_TRAP(ht->refcnt == 0);
+                       WARN_ON(ht->refcnt != 0);
 
                        kfree(ht);
                }
index 04faa835be173bf703cd899038f8ba3c0c686130..6b517b9dac5be93fb959e008b678904bf1c467a0 100644 (file)
@@ -162,7 +162,7 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl)
        qdisc_destroy(flow->q);
        tcf_destroy_chain(&flow->filter_list);
        if (flow->sock) {
-               pr_debug("atm_tc_put: f_count %d\n",
+               pr_debug("atm_tc_put: f_count %ld\n",
                        file_count(flow->sock->file));
                flow->vcc->pop = flow->old_pop;
                sockfd_put(flow->sock);
@@ -259,7 +259,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent,
        sock = sockfd_lookup(fd, &error);
        if (!sock)
                return error;   /* f_count++ */
-       pr_debug("atm_tc_change: f_count %d\n", file_count(sock->file));
+       pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file));
        if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) {
                error = -EPROTOTYPE;
                goto err_out;
index f1d2f8ec8b4cde95e206ff184627ae9b8bdcf55b..14954bf4a6836c8fe21731072af228d23f85b33e 100644 (file)
@@ -1175,7 +1175,7 @@ static void cbq_unlink_class(struct cbq_class *this)
                                this->tparent->children = NULL;
                }
        } else {
-               BUG_TRAP(this->sibling == this);
+               WARN_ON(this->sibling != this);
        }
 }
 
@@ -1699,7 +1699,7 @@ static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl)
 {
        struct cbq_sched_data *q = qdisc_priv(sch);
 
-       BUG_TRAP(!cl->filters);
+       WARN_ON(cl->filters);
 
        tcf_destroy_chain(&cl->filter_list);
        qdisc_destroy(cl->q);
index 43abd4d27ea6d767ae9c2227510b2628bb3d2a90..fd2a6cadb1155429fff6de7aa4045ef00899deb9 100644 (file)
@@ -746,5 +746,5 @@ void dev_shutdown(struct net_device *dev)
 {
        netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
        shutdown_scheduler_queue(dev, &dev->rx_queue, NULL);
-       BUG_TRAP(!timer_pending(&dev->watchdog_timer));
+       WARN_ON(timer_pending(&dev->watchdog_timer));
 }
index 30c999c61b01f1ab771fb3bb75984b7595fbaee4..75a40951c4f24edc68684462980e74dfd7123214 100644 (file)
@@ -524,7 +524,7 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, long *diff)
  */
 static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
 {
-       BUG_TRAP(!cl->level && cl->un.leaf.q && cl->un.leaf.q->q.qlen);
+       WARN_ON(cl->level || !cl->un.leaf.q || !cl->un.leaf.q->q.qlen);
 
        if (!cl->prio_activity) {
                cl->prio_activity = 1 << (cl->un.leaf.aprio = cl->un.leaf.prio);
@@ -542,7 +542,7 @@ static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
  */
 static inline void htb_deactivate(struct htb_sched *q, struct htb_class *cl)
 {
-       BUG_TRAP(cl->prio_activity);
+       WARN_ON(!cl->prio_activity);
 
        htb_deactivate_prios(q, cl);
        cl->prio_activity = 0;
@@ -757,7 +757,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                u32 *pid;
        } stk[TC_HTB_MAXDEPTH], *sp = stk;
 
-       BUG_TRAP(tree->rb_node);
+       WARN_ON(!tree->rb_node);
        sp->root = tree->rb_node;
        sp->pptr = pptr;
        sp->pid = pid;
@@ -777,7 +777,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                                *sp->pptr = (*sp->pptr)->rb_left;
                        if (sp > stk) {
                                sp--;
-                               BUG_TRAP(*sp->pptr);
+                               WARN_ON(!*sp->pptr);
                                if (!*sp->pptr)
                                        return NULL;
                                htb_next_rb_node(sp->pptr);
@@ -792,7 +792,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                        sp->pid = cl->un.inner.last_ptr_id + prio;
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
        return NULL;
 }
 
@@ -810,7 +810,7 @@ static struct sk_buff *htb_dequeue_tree(struct htb_sched *q, int prio,
 
        do {
 next:
-               BUG_TRAP(cl);
+               WARN_ON(!cl);
                if (!cl)
                        return NULL;
 
@@ -1185,7 +1185,7 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
 {
        struct htb_class *parent = cl->parent;
 
-       BUG_TRAP(!cl->level && cl->un.leaf.q && !cl->prio_activity);
+       WARN_ON(cl->level || !cl->un.leaf.q || cl->prio_activity);
 
        if (parent->cmode != HTB_CAN_SEND)
                htb_safe_rb_erase(&parent->pq_node, q->wait_pq + parent->level);
@@ -1205,7 +1205,7 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
 static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
 {
        if (!cl->level) {
-               BUG_TRAP(cl->un.leaf.q);
+               WARN_ON(!cl->un.leaf.q);
                qdisc_destroy(cl->un.leaf.q);
        }
        gen_kill_estimator(&cl->bstats, &cl->rate_est);
index 73f53844ce976128f35a59663d2a9b3f13ed254f..8589da666568b50fc47ac1a2c280a0a96c92a5f3 100644 (file)
@@ -536,14 +536,7 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 
        opt.limit = q->limit;
        opt.divisor = SFQ_HASH_DIVISOR;
-       opt.flows = 0;
-       if (q->tail != SFQ_DEPTH) {
-               unsigned int i;
-
-               for (i = 0; i < SFQ_HASH_DIVISOR; i++)
-                       if (q->ht[i] != SFQ_DEPTH)
-                               opt.flows++;
-       }
+       opt.flows = q->limit;
 
        NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 
index ec2a0a33fd780e8c8185c74cbbcf453dbebeb99a..8472b8b349c464ac8a900d6b23241ce59efc5397 100644 (file)
@@ -464,7 +464,7 @@ static void sctp_association_destroy(struct sctp_association *asoc)
                spin_unlock_bh(&sctp_assocs_id_lock);
        }
 
-       BUG_TRAP(!atomic_read(&asoc->rmem_alloc));
+       WARN_ON(atomic_read(&asoc->rmem_alloc));
 
        if (asoc->base.malloced) {
                kfree(asoc);
index 63ada437fc2f0196e42fb349ee552a54a25ecc7f..972201cd5fa76791be3d558252d3efb5b009f92e 100644 (file)
 #include <linux/if_tr.h>
 #endif
 
-static struct list_head *
+static struct ctl_table_set *
 net_ctl_header_lookup(struct ctl_table_root *root, struct nsproxy *namespaces)
 {
-       return &namespaces->net_ns->sysctl_table_headers;
+       return &namespaces->net_ns->sysctls;
+}
+
+static int is_seen(struct ctl_table_set *set)
+{
+       return &current->nsproxy->net_ns->sysctls == set;
 }
 
 /* Return standard mode bits for table entry. */
@@ -53,13 +58,6 @@ static struct ctl_table_root net_sysctl_root = {
        .permissions = net_ctl_permissions,
 };
 
-static LIST_HEAD(net_sysctl_ro_tables);
-static struct list_head *net_ctl_ro_header_lookup(struct ctl_table_root *root,
-               struct nsproxy *namespaces)
-{
-       return &net_sysctl_ro_tables;
-}
-
 static int net_ctl_ro_header_perms(struct ctl_table_root *root,
                struct nsproxy *namespaces, struct ctl_table *table)
 {
@@ -70,19 +68,20 @@ static int net_ctl_ro_header_perms(struct ctl_table_root *root,
 }
 
 static struct ctl_table_root net_sysctl_ro_root = {
-       .lookup = net_ctl_ro_header_lookup,
        .permissions = net_ctl_ro_header_perms,
 };
 
 static int sysctl_net_init(struct net *net)
 {
-       INIT_LIST_HEAD(&net->sysctl_table_headers);
+       setup_sysctl_set(&net->sysctls,
+                        &net_sysctl_ro_root.default_set,
+                        is_seen);
        return 0;
 }
 
 static void sysctl_net_exit(struct net *net)
 {
-       WARN_ON(!list_empty(&net->sysctl_table_headers));
+       WARN_ON(!list_empty(&net->sysctls.list));
        return;
 }
 
@@ -98,6 +97,7 @@ static __init int sysctl_init(void)
        if (ret)
                goto out;
        register_sysctl_root(&net_sysctl_root);
+       setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL, NULL);
        register_sysctl_root(&net_sysctl_ro_root);
 out:
        return ret;
index 70ceb1604ad83278359dc30b313dd43b5ac481c4..015606b54d9b28325aad62a71ea4fc160b686cd8 100644 (file)
@@ -227,7 +227,7 @@ static void __unix_remove_socket(struct sock *sk)
 
 static void __unix_insert_socket(struct hlist_head *list, struct sock *sk)
 {
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        sk_add_node(sk, list);
 }
 
@@ -350,9 +350,9 @@ static void unix_sock_destructor(struct sock *sk)
 
        skb_queue_purge(&sk->sk_receive_queue);
 
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(sk_unhashed(sk));
-       BUG_TRAP(!sk->sk_socket);
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(!sk_unhashed(sk));
+       WARN_ON(sk->sk_socket);
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk("Attempt to release alive unix socket: %p\n", sk);
                return;
@@ -603,7 +603,7 @@ static struct sock * unix_create1(struct net *net, struct socket *sock)
        u->dentry = NULL;
        u->mnt    = NULL;
        spin_lock_init(&u->lock);
-       atomic_set(&u->inflight, 0);
+       atomic_long_set(&u->inflight, 0);
        INIT_LIST_HEAD(&u->link);
        mutex_init(&u->readlock); /* single task reading lock */
        init_waitqueue_head(&u->peer_wait);
index ebdff3d877a1e3201f69af1ae60e92d58df7177a..2a27b84f740bc836a8c4d3debb5031209bf8441d 100644 (file)
@@ -127,7 +127,7 @@ void unix_inflight(struct file *fp)
        if(s) {
                struct unix_sock *u = unix_sk(s);
                spin_lock(&unix_gc_lock);
-               if (atomic_inc_return(&u->inflight) == 1) {
+               if (atomic_long_inc_return(&u->inflight) == 1) {
                        BUG_ON(!list_empty(&u->link));
                        list_add_tail(&u->link, &gc_inflight_list);
                } else {
@@ -145,7 +145,7 @@ void unix_notinflight(struct file *fp)
                struct unix_sock *u = unix_sk(s);
                spin_lock(&unix_gc_lock);
                BUG_ON(list_empty(&u->link));
-               if (atomic_dec_and_test(&u->inflight))
+               if (atomic_long_dec_and_test(&u->inflight))
                        list_del_init(&u->link);
                unix_tot_inflight--;
                spin_unlock(&unix_gc_lock);
@@ -237,17 +237,17 @@ static void scan_children(struct sock *x, void (*func)(struct unix_sock *),
 
 static void dec_inflight(struct unix_sock *usk)
 {
-       atomic_dec(&usk->inflight);
+       atomic_long_dec(&usk->inflight);
 }
 
 static void inc_inflight(struct unix_sock *usk)
 {
-       atomic_inc(&usk->inflight);
+       atomic_long_inc(&usk->inflight);
 }
 
 static void inc_inflight_move_tail(struct unix_sock *u)
 {
-       atomic_inc(&u->inflight);
+       atomic_long_inc(&u->inflight);
        /*
         * If this is still a candidate, move it to the end of the
         * list, so that it's checked even if it was already passed
@@ -288,11 +288,11 @@ void unix_gc(void)
         * before the detach without atomicity guarantees.
         */
        list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
-               int total_refs;
-               int inflight_refs;
+               long total_refs;
+               long inflight_refs;
 
                total_refs = file_count(u->sk.sk_socket->file);
-               inflight_refs = atomic_read(&u->inflight);
+               inflight_refs = atomic_long_read(&u->inflight);
 
                BUG_ON(inflight_refs < 1);
                BUG_ON(total_refs < inflight_refs);
@@ -324,7 +324,7 @@ void unix_gc(void)
                /* Move cursor to after the current position. */
                list_move(&cursor, &u->link);
 
-               if (atomic_read(&u->inflight) > 0) {
+               if (atomic_long_read(&u->inflight) > 0) {
                        list_move_tail(&u->link, &gc_inflight_list);
                        u->gc_candidate = 0;
                        scan_children(&u->sk, inc_inflight_move_tail, NULL);
index 23a2cc04b8cdb5f4a9f215643bfc1df130800322..96036cf2216d09c7dea7907c31c73c3b42f5b7cf 100644 (file)
@@ -718,7 +718,7 @@ int skb_icv_walk(const struct sk_buff *skb, struct hash_desc *desc,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -748,7 +748,7 @@ int skb_icv_walk(const struct sk_buff *skb, struct hash_desc *desc,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 800f669083fb4af63a745778993441981fccf937..c609a4b98e15f9b730701a15e9292fbaffd3000c 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/percpu.h>
-#include <linux/rtnetlink.h>
 #include <linux/smp.h>
 #include <linux/vmalloc.h>
 #include <net/ip.h>
@@ -251,7 +250,7 @@ static void ipcomp_free_tfms(struct crypto_comp **tfms)
                        break;
        }
 
-       BUG_TRAP(pos);
+       WARN_ON(!pos);
 
        if (--pos->users)
                return;
index 72fddafd891a0b3f3be603804e6c5727a56a5b3d..4c6914ef7d92df5830baba92363108e502217c3f 100644 (file)
@@ -538,7 +538,7 @@ EXPORT_SYMBOL(xfrm_state_alloc);
 
 void __xfrm_state_destroy(struct xfrm_state *x)
 {
-       BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
 
        spin_lock_bh(&xfrm_state_lock);
        list_del(&x->all);
index 53dae3eb3d1f0334fb0043759598e6117ab4b3ba..612dc13ddd85fcda58d327ea688746f887ad02db 100644 (file)
 # ==========================================================================
 # Installing headers
 #
-# header-y files will be installed verbatim
-# unifdef-y are the files where unifdef will be run before installing files
-# objhdr-y are generated files that will be installed verbatim
+# header-y  - list files to be installed. They are preprocessed
+#             to remove __KERNEL__ section of the file
+# unifdef-y - Same as header-y. Obsolete
+# objhdr-y  - Same as header-y but for generated files
 #
 # ==========================================================================
 
-UNIFDEF := scripts/unifdef -U__KERNEL__
-
-# Eliminate the contents of (and inclusions of) compiler.h
-HDRSED  := sed         -e "s/ inline / __inline__ /g" \
-               -e "s/[[:space:]]__user[[:space:]]\{1,\}/ /g" \
-               -e "s/(__user[[:space:]]\{1,\}/ (/g" \
-               -e "s/[[:space:]]__force[[:space:]]\{1,\}/ /g" \
-               -e "s/(__force[[:space:]]\{1,\}/ (/g" \
-               -e "s/[[:space:]]__iomem[[:space:]]\{1,\}/ /g" \
-               -e "s/(__iomem[[:space:]]\{1,\}/ (/g" \
-               -e "s/[[:space:]]__attribute_const__[[:space:]]\{1,\}/\ /g" \
-               -e "s/[[:space:]]__attribute_const__$$//" \
-               -e "/^\#include <linux\/compiler.h>/d"
-
+# called may set destination dir (when installing to asm/)
 _dst := $(if $(dst),$(dst),$(obj))
 
-ifeq (,$(patsubst include/asm/%,,$(obj)/))
-# For producing the generated stuff in include/asm for biarch builds, include
-# both sets of Kbuild files; we'll generate anything which is mentioned in
-# _either_ arch, and recurse into subdirectories which are mentioned in either
-# arch. Since some directories may exist in one but not the other, we must
-# use $(wildcard...). 
-GENASM := 1
-archasm           := $(subst include/asm,asm-$(ARCH),$(obj))
-altarchasm := $(subst include/asm,asm-$(ALTARCH),$(obj))
-KBUILDFILES := $(wildcard $(srctree)/include/$(archasm)/Kbuild $(srctree)/include/$(altarchasm)/Kbuild)
-else
-KBUILDFILES := $(srctree)/$(obj)/Kbuild
-endif
+kbuild-file := $(srctree)/$(obj)/Kbuild
+include $(kbuild-file)
 
-include $(KBUILDFILES)
+include scripts/Kbuild.include
 
-include scripts/Kbuild.include 
+install       := $(INSTALL_HDR_PATH)/$(_dst)
 
-# If this is include/asm-$(ARCH) and there's no $(ALTARCH), then
-# override $(_dst) so that we install to include/asm directly.
-# Unless $(BIASMDIR) is set, in which case we're probably doing
-# a 'headers_install_all' build and we should keep the -$(ARCH)
-# in the directory name.
-ifeq ($(obj)$(ALTARCH),include/asm-$(ARCH)$(BIASMDIR))
-     _dst := include/asm
-endif
+header-y      := $(sort $(header-y) $(unifdef-y))
+subdirs       := $(patsubst %/,%,$(filter %/, $(header-y)))
+header-y      := $(filter-out %/, $(header-y))
 
-header-y       := $(sort $(header-y))
-unifdef-y      := $(sort $(unifdef-y))
-subdir-y       := $(patsubst %/,%,$(filter %/, $(header-y)))
-header-y       := $(filter-out %/, $(header-y))
-header-y       := $(filter-out $(unifdef-y),$(header-y))
+# files used to track state of install/check
+install-file  := $(install)/.install
+check-file    := $(install)/.check
 
-# stamp files for header checks
-check-y                := $(patsubst %,.check.%,$(header-y) $(unifdef-y) $(objhdr-y))
+# all headers files for this dir
+all-files     := $(header-y) $(objhdr-y)
+input-files   := $(addprefix $(srctree)/$(obj)/,$(header-y)) \
+                 $(addprefix $(objtree)/$(obj)/,$(objhdr-y))
+output-files  := $(addprefix $(install)/, $(all-files))
 
 # Work out what needs to be removed
-oldheaders     := $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/%,%,$(wildcard $(INSTALL_HDR_PATH)/$(_dst)/*.h))
-unwanted       := $(filter-out $(header-y) $(unifdef-y) $(objhdr-y),$(oldheaders))
+oldheaders    := $(patsubst $(install)/%,%,$(wildcard $(install)/*.h))
+unwanted      := $(filter-out $(all-files),$(oldheaders))
 
-oldcheckstamps := $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/%,%,$(wildcard $(INSTALL_HDR_PATH)/$(_dst)/.check.*.h))
-unwanted       += $(filter-out $(check-y),$(oldcheckstamps))
+# Prefix unwanted with full paths to $(INSTALL_HDR_PATH)
+unwanted-file := $(addprefix $(install)/, $(unwanted))
 
-# Prefix them all with full paths to $(INSTALL_HDR_PATH)
-header-y       := $(patsubst %,$(INSTALL_HDR_PATH)/$(_dst)/%,$(header-y))
-unifdef-y      := $(patsubst %,$(INSTALL_HDR_PATH)/$(_dst)/%,$(unifdef-y))
-objhdr-y       := $(patsubst %,$(INSTALL_HDR_PATH)/$(_dst)/%,$(objhdr-y))
-check-y        := $(patsubst %,$(INSTALL_HDR_PATH)/$(_dst)/%,$(check-y))
+printdir = $(patsubst $(INSTALL_HDR_PATH)/%/,%,$(dir $@))
 
+quiet_cmd_install = INSTALL $(printdir) ($(words $(all-files))\
+                            file$(if $(word 2, $(all-files)),s))
+      cmd_install = \
+        $(PERL) $< $(srctree)/$(obj) $(install) $(SRCARCH) $(header-y); \
+        $(PERL) $< $(objtree)/$(obj) $(install) $(SRCARCH) $(objhdr-y); \
+        touch $@
 
-ifdef ALTARCH
-ifeq ($(obj),include/asm-$(ARCH))
-altarch-y      := altarch-dir
-endif
-endif
+quiet_cmd_remove = REMOVE  $(unwanted)
+      cmd_remove = rm -f $(unwanted-file)
 
-# Make the definitions visible for recursive make invocations
-export ALTARCH
-export ARCHDEF
-export ALTARCHDEF
-
-quiet_cmd_o_hdr_install   = INSTALL $(patsubst $(INSTALL_HDR_PATH)/%,%,$@)
-      cmd_o_hdr_install   = cp $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/%,$(objtree)/$(obj)/%,$@) \
-                           $(INSTALL_HDR_PATH)/$(_dst)
-
-quiet_cmd_headers_install = INSTALL $(patsubst $(INSTALL_HDR_PATH)/%,%,$@)
-      cmd_headers_install = $(HDRSED) $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/%,$(srctree)/$(obj)/%,$@) \
-                           > $@
-
-quiet_cmd_unifdef        = UNIFDEF $(patsubst $(INSTALL_HDR_PATH)/%,%,$@)
-      cmd_unifdef        = $(UNIFDEF) $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/%,$(srctree)/$(obj)/%,$@) \
-                                  | $(HDRSED) > $@ || :
-
-quiet_cmd_check                  = CHECK   $(patsubst $(INSTALL_HDR_PATH)/$(_dst)/.check.%,$(_dst)/%,$@)
-      cmd_check                  = $(CONFIG_SHELL) $(srctree)/scripts/hdrcheck.sh \
-                              $(INSTALL_HDR_PATH)/include $(subst /.check.,/,$@) $@
-
-quiet_cmd_remove         = REMOVE  $(_dst)/$@
-      cmd_remove         = rm -f $(INSTALL_HDR_PATH)/$(_dst)/$@
-
-quiet_cmd_mkdir                  = MKDIR   $(patsubst $(INSTALL_HDR_PATH)/%,%,$@)
-      cmd_mkdir                  = mkdir -p $@
-
-quiet_cmd_gen            = GEN     $(patsubst $(INSTALL_HDR_PATH)/%,%,$@)
-      cmd_gen            = \
-FNAME=$(patsubst $(INSTALL_HDR_PATH)/$(_dst)/%,%,$@);                  \
-STUBDEF=__ASM_STUB_`echo $$FNAME | tr a-z.- A-Z__`;                    \
-(echo "/* File autogenerated by 'make headers_install' */" ;           \
-echo "\#ifndef $$STUBDEF" ;                                            \
-echo "\#define $$STUBDEF" ;                                            \
-echo "\# if $(ARCHDEF)" ;                                              \
-if [ -r $(subst /$(_dst)/,/include/$(archasm)/,$@) ]; then             \
-       echo "\#  include <$(archasm)/$$FNAME>" ;                       \
-else                                                                   \
-       echo "\#  error $(archasm)/$$FNAME does not exist in"           \
-                       "the $(ARCH) architecture" ;                    \
-fi ;                                                                   \
-echo "\# elif $(ALTARCHDEF)" ;                                         \
-if [ -r $(subst /$(_dst)/,/include/$(altarchasm)/,$@) ]; then          \
-       echo "\#  include <$(altarchasm)/$$FNAME>" ;                    \
-else                                                                   \
-       echo "\#  error $(altarchasm)/$$FNAME does not exist in"        \
-                       "the $(ALTARCH) architecture" ;                 \
-fi ;                                                                   \
-echo "\# else" ;                                                       \
-echo "\#  warning This machine appears to be"                          \
-                "neither $(ARCH) nor $(ALTARCH)." ;                    \
-echo "\# endif" ;                                                      \
-echo "\#endif /* $$STUBDEF */" ;                                       \
-) > $@
-
-.PHONY: __headersinst __headerscheck
-
-ifdef HDRCHECK
-__headerscheck: $(subdir-y) $(check-y)
-       @true
-
-$(check-y) : $(INSTALL_HDR_PATH)/$(_dst)/.check.%.h : $(INSTALL_HDR_PATH)/$(_dst)/%.h 
-       $(call cmd,check)
-
-# Other dependencies for $(check-y)
-include /dev/null $(wildcard $(check-y))
-
-# ... but leave $(check-y) as .PHONY for now until those deps are actually correct.
-.PHONY: $(check-y)
+quiet_cmd_check = CHECK   $(printdir) ($(words $(all-files)) files)
+      cmd_check = $(PERL) $< $(INSTALL_HDR_PATH)/include $(SRCARCH) \
+                  $(addprefix $(install)/, $(all-files));           \
+                 touch $@
 
-else
-# Rules for installing headers
-__headersinst: $(subdir-y) $(header-y) $(unifdef-y) $(altarch-y) $(objhdr-y)
-       @true
+PHONY += __headersinst __headerscheck
 
-$(objhdr-y) $(subdir-y) $(header-y) $(unifdef-y): | $(INSTALL_HDR_PATH)/$(_dst) $(unwanted)
-
-$(INSTALL_HDR_PATH)/$(_dst):
-       $(call cmd,mkdir)
-
-.PHONY: $(unwanted)
-$(unwanted):
-       $(call cmd,remove)
+ifndef HDRCHECK
+# Rules for installing headers
+__headersinst: $(subdirs) $(install-file)
+       @:
 
-ifdef GENASM
-$(objhdr-y) $(header-y) $(unifdef-y): $(KBUILDFILES)
-       $(call cmd,gen)
+targets += $(install-file)
+$(install-file): scripts/headers_install.pl $(input-files) FORCE
+       $(if $(unwanted),$(call cmd,remove),)
+       $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@)))
+       $(call if_changed,install)
 
 else
-$(objhdr-y) :          $(INSTALL_HDR_PATH)/$(_dst)/%.h: $(objtree)/$(obj)/%.h $(KBUILDFILES)
-       $(call cmd,o_hdr_install)
+__headerscheck: $(subdirs) $(check-file)
+       @:
 
-$(header-y) :          $(INSTALL_HDR_PATH)/$(_dst)/%.h: $(srctree)/$(obj)/%.h $(KBUILDFILES)
-       $(call cmd,headers_install)
+targets += $(check-file)
+$(check-file): scripts/headers_check.pl $(output-files) FORCE
+       $(call if_changed,check)
 
-$(unifdef-y) :         $(INSTALL_HDR_PATH)/$(_dst)/%.h: $(srctree)/$(obj)/%.h $(KBUILDFILES)
-       $(call cmd,unifdef)
-endif
 endif
 
-hdrinst := -rR -f $(srctree)/scripts/Makefile.headersinst obj
+# Recursion
+hdr-inst := -rR -f $(srctree)/scripts/Makefile.headersinst obj
+.PHONY: $(subdirs)
+$(subdirs):
+       $(Q)$(MAKE) $(hdr-inst)=$(obj)/$@ dst=$(_dst)/$@
 
-.PHONY: altarch-dir
-# All the files in the normal arch dir must be created first, since we test
-# for their existence.
-altarch-dir: $(subdir-y) $(header-y) $(unifdef-y) $(objhdr-y)
-       $(Q)$(MAKE) $(hdrinst)=include/asm-$(ALTARCH) dst=include/asm-$(ALTARCH)
-       $(Q)$(MAKE) $(hdrinst)=include/asm dst=include/asm$(BIASMDIR)
+targets := $(wildcard $(sort $(targets)))
+cmd_files := $(wildcard \
+             $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))
 
-# Recursion
-.PHONY: $(subdir-y)
-$(subdir-y):
-       $(Q)$(MAKE) $(hdrinst)=$(obj)/$@ dst=$(_dst)/$@ rel=../$(rel)
+ifneq ($(cmd_files),)
+       include $(cmd_files)
+endif
+
+.PHONY: $(PHONY)
+PHONY += FORCE
+FORCE: ;
diff --git a/scripts/diffconfig b/scripts/diffconfig
new file mode 100755 (executable)
index 0000000..b91f3e3
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+#
+# diffconfig - a tool to compare .config files.
+#
+# originally written in 2006 by Matt Mackall
+#  (at least, this was in his bloatwatch source code)
+# last worked on 2008 by Tim Bird
+#
+
+import sys, os
+
+def usage():
+    print """Usage: diffconfig [-h] [-m] [<config1> <config2>]
+
+Diffconfig is a simple utility for comparing two .config files.
+Using standard diff to compare .config files often includes extraneous and
+distracting information.  This utility produces sorted output with only the
+changes in configuration values between the two files.
+
+Added and removed items are shown with a leading plus or minus, respectively.
+Changed items show the old and new values on a single line.
+
+If -m is specified, then output will be in "merge" style, which has the
+changed and new values in kernel config option format.
+
+If no config files are specified, .config and .config.old are used.
+
+Example usage:
+ $ diffconfig .config config-with-some-changes
+-EXT2_FS_XATTR  n
+-EXT2_FS_XIP  n
+ CRAMFS  n -> y
+ EXT2_FS  y -> n
+ LOG_BUF_SHIFT  14 -> 16
+ PRINTK_TIME  n -> y
+"""
+    sys.exit(0)
+
+# returns a dictionary of name/value pairs for config items in the file
+def readconfig(config_file):
+    d = {}
+    for line in config_file:
+        line = line[:-1]
+        if line[:7] == "CONFIG_":
+            name, val = line[7:].split("=", 1)
+            d[name] = val
+        if line[-11:] == " is not set":
+            d[line[9:-11]] = "n"
+    return d
+
+def print_config(op, config, value, new_value):
+    global merge_style
+
+    if merge_style:
+        if new_value:
+            if new_value=="n":
+                print "# CONFIG_%s is not set" % config
+            else:
+                print "CONFIG_%s=%s" % (config, new_value)
+    else:
+        if op=="-":
+            print "-%s %s" % (config, value)
+        elif op=="+":
+            print "+%s %s" % (config, new_value)
+        else:
+            print " %s %s -> %s" % (config, value, new_value)
+
+def main():
+    global merge_style
+
+    # parse command line args
+    if ("-h" in sys.argv or "--help" in sys.argv):
+       usage()
+
+    merge_style = 0
+    if "-m" in sys.argv:
+        merge_style = 1
+        sys.argv.remove("-m")
+
+    argc = len(sys.argv)
+    if not (argc==1 or argc == 3):
+        print "Error: incorrect number of arguments or unrecognized option"
+        usage()
+
+    if argc == 1:
+        # if no filenames given, assume .config and .config.old
+        build_dir=""
+        if os.environ.has_key("KBUILD_OUTPUT"):
+            build_dir = os.environ["KBUILD_OUTPUT"]+"/"
+
+        configa_filename = build_dir + ".config.old"
+        configb_filename = build_dir + ".config"
+    else:
+        configa_filename = sys.argv[1]
+        configb_filename = sys.argv[2]
+
+    a = readconfig(file(configa_filename))
+    b = readconfig(file(configb_filename))
+
+    # print items in a but not b (accumulate, sort and print)
+    old = []
+    for config in a:
+        if config not in b:
+            old.append(config)
+    old.sort()
+    for config in old:
+        print_config("-", config, a[config], None)
+        del a[config]
+
+    # print items that changed (accumulate, sort, and print)
+    changed = []
+    for config in a:
+        if a[config] != b[config]:
+            changed.append(config)
+        else:
+            del b[config]
+    changed.sort()
+    for config in changed:
+        print_config("->", config, a[config], b[config])
+        del b[config]
+
+    # now print items in b but not in a
+    # (items from b that were in a were removed above)
+    new = b.keys()
+    new.sort()
+    for config in new:
+        print_config("+", config, None, b[config])
+
+main()
diff --git a/scripts/hdrcheck.sh b/scripts/hdrcheck.sh
deleted file mode 100755 (executable)
index 3159858..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-for FILE in `grep '^[ \t]*#[ \t]*include[ \t]*<' $2 | cut -f2 -d\< | cut -f1 -d\> | egrep ^linux\|^asm` ; do
-    if [ ! -r $1/$FILE ]; then
-       echo $2 requires $FILE, which does not exist in exported headers
-       exit 1
-    fi
-done
-# FIXME: List dependencies into $3
-touch $3
diff --git a/scripts/headers.sh b/scripts/headers.sh
new file mode 100755 (executable)
index 0000000..d33426f
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Run headers_$1 command for all suitable architectures
+
+# Stop on error
+set -e
+
+do_command()
+{
+       if [ -f ${srctree}/arch/$2/include/asm/Kbuild ]; then
+               make ARCH=$2 KBUILD_HEADERS=$1 headers_$1
+       elif [ -f ${srctree}/include/asm-$2/Kbuild ]; then
+               make ARCH=$2 KBUILD_HEADERS=$1 headers_$1
+       else
+               printf "Ignoring arch: %s\n" ${arch}
+       fi
+}
+
+# Do not try this architecture
+drop="generic um ppc sparc64 cris"
+
+archs=$(ls ${srctree}/arch)
+
+for arch in ${archs}; do
+       case ${arch} in
+       um)        # no userspace export
+               ;;
+       ppc)       # headers exported by powerpc
+               ;;
+       sparc64)   # headers exported by sparc
+               ;;
+       cris)      # headers export are known broken
+               ;;
+       *)
+               if [ -d ${srctree}/arch/${arch} ]; then
+                       do_command $1 ${arch}
+               fi
+               ;;
+       esac
+done
+
+
diff --git a/scripts/headers_check.pl b/scripts/headers_check.pl
new file mode 100644 (file)
index 0000000..15d53a6
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+#
+# headers_check.pl execute a number of trivial consistency checks
+#
+# Usage: headers_check.pl dir [files...]
+# dir:   dir to look for included files
+# arch:  architecture
+# files: list of files to check
+#
+# The script reads the supplied files line by line and:
+#
+# 1) for each include statement it checks if the
+#    included file actually exists.
+#    Only include files located in asm* and linux* are checked.
+#    The rest are assumed to be system include files.
+#
+# 2) TODO: check for leaked CONFIG_ symbols
+
+use strict;
+use warnings;
+
+my ($dir, $arch, @files) = @ARGV;
+
+my $ret = 0;
+my $line;
+my $lineno = 0;
+my $filename;
+
+foreach my $file (@files) {
+       $filename = $file;
+       open(my $fh, '<', "$filename") or die "$filename: $!\n";
+       $lineno = 0;
+       while ($line = <$fh>) {
+               $lineno++;
+               check_include();
+       }
+       close $fh;
+}
+exit $ret;
+
+sub check_include
+{
+       if ($line =~ m/^\s*#\s*include\s+<((asm|linux).*)>/) {
+               my $inc = $1;
+               my $found;
+               $found = stat($dir . "/" . $inc);
+               if (!$found) {
+                       $inc =~ s#asm/#asm-$arch/#;
+                       $found = stat($dir . "/" . $inc);
+               }
+               if (!$found) {
+                       printf STDERR "$filename:$lineno: included file '$inc' is not exported\n";
+                       $ret = 1;
+               }
+       }
+}
diff --git a/scripts/headers_install.pl b/scripts/headers_install.pl
new file mode 100644 (file)
index 0000000..68591cd
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+#
+# headers_install prepare the listed header files for use in
+# user space and copy the files to their destination.
+#
+# Usage: headers_install.pl readdir installdir arch [files...]
+# readdir:    dir to open files
+# installdir: dir to install the files
+# arch:       current architecture
+#             arch is used to force a reinstallation when the arch
+#             changes because kbuild then detect a command line change.
+# files:      list of files to check
+#
+# Step in preparation for users space:
+# 1) Drop all use of compiler.h definitions
+# 2) Drop include of compiler.h
+# 3) Drop all sections defined out by __KERNEL__ (using unifdef)
+
+use strict;
+use warnings;
+
+my ($readdir, $installdir, $arch, @files) = @ARGV;
+
+my $unifdef = "scripts/unifdef -U__KERNEL__";
+
+foreach my $file (@files) {
+       my $tmpfile = "$installdir/$file.tmp";
+       open(my $infile, '<', "$readdir/$file")
+               or die "$readdir/$file: $!\n";
+       open(my $outfile, '>', "$tmpfile") or die "$tmpfile: $!\n";
+       while (my $line = <$infile>) {
+               $line =~ s/([\s(])__user\s/$1/g;
+               $line =~ s/([\s(])__force\s/$1/g;
+               $line =~ s/([\s(])__iomem\s/$1/g;
+               $line =~ s/\s__attribute_const__\s/ /g;
+               $line =~ s/\s__attribute_const__$//g;
+               $line =~ s/^#include <linux\/compiler.h>//;
+               printf $outfile "%s", $line;
+       }
+       close $outfile;
+       close $infile;
+       system $unifdef . " $tmpfile > $installdir/$file";
+       unlink $tmpfile;
+}
+exit 0;
index fda63136ae6809512c6d84ab5c11d1497eee3bc5..9fba838c7069c4cb71d3fbd9c109e0f951c5e340 100644 (file)
@@ -76,7 +76,6 @@ static void check_stdin(void)
 static int conf_askvalue(struct symbol *sym, const char *def)
 {
        enum symbol_type type = sym_get_type(sym);
-       tristate val;
 
        if (!sym_has_value(sym))
                printf(_("(NEW) "));
@@ -92,15 +91,6 @@ static int conf_askvalue(struct symbol *sym, const char *def)
        }
 
        switch (input_mode) {
-       case set_no:
-       case set_mod:
-       case set_yes:
-       case set_random:
-               if (sym_has_value(sym)) {
-                       printf("%s\n", def);
-                       return 0;
-               }
-               break;
        case ask_new:
        case ask_silent:
                if (sym_has_value(sym)) {
@@ -112,9 +102,6 @@ static int conf_askvalue(struct symbol *sym, const char *def)
                fflush(stdout);
                fgets(line, 128, stdin);
                return 1;
-       case set_default:
-               printf("%s\n", def);
-               return 1;
        default:
                break;
        }
@@ -128,52 +115,6 @@ static int conf_askvalue(struct symbol *sym, const char *def)
        default:
                ;
        }
-       switch (input_mode) {
-       case set_yes:
-               if (sym_tristate_within_range(sym, yes)) {
-                       line[0] = 'y';
-                       line[1] = '\n';
-                       line[2] = 0;
-                       break;
-               }
-       case set_mod:
-               if (type == S_TRISTATE) {
-                       if (sym_tristate_within_range(sym, mod)) {
-                               line[0] = 'm';
-                               line[1] = '\n';
-                               line[2] = 0;
-                               break;
-                       }
-               } else {
-                       if (sym_tristate_within_range(sym, yes)) {
-                               line[0] = 'y';
-                               line[1] = '\n';
-                               line[2] = 0;
-                               break;
-                       }
-               }
-       case set_no:
-               if (sym_tristate_within_range(sym, no)) {
-                       line[0] = 'n';
-                       line[1] = '\n';
-                       line[2] = 0;
-                       break;
-               }
-       case set_random:
-               do {
-                       val = (tristate)(rand() % 3);
-               } while (!sym_tristate_within_range(sym, val));
-               switch (val) {
-               case no: line[0] = 'n'; break;
-               case mod: line[0] = 'm'; break;
-               case yes: line[0] = 'y'; break;
-               }
-               line[1] = '\n';
-               line[2] = 0;
-               break;
-       default:
-               break;
-       }
        printf("%s", line);
        return 1;
 }
@@ -374,15 +315,7 @@ static int conf_choice(struct menu *menu)
                        else
                                continue;
                        break;
-               case set_random:
-                       if (is_new)
-                               def = (rand() % cnt) + 1;
-               case set_default:
-               case set_yes:
-               case set_mod:
-               case set_no:
-                       cnt = def;
-                       printf("%d\n", cnt);
+               default:
                        break;
                }
 
@@ -494,6 +427,43 @@ static void check_conf(struct menu *menu)
                check_conf(child);
 }
 
+static void conf_do_update(void)
+{
+       /* Update until a loop caused no more changes */
+       do {
+               conf_cnt = 0;
+               check_conf(&rootmenu);
+       } while (conf_cnt);
+}
+
+static int conf_silent_update(void)
+{
+       const char *name;
+
+       if (conf_get_changed()) {
+               name = getenv("KCONFIG_NOSILENTUPDATE");
+               if (name && *name) {
+                       fprintf(stderr,
+                       _("\n*** Kernel configuration requires explicit update.\n\n"));
+                       return 1;
+               }
+               conf_do_update();
+       }
+       return 0;
+}
+
+static int conf_update(void)
+{
+       rootEntry = &rootmenu;
+       conf(&rootmenu);
+       if (input_mode == ask_all) {
+               input_mode = ask_silent;
+               valid_stdin = 1;
+       }
+       conf_do_update();
+       return 0;
+}
+
 int main(int ac, char **av)
 {
        int opt;
@@ -599,36 +569,43 @@ int main(int ac, char **av)
        default:
                break;
        }
+       switch (input_mode) {
+       case set_no:
+               conf_set_all_new_symbols(def_no);
+               break;
+       case set_yes:
+               conf_set_all_new_symbols(def_yes);
+               break;
+       case set_mod:
+               conf_set_all_new_symbols(def_mod);
+               break;
+       case set_random:
+               conf_set_all_new_symbols(def_random);
+               break;
+       case set_default:
+               conf_set_all_new_symbols(def_default);
+               break;
+       case ask_silent:
+       case ask_new:
+               if (conf_silent_update())
+                       exit(1);
+               break;
+       case ask_all:
+               if (conf_update())
+                       exit(1);
+               break;
+       }
 
-       if (input_mode != ask_silent) {
-               rootEntry = &rootmenu;
-               conf(&rootmenu);
-               if (input_mode == ask_all) {
-                       input_mode = ask_silent;
-                       valid_stdin = 1;
-               }
-       } else if (conf_get_changed()) {
-               name = getenv("KCONFIG_NOSILENTUPDATE");
-               if (name && *name) {
-                       fprintf(stderr, _("\n*** Kernel configuration requires explicit update.\n\n"));
-                       return 1;
-               }
-       } else
-               goto skip_check;
-
-       do {
-               conf_cnt = 0;
-               check_conf(&rootmenu);
-       } while (conf_cnt);
-       if (conf_write(NULL)) {
+       if (conf_get_changed() && conf_write(NULL)) {
                fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n"));
-               return 1;
+               exit(1);
        }
-skip_check:
+       /* ask_silent is used during the build so we shall update autoconf.
+        * All other commands are only used to generate a config.
+        */
        if (input_mode == ask_silent && conf_write_autoconf()) {
                fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n"));
                return 1;
        }
-
        return 0;
 }
index ee5fe943d58db0eda374b91a8181e5991d8cf23b..07597611cc504deca5098cdd7638ace892cd4441 100644 (file)
@@ -812,3 +812,73 @@ void conf_set_changed_callback(void (*fn)(void))
 {
        conf_changed_callback = fn;
 }
+
+
+void conf_set_all_new_symbols(enum conf_def_mode mode)
+{
+       struct symbol *sym, *csym;
+       struct property *prop;
+       struct expr *e;
+       int i, cnt, def;
+
+       for_all_symbols(i, sym) {
+               if (sym_has_value(sym))
+                       continue;
+               switch (sym_get_type(sym)) {
+               case S_BOOLEAN:
+               case S_TRISTATE:
+                       switch (mode) {
+                       case def_yes:
+                               sym->def[S_DEF_USER].tri = yes;
+                               break;
+                       case def_mod:
+                               sym->def[S_DEF_USER].tri = mod;
+                               break;
+                       case def_no:
+                               sym->def[S_DEF_USER].tri = no;
+                               break;
+                       case def_random:
+                               sym->def[S_DEF_USER].tri = (tristate)(rand() % 3);
+                               break;
+                       default:
+                               continue;
+                       }
+                       if (!sym_is_choice(sym) || mode != def_random)
+                               sym->flags |= SYMBOL_DEF_USER;
+                       break;
+               default:
+                       break;
+               }
+
+       }
+
+       if (modules_sym)
+               sym_calc_value(modules_sym);
+
+       if (mode != def_random)
+               return;
+
+       for_all_symbols(i, csym) {
+               if (sym_has_value(csym) || !sym_is_choice(csym))
+                       continue;
+
+               sym_calc_value(csym);
+               prop = sym_get_choice_prop(csym);
+               def = -1;
+               while (1) {
+                       cnt = 0;
+                       expr_list_for_each_sym(prop->expr, e, sym) {
+                               if (sym->visible == no)
+                                       continue;
+                               if (def == cnt++) {
+                                       csym->def[S_DEF_USER].val = sym;
+                                       break;
+                               }
+                       }
+                       if (def >= 0 || cnt < 2)
+                               break;
+                       def = (rand() % cnt) + 1;
+               }
+               csym->flags |= SYMBOL_DEF_USER;
+       }
+}
index 96521cb087ec388587d4737c375c42862f4b5a24..4a9af6f7886b8f1362fc593009f3e36374a5e3bd 100644 (file)
@@ -42,6 +42,14 @@ extern "C" {
 #define TF_PARAM       0x0002
 #define TF_OPTION      0x0004
 
+enum conf_def_mode {
+       def_default,
+       def_yes,
+       def_mod,
+       def_no,
+       def_random
+};
+
 #define T_OPT_MODULES          1
 #define T_OPT_DEFCONFIG_LIST   2
 #define T_OPT_ENV              3
@@ -69,6 +77,7 @@ const char *conf_get_configname(void);
 char *conf_get_default_confname(void);
 void sym_set_change_count(int count);
 void sym_add_change_count(int count);
+void conf_set_all_new_symbols(enum conf_def_mode mode);
 
 /* kconfig_load.c */
 void kconfig_load(void);
index 88e3934a8b8c86f6e5962ac64acb36ab7d0c08dd..d8f77e26081c230a3bc489f5c773950cab3b10d3 100755 (executable)
@@ -1643,6 +1643,7 @@ sub dump_function($$) {
     $prototype =~ s/^__always_inline +//;
     $prototype =~ s/^noinline +//;
     $prototype =~ s/__devinit +//;
+    $prototype =~ s/__init +//;
     $prototype =~ s/^#define\s+//; #ak added
     $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//;
 
index 1c1bdaf7348a61fc3606165cb1cc5c6725831e05..83b75126c9f7b5c57a92c8f996dc94e6c61d6680 100755 (executable)
@@ -12,7 +12,9 @@ cd "${1:-.}" || usage
 if head=`git rev-parse --verify HEAD 2>/dev/null`; then
        # Do we have an untagged version?
        if git name-rev --tags HEAD | grep -E '^HEAD[[:space:]]+(.*~[0-9]*|undefined)$' > /dev/null; then
-               git describe | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
+               if tag=`git describe 2>/dev/null`; then
+                       echo $tag | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
+               fi
        fi
 
        # Are there uncommitted changes?
index 5b01c0b02422b28a1809ba7be383e1b93e76159f..63d10da515a5b01e9cf36a2f7b2a49e82b10d199 100644 (file)
@@ -211,8 +211,7 @@ static int cap_inode_follow_link(struct dentry *dentry,
        return 0;
 }
 
-static int cap_inode_permission(struct inode *inode, int mask,
-                               struct nameidata *nd)
+static int cap_inode_permission(struct inode *inode, int mask)
 {
        return 0;
 }
index 59f23b5918b38c01028263798cebd1ca304b171d..ff7068727757639149501937043766d2199a8228 100644 (file)
@@ -429,11 +429,11 @@ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
        return security_ops->inode_follow_link(dentry, nd);
 }
 
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+int security_inode_permission(struct inode *inode, int mask)
 {
        if (unlikely(IS_PRIVATE(inode)))
                return 0;
-       return security_ops->inode_permission(inode, mask, nd);
+       return security_ops->inode_permission(inode, mask);
 }
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
@@ -442,6 +442,7 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
                return 0;
        return security_ops->inode_setattr(dentry, attr);
 }
+EXPORT_SYMBOL_GPL(security_inode_setattr);
 
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 {
index 3481cde5bf15f018fac6d83cd100968d6b701c5b..40d06c533f89fb574efd68507142fbe9b770d9f7 100644 (file)
@@ -2624,12 +2624,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na
        return dentry_has_perm(current, NULL, dentry, FILE__READ);
 }
 
-static int selinux_inode_permission(struct inode *inode, int mask,
-                                   struct nameidata *nd)
+static int selinux_inode_permission(struct inode *inode, int mask)
 {
        int rc;
 
-       rc = secondary_ops->inode_permission(inode, mask, nd);
+       rc = secondary_ops->inode_permission(inode, mask);
        if (rc)
                return rc;
 
@@ -5654,27 +5653,20 @@ static struct nf_hook_ops selinux_ipv6_ops[] = {
 static int __init selinux_nf_ip_init(void)
 {
        int err = 0;
-       u32 iter;
 
        if (!selinux_enabled)
                goto out;
 
        printk(KERN_DEBUG "SELinux:  Registering netfilter hooks\n");
 
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
-               err = nf_register_hook(&selinux_ipv4_ops[iter]);
-               if (err)
-                       panic("SELinux: nf_register_hook for IPv4: error %d\n",
-                             err);
-       }
+       err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+       if (err)
+               panic("SELinux: nf_register_hooks for IPv4: error %d\n", err);
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
-               err = nf_register_hook(&selinux_ipv6_ops[iter]);
-               if (err)
-                       panic("SELinux: nf_register_hook for IPv6: error %d\n",
-                             err);
-       }
+       err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+       if (err)
+               panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
 #endif /* IPV6 */
 
 out:
@@ -5686,15 +5678,11 @@ __initcall(selinux_nf_ip_init);
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 static void selinux_nf_ip_exit(void)
 {
-       u32 iter;
-
        printk(KERN_DEBUG "SELinux:  Unregistering netfilter hooks\n");
 
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
-               nf_unregister_hook(&selinux_ipv4_ops[iter]);
+       nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
-               nf_unregister_hook(&selinux_ipv6_ops[iter]);
+       nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
 #endif /* IPV6 */
 }
 #endif
index ee5a51cbc5ebdd537997978760093f11994a59cc..1b40e558f98329a718a76c7a4b4458380a63057e 100644 (file)
@@ -522,8 +522,7 @@ static int smack_inode_rename(struct inode *old_inode,
  *
  * Returns 0 if access is permitted, -EACCES otherwise
  */
-static int smack_inode_permission(struct inode *inode, int mask,
-                                 struct nameidata *nd)
+static int smack_inode_permission(struct inode *inode, int mask)
 {
        /*
         * No permission to check. Existence test. Yup, it's there.
index dbe63db4bfd654cfb656aee997d873353374c02b..4d4b8ddc26bac1711e51b772212497cdfe0f2ddf 100644 (file)
@@ -325,6 +325,7 @@ static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
 static int __devinit snd_card_cs4232_pnp(int dev, struct snd_card_cs4236 *acard,
                                         struct pnp_dev *pdev)
 {
+       acard->wss = pdev;
        if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
                return -EBUSY;
        cport[dev] = -1;
index 41c047e665ec7d62c6f85c25dbf45ce1441e901f..0797ca441a372d719de70bad9c7df9b96db458f6 100644 (file)
@@ -68,7 +68,9 @@ MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
 static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
 static char *id = SNDRV_DEFAULT_STR1;          /* ID for this card */
 //static int enable = SNDRV_DEFAULT_ENABLE1;   /* Enable this card */
+#ifdef CONFIG_PNP
 static int isapnp = 1;                 /* Enable ISA PnP detection */
+#endif
 static long port = SNDRV_DEFAULT_PORT1;        /* 0x530,0xe80,0xf40,0x604 */
 static long mpu_port = SNDRV_DEFAULT_PORT1;    /* 0x300,0x310,0x320,0x330 */
 static long fm_port = SNDRV_DEFAULT_PORT1;     /* 0x388 */
@@ -85,8 +87,10 @@ module_param(id, charp, 0444);
 MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
 //module_param(enable, bool, 0444);
 //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
+#ifdef CONFIG_PNP
 module_param(isapnp, bool, 0444);
 MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
+#endif
 module_param(port, long, 0444);
 MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
 module_param(mpu_port, long, 0444);
@@ -688,7 +692,7 @@ static void snd_card_opti9xx_free(struct snd_card *card)
        if (chip) {
 #ifdef OPTi93X
                struct snd_cs4231 *codec = chip->codec;
-               if (codec->irq > 0) {
+               if (codec && codec->irq > 0) {
                        disable_irq(codec->irq);
                        free_irq(codec->irq, codec);
                }
index 07364c00768a4dd90f8eceeb338d899955a17685..8c49a00a5e3919d835b3f5d3a6fe0569eafda80d 100644 (file)
@@ -161,6 +161,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x50534304, 0xffffffff, "UCB1400",           patch_ucb1400,  NULL },
 { 0x53494c20, 0xffffffe0, "Si3036,8",          mpatch_si3036,  mpatch_si3036, AC97_MODEM_PATCH },
 { 0x54524102, 0xffffffff, "TR28022",           NULL,           NULL },
+{ 0x54524103, 0xffffffff, "TR28023",           NULL,           NULL },
 { 0x54524106, 0xffffffff, "TR28026",           NULL,           NULL },
 { 0x54524108, 0xffffffff, "TR28028",           patch_tritech_tr28028,  NULL }, // added by xin jin [07/09/99]
 { 0x54524123, 0xffffffff, "TR28602",           NULL,           NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
@@ -169,7 +170,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x56494170, 0xffffffff, "VIA1617A",          patch_vt1617a,  NULL }, // modified VT1616 with S/PDIF
 { 0x56494182, 0xffffffff, "VIA1618",           NULL,           NULL },
 { 0x57454301, 0xffffffff, "W83971D",           NULL,           NULL },
-{ 0x574d4c00, 0xffffffff, "WM9701A",           NULL,           NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701,WM9701A",    NULL,           NULL },
 { 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL},
 { 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q",   patch_wolfson04, NULL},
 { 0x574d4C05, 0xffffffff, "WM9705,WM9710",     patch_wolfson05, NULL},
index 0746e9ccc20b1572ec1cae300b0178c1ad6f90e9..f4fbc795ee8140853305d4297e0818a48e01dd87 100644 (file)
@@ -3381,8 +3381,8 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
 }
 
 /* create a virtual master control and add slaves */
-int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
-                        const unsigned int *tlv, const char **slaves)
+static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+                               const unsigned int *tlv, const char **slaves)
 {
        struct snd_kcontrol *kctl;
        const char **s;
index 7e3e8942d0733f01fa3b9d5363878f2f9bf2f665..974e05122f005f6d23d70fb1f593a94c017b0081 100644 (file)
@@ -94,7 +94,7 @@ enum azf_freq_t {
   AZF_FREQ(48000),
   AZF_FREQ(66200),
 #undef AZF_FREQ
-} AZF_FREQUENCIES;
+};
 
 /** recording area (see also: playback bit flag definitions) **/
 #define IDX_IO_REC_FLAGS       0x20 /* ??, PU:0x0000 */
@@ -210,7 +210,7 @@ enum azf_freq_t {
 
 enum {
        AZF_GAME_LEGACY_IO_PORT = 0x200
-} AZF_GAME_CONFIGS;
+};
 
 #define IDX_GAME_LEGACY_COMPATIBLE     0x00
        /* in some operation mode, writing anything to this port
index fbf1124f7c79d1609f594fdb786e0ec0e0b676ac..9bf95367c882b4fbe80558f254aa2b45456f9646 100644 (file)
@@ -522,7 +522,7 @@ static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq)
                        return r;
                cond_resched();
        }
-       snd_printk(KERN_ERR "wait source ready timeout 0x%lx [0x%x]\n",
+       snd_printk(KERN_ERR "wait src ready timeout 0x%lx [0x%x]\n",
                   ES_REG(ensoniq, 1371_SMPRATE), r);
        return 0;
 }
@@ -1629,6 +1629,7 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
        memset(&ac97, 0, sizeof(ac97));
        ac97.private_data = ensoniq;
        ac97.private_free = snd_ensoniq_mixer_free_ac97;
+       ac97.pci = ensoniq->pci;
        ac97.scaps = AC97_SCAP_AUDIO;
        if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0)
                return err;
index 16715a68ba5e59c362b5ea3d3db252b4f022523d..ef9f072b47fcc65749729d22fa8e1390c1f8a8de 100644 (file)
@@ -1047,9 +1047,13 @@ static int azx_setup_periods(struct azx *chip,
        pos_adj = bdl_pos_adj[chip->dev_index];
        if (pos_adj > 0) {
                struct snd_pcm_runtime *runtime = substream->runtime;
+               int pos_align = pos_adj;
                pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
                if (!pos_adj)
-                       pos_adj = 1;
+                       pos_adj = pos_align;
+               else
+                       pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
+                               pos_align;
                pos_adj = frames_to_bytes(runtime, pos_adj);
                if (pos_adj >= period_bytes) {
                        snd_printk(KERN_WARNING "Too big adjustment %d\n",
index 2807bc840d26973f695d6be66c55b6bb5ac58717..add4e87e0b203a6ff8d9a52705158622550d6d79 100644 (file)
@@ -122,6 +122,8 @@ enum {
 /* ALC269 models */
 enum {
        ALC269_BASIC,
+       ALC269_ASUS_EEEPC_P703,
+       ALC269_ASUS_EEEPC_P901,
        ALC269_AUTO,
        ALC269_MODEL_LAST /* last tag */
 };
@@ -7905,6 +7907,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
        SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
        SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
+       SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), 
        SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
        SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
        SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
@@ -10946,7 +10949,23 @@ static int patch_alc268(struct hda_codec *codec)
 
 static hda_nid_t alc269_adc_nids[1] = {
        /* ADC1 */
-       0x07,
+       0x08,
+};
+
+static struct hda_input_mux alc269_eeepc_dmic_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "i-Mic", 0x5 },
+               { "e-Mic", 0x0 },
+       },
+};
+
+static struct hda_input_mux alc269_eeepc_amic_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "i-Mic", 0x1 },
+               { "e-Mic", 0x0 },
+       },
 };
 
 #define alc269_modes           alc260_modes
@@ -10968,10 +10987,27 @@ static struct snd_kcontrol_new alc269_base_mixer[] = {
        { } /* end */
 };
 
+/* bind volumes of both NID 0x0c and 0x0d */
+static struct hda_bind_ctls alc269_epc_bind_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
+               0
+       },
+};
+
+static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
+       HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_BIND_VOL("LineOut Playback Volume", &alc269_epc_bind_vol),
+       HDA_CODEC_MUTE("LineOut Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
 /* capture mixer elements */
 static struct snd_kcontrol_new alc269_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
@@ -10987,6 +11023,13 @@ static struct snd_kcontrol_new alc269_capture_mixer[] = {
        { } /* end */
 };
 
+/* capture mixer elements */
+static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -10994,7 +11037,7 @@ static struct hda_verb alc269_init_verbs[] = {
        /*
         * Unmute ADC0 and set the default input to mic-in
         */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
        /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
         * analog-loopback mixer widget
@@ -11057,6 +11100,98 @@ static struct hda_verb alc269_init_verbs[] = {
        { }
 };
 
+static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {}
+};
+
+static struct hda_verb alc269_eeepc_amic_init_verbs[] = {
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {}
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc269_speaker_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+       unsigned int bits;
+
+       present = snd_hda_codec_read(codec, 0x15, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       bits = present ? AMP_IN_MUTE(0) : 0;
+       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+                                AMP_IN_MUTE(0), bits);
+       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+                                AMP_IN_MUTE(0), bits);
+}
+
+static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0)
+               & AC_PINSENSE_PRESENCE;
+       snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
+                           present ? 0 : 5);
+}
+
+static void alc269_eeepc_amic_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0)
+               & AC_PINSENSE_PRESENCE;
+       snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           present ? AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0));
+       snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           present ? AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1));
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
+                                         unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc269_speaker_automute(codec);
+
+       if ((res >> 26) == ALC880_MIC_EVENT)
+               alc269_eeepc_dmic_automute(codec);
+}
+
+static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
+{
+       alc269_speaker_automute(codec);
+       alc269_eeepc_dmic_automute(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
+                                         unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc269_speaker_automute(codec);
+
+       if ((res >> 26) == ALC880_MIC_EVENT)
+               alc269_eeepc_amic_automute(codec);
+}
+
+static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
+{
+       alc269_speaker_automute(codec);
+       alc269_eeepc_amic_automute(codec);
+}
+
 /* add playback controls from the parsed DAC table */
 static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
@@ -11188,6 +11323,9 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       spec->mixers[spec->num_mixers] = alc269_capture_mixer;
+       spec->num_mixers++;
+
        return 1;
 }
 
@@ -11215,12 +11353,16 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
 };
 
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
+                     ALC269_ASUS_EEEPC_P703),
+       SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
+                     ALC269_ASUS_EEEPC_P901),
        {}
 };
 
 static struct alc_config_preset alc269_presets[] = {
        [ALC269_BASIC] = {
-               .mixers = { alc269_base_mixer },
+               .mixers = { alc269_base_mixer, alc269_capture_mixer },
                .init_verbs = { alc269_init_verbs },
                .num_dacs = ARRAY_SIZE(alc269_dac_nids),
                .dac_nids = alc269_dac_nids,
@@ -11229,6 +11371,32 @@ static struct alc_config_preset alc269_presets[] = {
                .channel_mode = alc269_modes,
                .input_mux = &alc269_capture_source,
        },
+       [ALC269_ASUS_EEEPC_P703] = {
+               .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
+               .init_verbs = { alc269_init_verbs,
+                               alc269_eeepc_amic_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+               .dac_nids = alc269_dac_nids,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc269_modes),
+               .channel_mode = alc269_modes,
+               .input_mux = &alc269_eeepc_amic_capture_source,
+               .unsol_event = alc269_eeepc_amic_unsol_event,
+               .init_hook = alc269_eeepc_amic_inithook,
+       },
+       [ALC269_ASUS_EEEPC_P901] = {
+               .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer},
+               .init_verbs = { alc269_init_verbs,
+                               alc269_eeepc_dmic_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+               .dac_nids = alc269_dac_nids,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc269_modes),
+               .channel_mode = alc269_modes,
+               .input_mux = &alc269_eeepc_dmic_capture_source,
+               .unsol_event = alc269_eeepc_dmic_unsol_event,
+               .init_hook = alc269_eeepc_dmic_inithook,
+       },
 };
 
 static int patch_alc269(struct hda_codec *codec)
@@ -11282,8 +11450,6 @@ static int patch_alc269(struct hda_codec *codec)
 
        spec->adc_nids = alc269_adc_nids;
        spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
-       spec->mixers[spec->num_mixers] = alc269_capture_mixer;
-       spec->num_mixers++;
 
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC269_AUTO)
@@ -12994,6 +13160,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
        SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
+       SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO),
        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
        SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
        SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
index 08cb77f51880a31265c46cf66691715cfbf5dd55..7fdafcb0015d8e9030c13c27b9d060c16ff9b39d 100644 (file)
@@ -94,6 +94,9 @@ enum {
        STAC_INTEL_MAC_V3,
        STAC_INTEL_MAC_V4,
        STAC_INTEL_MAC_V5,
+       STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter
+                             * is given, one of the above models will be
+                             * chosen according to the subsystem id. */
        /* for backward compatibility */
        STAC_MACMINI,
        STAC_MACBOOK,
@@ -1483,6 +1486,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
        [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
        [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
        [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
+       [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs,
        /* for backward compatibility */
        [STAC_MACMINI] = intel_mac_v3_pin_configs,
        [STAC_MACBOOK] = intel_mac_v5_pin_configs,
@@ -1505,6 +1509,7 @@ static const char *stac922x_models[STAC_922X_MODELS] = {
        [STAC_INTEL_MAC_V3] = "intel-mac-v3",
        [STAC_INTEL_MAC_V4] = "intel-mac-v4",
        [STAC_INTEL_MAC_V5] = "intel-mac-v5",
+       [STAC_INTEL_MAC_AUTO] = "intel-mac-auto",
        /* for backward compatibility */
        [STAC_MACMINI]  = "macmini",
        [STAC_MACBOOK]  = "macbook",
@@ -1576,9 +1581,9 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
                      "Intel D945P", STAC_D945GTP5),
        /* other systems  */
-       /* Apple Mac Mini (early 2006) */
+       /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
        SND_PCI_QUIRK(0x8384, 0x7680,
-                     "Mac Mini", STAC_INTEL_MAC_V3),
+                     "Mac", STAC_INTEL_MAC_AUTO),
        /* Dell systems  */
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
                      "unknown Dell", STAC_922X_DELL_D81),
@@ -3725,7 +3730,7 @@ static int patch_stac922x(struct hda_codec *codec)
        spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
                                                        stac922x_models,
                                                        stac922x_cfg_tbl);
-       if (spec->board_config == STAC_INTEL_MAC_V3) {
+       if (spec->board_config == STAC_INTEL_MAC_AUTO) {
                spec->gpio_mask = spec->gpio_dir = 0x03;
                spec->gpio_data = 0x03;
                /* Intel Macs have all same PCI SSID, so we need to check
@@ -3757,6 +3762,9 @@ static int patch_stac922x(struct hda_codec *codec)
                case 0x106b2200:
                        spec->board_config = STAC_INTEL_MAC_V5;
                        break;
+               default:
+                       spec->board_config = STAC_INTEL_MAC_V3;
+                       break;
                }
        }
 
index ba4b5c199f21b9fa97aa62cf0f6166309e00a901..9384702c7ebd7c09f0df6d4e25f9161ac854cc37 100644 (file)
@@ -231,7 +231,7 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
 
        /* if both TX and RX are idle, disable PSC */
        stat = au_readl(I2S_STAT(pscdata));
-       if (!(stat & (PSC_I2SSTAT_RB | PSC_I2SSTAT_RB))) {
+       if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) {
                au_writel(0, I2S_CFG(pscdata));
                au_sync();
                au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata));
index 9fc8edd82225cfe98dfbcb6aabadddf48eb1e9c5..1fb7f9a7aecd63dfa6ae6c71414e3702047a5c5a 100644 (file)
@@ -427,20 +427,20 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"HPOUTR", NULL, "Headphone PGA"},
        {"Headphone PGA", NULL, "Right HP Mixer"},
 
-       /* mono hp mixer */
-       {"Mono HP Mixer", NULL, "Left HP Mixer"},
-       {"Mono HP Mixer", NULL, "Right HP Mixer"},
+       /* mono mixer */
+       {"Mono Mixer", NULL, "Left HP Mixer"},
+       {"Mono Mixer", NULL, "Right HP Mixer"},
 
        /* Out3 Mux */
        {"Out3 Mux", "Left", "Left HP Mixer"},
        {"Out3 Mux", "Mono", "Phone Mixer"},
-       {"Out3 Mux", "Left + Right", "Mono HP Mixer"},
+       {"Out3 Mux", "Left + Right", "Mono Mixer"},
        {"Out 3 PGA", NULL, "Out3 Mux"},
        {"OUT3", NULL, "Out 3 PGA"},
 
        /* speaker Mux */
        {"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
-       {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"},
+       {"Speaker Mux", "Headphone Mix", "Mono Mixer"},
        {"Speaker PGA", NULL, "Speaker Mux"},
        {"LOUT2", NULL, "Speaker PGA"},
        {"ROUT2", NULL, "Speaker PGA"},
index 2c87061c2a6b4d7b9c35d7479d782462a0d8b3c1..820347c9ae4bbc5d5281e981e94f0cd27d2e28ae 100644 (file)
@@ -523,24 +523,6 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                                continue;
                        }
 
-                       /* programmable gain/attenuation */
-                       if (w->id == snd_soc_dapm_pga) {
-                               int on;
-                               in = is_connected_input_ep(w);
-                               dapm_clear_walk(w->codec);
-                               out = is_connected_output_ep(w);
-                               dapm_clear_walk(w->codec);
-                               w->power = on = (out != 0 && in != 0) ? 1 : 0;
-
-                               if (!on)
-                                       dapm_set_pga(w, on); /* lower volume to reduce pops */
-                               dapm_update_bits(w);
-                               if (on)
-                                       dapm_set_pga(w, on); /* restore volume from zero */
-
-                               continue;
-                       }
-
                        /* pre and post event widgets */
                        if (w->id == snd_soc_dapm_pre) {
                                if (!w->event)
@@ -586,45 +568,56 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                        power_change = (w->power == power) ? 0: 1;
                        w->power = power;
 
+                       if (!power_change)
+                               continue;
+
                        /* call any power change event handlers */
-                       if (power_change) {
-                               if (w->event) {
-                                       pr_debug("power %s event for %s flags %x\n",
-                                                w->power ? "on" : "off", w->name, w->event_flags);
-                                       if (power) {
-                                               /* power up event */
-                                               if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
-                                                       ret = w->event(w,
-                                                               NULL, SND_SOC_DAPM_PRE_PMU);
-                                                       if (ret < 0)
-                                                               return ret;
-                                               }
-                                               dapm_update_bits(w);
-                                               if (w->event_flags & SND_SOC_DAPM_POST_PMU){
-                                                       ret = w->event(w,
-                                                               NULL, SND_SOC_DAPM_POST_PMU);
-                                                       if (ret < 0)
-                                                               return ret;
-                                               }
-                                       } else {
-                                               /* power down event */
-                                               if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
-                                                       ret = w->event(w,
-                                                               NULL, SND_SOC_DAPM_PRE_PMD);
-                                                       if (ret < 0)
-                                                               return ret;
-                                               }
-                                               dapm_update_bits(w);
-                                               if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
-                                                       ret = w->event(w,
-                                                               NULL, SND_SOC_DAPM_POST_PMD);
-                                                       if (ret < 0)
-                                                               return ret;
-                                               }
-                                       }
-                               } else
-                                       /* no event handler */
-                                       dapm_update_bits(w);
+                       if (w->event)
+                               pr_debug("power %s event for %s flags %x\n",
+                                        w->power ? "on" : "off",
+                                        w->name, w->event_flags);
+
+                       /* power up pre event */
+                       if (power && w->event &&
+                           (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+                               ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
+                               if (ret < 0)
+                                       return ret;
+                       }
+
+                       /* power down pre event */
+                       if (!power && w->event &&
+                           (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+                               ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
+                               if (ret < 0)
+                                       return ret;
+                       }
+
+                       /* Lower PGA volume to reduce pops */
+                       if (w->id == snd_soc_dapm_pga && !power)
+                               dapm_set_pga(w, power);
+
+                       dapm_update_bits(w);
+
+                       /* Raise PGA volume to reduce pops */
+                       if (w->id == snd_soc_dapm_pga && power)
+                               dapm_set_pga(w, power);
+
+                       /* power up post event */
+                       if (power && w->event &&
+                           (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+                               ret = w->event(w,
+                                              NULL, SND_SOC_DAPM_POST_PMU);
+                               if (ret < 0)
+                                       return ret;
+                       }
+
+                       /* power down post event */
+                       if (!power && w->event &&
+                           (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+                               ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
+                               if (ret < 0)
+                                       return ret;
                        }
                }
        }